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 relativeRepositoryPath
102             Commandline clRevparse = GitStatusCommand.createRevparseShowToplevelCommand( fileSet );
103             
104             stdout = new CommandLineUtils.StringStreamConsumer();
105             stderr = new CommandLineUtils.StringStreamConsumer();
106 
107             URI relativeRepositoryPath = null;
108             
109             exitCode = GitCommandLineUtils.execute( clRevparse, stdout, stderr, getLogger() );
110             if ( exitCode != 0 )
111             {
112                 // git-status returns non-zero if nothing to do
113                 if ( getLogger().isInfoEnabled() )
114                 {
115                     getLogger().info( "Could not resolve toplevel" );
116                 }
117             }
118             else
119             {
120                 relativeRepositoryPath = GitStatusConsumer.resolveURI( stdout.getOutput().trim(), fileSet.getBasedir().toURI() ); 
121             }
122 
123             // git-commit doesn't show single files, but only summary :/
124             // so we must run git-status and consume the output
125             // borrow a few things from the git-status command
126             Commandline clStatus = GitStatusCommand.createCommandLine( repository, fileSet );
127 
128             GitStatusConsumer statusConsumer = new GitStatusConsumer( getLogger(), fileSet.getBasedir(), relativeRepositoryPath );
129             exitCode = GitCommandLineUtils.execute( clStatus, statusConsumer, stderr, getLogger() );
130             if ( exitCode != 0 )
131             {
132                 // git-status returns non-zero if nothing to do
133                 if ( getLogger().isInfoEnabled() )
134                 {
135                     getLogger().info( "nothing added to commit but untracked files present (use \"git add\" to " +
136                             "track)" );
137                 }
138             }
139             
140             if ( statusConsumer.getChangedFiles().isEmpty() )
141             {
142                 return new CheckInScmResult( null, statusConsumer.getChangedFiles() );
143             }
144 
145             Commandline clCommit = createCommitCommandLine( repository, fileSet, messageFile );
146 
147             exitCode = GitCommandLineUtils.execute( clCommit, stdout, stderr, getLogger() );
148             if ( exitCode != 0 )
149             {
150                 return new CheckInScmResult( clCommit.toString(), "The git-commit command failed.", stderr.getOutput(),
151                                              false );
152             }
153 
154             if( repo.isPushChanges() ) 
155             {
156                 Commandline cl = createPushCommandLine( getLogger(), repository, fileSet, version );
157 
158                 exitCode = GitCommandLineUtils.execute( cl, stdout, stderr, getLogger() );
159                 if ( exitCode != 0 )
160                 {
161                     return new CheckInScmResult( cl.toString(), "The git-push command failed.", stderr.getOutput(), false );
162                 }                
163             }
164 
165             List<ScmFile> checkedInFiles = new ArrayList<ScmFile>( statusConsumer.getChangedFiles().size() );
166 
167             // rewrite all detected files to now have status 'checked_in'
168             for ( ScmFile changedFile : statusConsumer.getChangedFiles() )
169             {
170                 ScmFile scmfile = new ScmFile( changedFile.getPath(), ScmFileStatus.CHECKED_IN );
171 
172                 if ( fileSet.getFileList().isEmpty() )
173                 {
174                     checkedInFiles.add( scmfile );
175                 }
176                 else
177                 {
178                     // if a specific fileSet is given, we have to check if the file is really tracked
179                     for ( File f : fileSet.getFileList() )
180                     {
181                         if ( FilenameUtils.separatorsToUnix( f.getPath() ).equals( scmfile.getPath() ) )
182                         {
183                             checkedInFiles.add( scmfile );
184                         }
185 
186                     }
187                 }
188             }
189 
190             return new CheckInScmResult( clCommit.toString(), checkedInFiles );
191         }
192         finally
193         {
194             try
195             {
196                 FileUtils.forceDelete( messageFile );
197             }
198             catch ( IOException ex )
199             {
200                 // ignore
201             }
202         }
203 
204     }
205 
206     // ----------------------------------------------------------------------
207     //
208     // ----------------------------------------------------------------------
209 
210     public static Commandline createPushCommandLine( ScmLogger logger, GitScmProviderRepository repository,
211                                                      ScmFileSet fileSet, ScmVersion version )
212         throws ScmException
213     {
214         Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "push" );
215 
216         String branch = GitBranchCommand.getCurrentBranch( logger, repository, fileSet );
217         
218         if ( branch == null || branch.length() == 0 )
219         {
220             throw new ScmException( "Could not detect the current branch. Don't know where I should push to!" );
221         }
222         
223         cl.createArg().setValue( repository.getPushUrl() );
224         
225         cl.createArg().setValue( "refs/heads/" + branch + ":" + "refs/heads/" + branch );
226 
227         return cl;
228     }
229 
230     public static Commandline createCommitCommandLine( GitScmProviderRepository repository, ScmFileSet fileSet,
231                                                        File messageFile )
232         throws ScmException
233     {
234         Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "commit" );
235 
236         cl.createArg().setValue( "--verbose" );
237 
238         cl.createArg().setValue( "-F" );
239 
240         cl.createArg().setValue( messageFile.getAbsolutePath() );
241 
242         if ( fileSet.getFileList().isEmpty() )
243         {
244             // commit all tracked files
245             cl.createArg().setValue( "-a" );
246         }
247         else
248         {
249             // specify exactly which files to commit
250             GitCommandLineUtils.addTarget( cl, fileSet.getFileList() );
251         }
252 
253         if ( GitUtil.getSettings().isCommitNoVerify() )
254         {
255             cl.createArg().setValue( "--no-verify" );
256         }
257 
258         return cl;
259     }
260 
261 }