View Javadoc

1   /*
2    * Copyright (c) 2010 Kathryn Huxtable
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kathrynhuxtable.maven.wagon.gitsite.git;
17  
18  import java.io.File;
19  import java.io.IOException;
20  
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  
25  import org.apache.maven.scm.ScmException;
26  import org.apache.maven.scm.ScmFile;
27  import org.apache.maven.scm.ScmFileSet;
28  import org.apache.maven.scm.ScmFileStatus;
29  import org.apache.maven.scm.ScmVersion;
30  import org.apache.maven.scm.command.checkin.AbstractCheckInCommand;
31  import org.apache.maven.scm.command.checkin.CheckInScmResult;
32  import org.apache.maven.scm.provider.ScmProviderRepository;
33  import org.apache.maven.scm.provider.git.command.GitCommand;
34  import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils;
35  import org.apache.maven.scm.provider.git.gitexe.command.add.GitAddCommand;
36  import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusCommand;
37  import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusConsumer;
38  import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
39  import org.apache.maven.scm.provider.git.util.GitUtil;
40  
41  import org.codehaus.plexus.util.FileUtils;
42  import org.codehaus.plexus.util.cli.CommandLineUtils;
43  import org.codehaus.plexus.util.cli.Commandline;
44  
45  /**
46   * Handle git check-in and push to remote site.
47   *
48   * <p>Based on GitCheckInCommand by Mark Struberg.</p>
49   *
50   * @author Kathryn Huxtable
51   * @see    org.apache.maven.scm.provider.git.gitexe.command.checkin.GitCheckInCommand
52   */
53  public class GitSiteCheckInCommand extends AbstractCheckInCommand implements GitCommand {
54  
55      /**
56       * @see org.apache.maven.scm.command.checkin.AbstractCheckInCommand#executeCheckInCommand(org.apache.maven.scm.provider.ScmProviderRepository,
57       *      org.apache.maven.scm.ScmFileSet, java.lang.String,
58       *      org.apache.maven.scm.ScmVersion)
59       */
60      protected CheckInScmResult executeCheckInCommand(ScmProviderRepository repo, ScmFileSet fileSet, String message, ScmVersion version)
61          throws ScmException {
62          GitScmProviderRepository repository = (GitScmProviderRepository) repo;
63  
64          CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
65          CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
66  
67          int exitCode;
68  
69          File messageFile = FileUtils.createTempFile("maven-scm-", ".commit", null);
70  
71          try {
72              FileUtils.fileWrite(messageFile.getAbsolutePath(), message);
73          } catch (IOException ex) {
74              return new CheckInScmResult(null, "Error while making a temporary file for the commit message: " + ex.getMessage(), null,
75                                          false);
76          }
77  
78          try {
79              Commandline cl = null;
80  
81              if (!fileSet.getFileList().isEmpty()) {
82                  // If specific fileSet is given, we have to git-add them first,
83                  // otherwise we will use 'git-commit -a' later.
84                  cl       = GitAddCommand.createCommandLine(fileSet.getBasedir(), fileSet.getFileList());
85                  exitCode = GitCommandLineUtils.execute(cl, stdout, stderr, getLogger());
86                  if (exitCode != 0) {
87                      return new CheckInScmResult(cl.toString(), "The git-add command failed.", stderr.getOutput(), false);
88                  }
89              }
90  
91              // The git-commit command doesn't show single files, but only summary :/
92              // so we must run git-status and consume the output.
93              // Borrow a few things from the git-status command.
94              GitStatusConsumer statusConsumer = new GitStatusConsumer(getLogger(), fileSet.getBasedir());
95  
96              cl       = GitStatusCommand.createCommandLine(repository, fileSet);
97              exitCode = GitCommandLineUtils.execute(cl, statusConsumer, stderr, getLogger());
98              if (exitCode != 0) {
99                  // git-status returns non-zero if nothing to do.
100                 if (getLogger().isInfoEnabled()) {
101                     getLogger().info("nothing added to commit but untracked files present (use \"git add\" to "
102                                      + "track)");
103                 }
104             }
105 
106             cl       = createCommitCommandLine(fileSet, messageFile);
107             exitCode = GitCommandLineUtils.execute(cl, stdout, stderr, getLogger());
108             if (exitCode != 0) {
109                 return new CheckInScmResult(cl.toString(), "The git-commit command failed.", stderr.getOutput(), false);
110             }
111 
112             cl       = createPushCommandLine(fileSet, repository, version);
113             exitCode = GitCommandLineUtils.execute(cl, stdout, stderr, getLogger());
114             if (exitCode != 0) {
115                 return new CheckInScmResult(cl.toString(), "The git-push command failed.", stderr.getOutput(), false);
116             }
117 
118             List<ScmFile> checkedInFiles = new ArrayList<ScmFile>(statusConsumer.getChangedFiles().size());
119 
120             // rewrite all detected files to now have status 'checked_in'
121             for (Object foo : statusConsumer.getChangedFiles()) {
122                 ScmFile scmfile = new ScmFile(((ScmFile) foo).getPath(), ScmFileStatus.CHECKED_IN);
123 
124                 if (fileSet.getFileList().isEmpty()) {
125                     checkedInFiles.add(scmfile);
126                 } else {
127                     // If a specific fileSet is given, we have to check if the file is really tracked.
128                     for (Iterator<?> itfl = fileSet.getFileList().iterator(); itfl.hasNext();) {
129                         File f = (File) itfl.next();
130 
131                         if (f.toString().equals(scmfile.getPath())) {
132                             checkedInFiles.add(scmfile);
133                         }
134                     }
135                 }
136             }
137 
138             return new CheckInScmResult(cl.toString(), checkedInFiles);
139         } finally {
140             try {
141                 FileUtils.forceDelete(messageFile);
142             } catch (IOException ex) {
143                 // ignore
144             }
145         }
146     }
147 
148     /**
149      * Create the "git commit" command line.
150      *
151      * @param  fileSet     the file set to commit.
152      * @param  messageFile the file containing the commit message.
153      *
154      * @return the command line to commit the changes.
155      *
156      * @throws ScmException if an error occurs.
157      */
158     private Commandline createCommitCommandLine(ScmFileSet fileSet, File messageFile) throws ScmException {
159         Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(fileSet.getBasedir(), "commit");
160 
161         cl.createArg().setValue("--verbose");
162 
163         cl.createArg().setValue("--allow-empty");
164 
165         cl.createArg().setValue("-F");
166 
167         cl.createArg().setValue(messageFile.getAbsolutePath());
168 
169         if (fileSet.getFileList().isEmpty()) {
170             // commit all tracked files
171             cl.createArg().setValue("-a");
172         } else {
173             // specify exactly which files to commit
174             GitCommandLineUtils.addTarget(cl, fileSet.getFileList());
175         }
176 
177         if (GitUtil.getSettings().isCommitNoVerify()) {
178             cl.createArg().setValue("--no-verify");
179         }
180 
181         return cl;
182     }
183 
184     /**
185      * Create the "git push origin" command.
186      *
187      * @param  fileSet    the file set.
188      * @param  repository the SCM repository.
189      * @param  version    the site branch.
190      *
191      * @return the command line to push the changes to the site branch.
192      */
193     private Commandline createPushCommandLine(ScmFileSet fileSet, GitScmProviderRepository repository, ScmVersion version) {
194         Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(fileSet.getBasedir(), "push");
195 
196         cl.createArg().setValue("origin");
197 
198         cl.createArg().setValue("master:" + version.getName());
199 
200         return cl;
201     }
202 }