View Javadoc
1   package org.apache.maven.scm.provider.git.jgit.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.maven.scm.ScmException;
23  import org.apache.maven.scm.ScmFile;
24  import org.apache.maven.scm.ScmFileSet;
25  import org.apache.maven.scm.ScmVersion;
26  import org.apache.maven.scm.command.checkin.AbstractCheckInCommand;
27  import org.apache.maven.scm.command.checkin.CheckInScmResult;
28  import org.apache.maven.scm.provider.ScmProviderRepository;
29  import org.apache.maven.scm.provider.git.command.GitCommand;
30  import org.apache.maven.scm.provider.git.jgit.command.JGitUtils;
31  import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
32  import org.codehaus.plexus.util.StringUtils;
33  import org.eclipse.jgit.api.AddCommand;
34  import org.eclipse.jgit.api.CommitCommand;
35  import org.eclipse.jgit.api.Git;
36  import org.eclipse.jgit.api.Status;
37  import org.eclipse.jgit.lib.Constants;
38  import org.eclipse.jgit.lib.UserConfig;
39  import org.eclipse.jgit.revwalk.RevCommit;
40  import org.eclipse.jgit.transport.RefSpec;
41  
42  import java.io.File;
43  import java.net.InetAddress;
44  import java.net.UnknownHostException;
45  import java.util.Collections;
46  import java.util.List;
47  import java.util.Set;
48  
49  /**
50   * This provider uses the following strategy to discover the committer and author name/mail for a commit:
51   * <ol>
52   * <li>"user" section in .gitconfig</li>
53   * <li>"username" passed to maven execution</li>
54   * <li>default git config (system user and hostname for email)</li>
55   * </ol>
56   * the "maven-scm" config can be configured like this: <br>
57   * the default email domain to be used (will be used to create an email from the username passed to maven):<br>
58   * <code>git config --global maven-scm.maildomain mycomp.com</code> <br>
59   * you can also enforce the usage of the username for the author and committer:<br>
60   * <code>git config --global maven-scm.forceUsername true</code> <br>
61   * 
62   * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
63   * @author Dominik Bartholdi (imod)
64   * @since 1.9
65   */
66  public class JGitCheckInCommand
67      extends AbstractCheckInCommand
68      implements GitCommand
69  {
70  
71      protected static final String GIT_MAVEN_SECTION = "maven-scm";
72  
73      protected static final String GIT_MAILDOMAIN = "maildomain";
74  
75      protected static final String GIT_FORCE = "forceUsername";
76  
77      /**
78       * {@inheritDoc}
79       */
80      protected CheckInScmResult executeCheckInCommand( ScmProviderRepository repo, ScmFileSet fileSet, String message,
81                                                        ScmVersion version )
82          throws ScmException
83      {
84  
85          Git git = null;
86          try
87          {
88              File basedir = fileSet.getBasedir();
89              git = JGitUtils.openRepo( basedir );
90  
91              boolean doCommit = false;
92  
93              if ( !fileSet.getFileList().isEmpty() )
94              {
95                  // add files first
96                  doCommit = JGitUtils.addAllFiles( git, fileSet ).size() > 0;
97                  if ( !doCommit )
98                  {
99                      doCommit = git.status().call().hasUncommittedChanges();
100                 }
101             }
102             else
103             {
104                 // add all tracked files which are modified manually
105                 Status status = git.status().call();
106                 Set<String> changeds = git.status().call().getModified();
107                 if ( changeds.isEmpty() )
108                 {
109                     if ( !status.hasUncommittedChanges() )
110                     {
111                         // warn there is nothing to add
112                         logger.warn( "There are neither files to be added nor any uncommitted changes" );
113                         doCommit = false;
114                     }
115                     else
116                     {
117                         logger.debug( "There are uncommitted changes in the git index" );
118                         doCommit = true;
119                     }
120                 }
121                 else
122                 {
123                     // TODO: gitexe only adds if fileSet is not empty
124                     AddCommand add = git.add();
125                     for ( String changed : changeds )
126                     {
127                         logger.debug( "Add manually: {}", changed );
128                         add.addFilepattern( changed );
129                         doCommit = true;
130                     }
131                     add.call();
132                 }
133             }
134 
135             List<ScmFile> checkedInFiles = Collections.emptyList();
136             if ( doCommit )
137             {
138                 UserInfo author = getAuthor( repo, git );
139                 UserInfo committer = getCommitter( repo, git );
140 
141                 CommitCommand command = git.commit().setMessage( message ).setAuthor( author.name, author.email );
142                 command.setCommitter( committer.name, committer.email );
143                 RevCommit commitRev = command.call();
144 
145                 logger.info( "commit done: " + commitRev.getShortMessage() );
146                 checkedInFiles = JGitUtils.getFilesInCommit( git.getRepository(), commitRev, fileSet.getBasedir() );
147                 if ( logger.isDebugEnabled() )
148                 {
149                     for ( ScmFile scmFile : checkedInFiles )
150                     {
151                         logger.debug( "in commit: " + scmFile );
152                     }
153                 }
154             }
155 
156             if ( repo.isPushChanges() )
157             {
158                 String branch = version != null ? version.getName() : null;
159                 if ( StringUtils.isBlank( branch ) )
160                 {
161                     branch = git.getRepository().getBranch();
162                 }
163                 RefSpec refSpec = new RefSpec( Constants.R_HEADS + branch + ":" + Constants.R_HEADS + branch );
164                 logger.info( "push changes to remote... " + refSpec );
165                 JGitUtils.push( git, (GitScmProviderRepository) repo, refSpec );
166             }
167 
168             return new CheckInScmResult( "JGit checkin", checkedInFiles );
169         }
170         catch ( Exception e )
171         {
172             throw new ScmException( "JGit checkin failure!", e );
173         }
174         finally
175         {
176             JGitUtils.closeRepo( git );
177         }
178     }
179 
180     private static final class UserInfo
181     {
182         final String name;
183 
184         final String email;
185 
186         UserInfo( String name, String email )
187         {
188             this.name = name;
189             this.email = email;
190         }
191     }
192 
193     private UserInfo getCommitter( ScmProviderRepository repo, Git git )
194     {
195         boolean forceMvnUser = git.getRepository().getConfig().getBoolean( GIT_MAVEN_SECTION, GIT_FORCE, false );
196 
197         // git config
198         UserConfig user = git.getRepository().getConfig().get( UserConfig.KEY );
199         String committerName = null;
200         if ( !forceMvnUser && !user.isCommitterNameImplicit() )
201         {
202             committerName = user.getCommitterName();
203         }
204 
205         // mvn parameter
206         if ( StringUtils.isBlank( committerName ) )
207         {
208             committerName = repo.getUser();
209         }
210 
211         // git default
212         if ( StringUtils.isBlank( committerName ) )
213         {
214             committerName = user.getCommitterName();
215         }
216 
217         // git config
218         String committerMail = null;
219         if ( !user.isCommitterEmailImplicit() )
220         {
221             committerMail = user.getCommitterEmail();
222         }
223 
224         if ( StringUtils.isBlank( committerMail ) )
225         {
226             String defaultDomain = git.getRepository().getConfig().getString( GIT_MAVEN_SECTION, null, GIT_MAILDOMAIN );
227             defaultDomain = StringUtils.isNotBlank( defaultDomain ) ? defaultDomain : getHostname();
228 
229             // mvn parameter (constructed with username) or git default
230             committerMail =
231                 StringUtils.isNotBlank( repo.getUser() ) ? repo.getUser() + "@" + defaultDomain
232                                 : user.getCommitterEmail();
233         }
234 
235         return new UserInfo( committerName, committerMail );
236     }
237 
238     private UserInfo getAuthor( ScmProviderRepository repo, Git git )
239     {
240         boolean forceMvnUser = git.getRepository().getConfig().getBoolean( GIT_MAVEN_SECTION, GIT_FORCE, false );
241 
242         // git config
243         UserConfig user = git.getRepository().getConfig().get( UserConfig.KEY );
244         String authorName = null;
245         if ( !forceMvnUser && !user.isAuthorNameImplicit() )
246         {
247             authorName = user.getAuthorName();
248         }
249 
250         // mvn parameter
251         if ( StringUtils.isBlank( authorName ) )
252         {
253             authorName = repo.getUser();
254         }
255 
256         // git default
257         if ( StringUtils.isBlank( authorName ) )
258         {
259             authorName = user.getAuthorName();
260         }
261 
262         // git config
263         String authorMail = null;
264         if ( !user.isAuthorEmailImplicit() )
265         {
266             authorMail = user.getAuthorEmail();
267         }
268 
269         if ( StringUtils.isBlank( authorMail ) )
270         {
271             String defaultDomain = git.getRepository().getConfig().getString( GIT_MAVEN_SECTION, null, GIT_MAILDOMAIN );
272             defaultDomain = StringUtils.isNotBlank( defaultDomain ) ? defaultDomain : getHostname();
273 
274             // mvn parameter (constructed with username) or git default
275             authorMail =
276                 StringUtils.isNotBlank( repo.getUser() ) ? repo.getUser() + "@" + defaultDomain : user.getAuthorEmail();
277         }
278 
279         return new UserInfo( authorName, authorMail );
280     }
281 
282     private String getHostname()
283     {
284         String hostname;
285         try
286         {
287             InetAddress localhost = java.net.InetAddress.getLocalHost();
288             hostname = localhost.getHostName();
289         }
290         catch ( UnknownHostException e )
291         {
292             logger.warn( "failed to resolve hostname to create mail address, "
293                                   + "defaulting to 'maven-scm-provider-jgit'" );
294             hostname = "maven-scm-provider-jgit";
295         }
296         return hostname;
297     }
298 
299 }