View Javadoc
1   package org.apache.maven.scm.provider.git.gitexe.command.checkin;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.io.FilenameUtils;
23  import org.apache.maven.scm.ScmException;
24  import org.apache.maven.scm.ScmFile;
25  import org.apache.maven.scm.ScmFileSet;
26  import org.apache.maven.scm.ScmFileStatus;
27  import org.apache.maven.scm.ScmVersion;
28  import org.apache.maven.scm.command.checkin.AbstractCheckInCommand;
29  import org.apache.maven.scm.command.checkin.CheckInScmResult;
30  import org.apache.maven.scm.log.ScmLogger;
31  import org.apache.maven.scm.provider.ScmProviderRepository;
32  import org.apache.maven.scm.provider.git.command.GitCommand;
33  import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
34  import org.apache.maven.scm.provider.git.util.GitUtil;
35  import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils;
36  import org.apache.maven.scm.provider.git.gitexe.command.add.GitAddCommand;
37  import org.apache.maven.scm.provider.git.gitexe.command.branch.GitBranchCommand;
38  import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusCommand;
39  import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusConsumer;
40  import org.codehaus.plexus.util.FileUtils;
41  import org.codehaus.plexus.util.cli.CommandLineUtils;
42  import org.codehaus.plexus.util.cli.Commandline;
43  
44  import java.io.File;
45  import java.io.IOException;
46  import java.net.URI;
47  import java.util.ArrayList;
48  import java.util.List;
49  
50  /**
51   * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
52   * @author Olivier Lamy
53   *
54   */
55  public class GitCheckInCommand
56      extends AbstractCheckInCommand
57      implements GitCommand
58  {
59      /** {@inheritDoc} */
60      protected CheckInScmResult executeCheckInCommand( ScmProviderRepository repo, ScmFileSet fileSet, String message,
61                                                        ScmVersion version )
62          throws ScmException
63      {
64          GitScmProviderRepository repository = (GitScmProviderRepository) repo;
65  
66          CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
67          CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
68  
69          int exitCode;
70  
71          File messageFile = FileUtils.createTempFile( "maven-scm-", ".commit", null );
72          try
73          {
74              FileUtils.fileWrite( messageFile.getAbsolutePath(), message );
75          }
76          catch ( IOException ex )
77          {
78              return new CheckInScmResult( null, "Error while making a temporary file for the commit message: "
79                  + ex.getMessage(), null, false );
80          }
81  
82          try
83          {
84              if ( !fileSet.getFileList().isEmpty() )
85              {
86                  // if specific fileSet is given, we have to git-add them first
87                  // otherwise we will use 'git-commit -a' later
88  
89                  Commandline clAdd = GitAddCommand.createCommandLine( fileSet.getBasedir(), fileSet.getFileList() );
90  
91                  exitCode = GitCommandLineUtils.execute( clAdd, stdout, stderr, getLogger() );
92  
93                  if ( exitCode != 0 )
94                  {
95                      return new CheckInScmResult( clAdd.toString(), "The git-add command failed.", stderr.getOutput(),
96                                                   false );
97                  }
98  
99              }
100             
101             // SCM-709: statusCommand uses repositoryRoot instead of workingDirectory, adjust it with
102             // relativeRepositoryPath
103             Commandline clRevparse = GitStatusCommand.createRevparseShowToplevelCommand( fileSet );
104             
105             stdout = new CommandLineUtils.StringStreamConsumer();
106             stderr = new CommandLineUtils.StringStreamConsumer();
107 
108             URI relativeRepositoryPath = null;
109             
110             exitCode = GitCommandLineUtils.execute( clRevparse, stdout, stderr, getLogger() );
111             if ( exitCode != 0 )
112             {
113                 // git-status returns non-zero if nothing to do
114                 if ( getLogger().isInfoEnabled() )
115                 {
116                     getLogger().info( "Could not resolve toplevel" );
117                 }
118             }
119             else
120             {
121                 relativeRepositoryPath =
122                     GitStatusConsumer.resolveURI( stdout.getOutput().trim(), fileSet.getBasedir().toURI() );
123             }
124 
125             // git-commit doesn't show single files, but only summary :/
126             // so we must run git-status and consume the output
127             // borrow a few things from the git-status command
128             Commandline clStatus = GitStatusCommand.createCommandLine( repository, fileSet );
129 
130             GitStatusConsumer statusConsumer =
131                 new GitStatusConsumer( getLogger(), fileSet.getBasedir(), relativeRepositoryPath );
132             exitCode = GitCommandLineUtils.execute( clStatus, statusConsumer, stderr, getLogger() );
133             if ( exitCode != 0 )
134             {
135                 // git-status returns non-zero if nothing to do
136                 if ( getLogger().isInfoEnabled() )
137                 {
138                     getLogger().info( "nothing added to commit but untracked files present (use \"git add\" to "
139                                           + "track)" );
140                 }
141             }
142             
143             if ( statusConsumer.getChangedFiles().isEmpty() )
144             {
145                 return new CheckInScmResult( null, statusConsumer.getChangedFiles() );
146             }
147 
148             Commandline clCommit = createCommitCommandLine( repository, fileSet, messageFile );
149 
150             exitCode = GitCommandLineUtils.execute( clCommit, stdout, stderr, getLogger() );
151             if ( exitCode != 0 )
152             {
153                 return new CheckInScmResult( clCommit.toString(), "The git-commit command failed.", stderr.getOutput(),
154                                              false );
155             }
156 
157             if ( repo.isPushChanges() ) 
158             {
159                 Commandline cl = createPushCommandLine( getLogger(), repository, fileSet, version );
160 
161                 exitCode = GitCommandLineUtils.execute( cl, stdout, stderr, getLogger() );
162                 if ( exitCode != 0 )
163                 {
164                     return new CheckInScmResult( cl.toString(), "The git-push command failed.", stderr.getOutput(),
165                                                  false );
166                 }                
167             }
168 
169             List<ScmFile> checkedInFiles = new ArrayList<ScmFile>( statusConsumer.getChangedFiles().size() );
170 
171             // rewrite all detected files to now have status 'checked_in'
172             for ( ScmFile changedFile : statusConsumer.getChangedFiles() )
173             {
174                 ScmFile scmfile = new ScmFile( changedFile.getPath(), ScmFileStatus.CHECKED_IN );
175 
176                 if ( fileSet.getFileList().isEmpty() )
177                 {
178                     checkedInFiles.add( scmfile );
179                 }
180                 else
181                 {
182                     // if a specific fileSet is given, we have to check if the file is really tracked
183                     for ( File f : fileSet.getFileList() )
184                     {
185                         if ( FilenameUtils.separatorsToUnix( f.getPath() ).equals( scmfile.getPath() ) )
186                         {
187                             checkedInFiles.add( scmfile );
188                         }
189 
190                     }
191                 }
192             }
193 
194             return new CheckInScmResult( clCommit.toString(), checkedInFiles );
195         }
196         finally
197         {
198             try
199             {
200                 FileUtils.forceDelete( messageFile );
201             }
202             catch ( IOException ex )
203             {
204                 // ignore
205             }
206         }
207 
208     }
209 
210     // ----------------------------------------------------------------------
211     //
212     // ----------------------------------------------------------------------
213 
214     public static Commandline createPushCommandLine( ScmLogger logger, GitScmProviderRepository repository,
215                                                      ScmFileSet fileSet, ScmVersion version )
216         throws ScmException
217     {
218         Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "push" );
219 
220         String branch = GitBranchCommand.getCurrentBranch( logger, repository, fileSet );
221         
222         if ( branch == null || branch.length() == 0 )
223         {
224             throw new ScmException( "Could not detect the current branch. Don't know where I should push to!" );
225         }
226         
227         cl.createArg().setValue( repository.getPushUrl() );
228         
229         cl.createArg().setValue( "refs/heads/" + branch + ":" + "refs/heads/" + branch );
230 
231         return cl;
232     }
233 
234     public static Commandline createCommitCommandLine( GitScmProviderRepository repository, ScmFileSet fileSet,
235                                                        File messageFile )
236         throws ScmException
237     {
238         Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "commit" );
239 
240         cl.createArg().setValue( "--verbose" );
241 
242         cl.createArg().setValue( "-F" );
243 
244         cl.createArg().setValue( messageFile.getAbsolutePath() );
245 
246         if ( fileSet.getFileList().isEmpty() )
247         {
248             // commit all tracked files
249             cl.createArg().setValue( "-a" );
250         }
251         else
252         {
253             // specify exactly which files to commit
254             GitCommandLineUtils.addTarget( cl, fileSet.getFileList() );
255         }
256 
257         if ( GitUtil.getSettings().isCommitNoVerify() )
258         {
259             cl.createArg().setValue( "--no-verify" );
260         }
261 
262         return cl;
263     }
264 
265 }