View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.scm.provider.git.jgit.command.checkin;
20  
21  import java.io.File;
22  import java.net.InetAddress;
23  import java.net.UnknownHostException;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Set;
27  
28  import org.apache.commons.lang3.StringUtils;
29  import org.apache.maven.scm.ScmException;
30  import org.apache.maven.scm.ScmFile;
31  import org.apache.maven.scm.ScmFileSet;
32  import org.apache.maven.scm.ScmVersion;
33  import org.apache.maven.scm.command.checkin.AbstractCheckInCommand;
34  import org.apache.maven.scm.command.checkin.CheckInScmResult;
35  import org.apache.maven.scm.provider.ScmProviderRepository;
36  import org.apache.maven.scm.provider.git.command.GitCommand;
37  import org.apache.maven.scm.provider.git.jgit.command.JGitUtils;
38  import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
39  import org.eclipse.jgit.api.AddCommand;
40  import org.eclipse.jgit.api.CommitCommand;
41  import org.eclipse.jgit.api.Git;
42  import org.eclipse.jgit.api.Status;
43  import org.eclipse.jgit.lib.Constants;
44  import org.eclipse.jgit.lib.UserConfig;
45  import org.eclipse.jgit.revwalk.RevCommit;
46  import org.eclipse.jgit.transport.RefSpec;
47  
48  /**
49   * This provider uses the following strategy to discover the committer and author name/mail for a commit:
50   * <ol>
51   * <li>"user" section in .gitconfig</li>
52   * <li>"username" passed to maven execution</li>
53   * <li>default git config (system user and hostname for email)</li>
54   * </ol>
55   * the "maven-scm" config can be configured like this: <br>
56   * the default email domain to be used (will be used to create an email from the username passed to maven):<br>
57   * <code>git config --global maven-scm.maildomain mycomp.com</code> <br>
58   * you can also enforce the usage of the username for the author and committer:<br>
59   * <code>git config --global maven-scm.forceUsername true</code> <br>
60   *
61   * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
62   * @author Dominik Bartholdi (imod)
63   * @since 1.9
64   */
65  public class JGitCheckInCommand extends AbstractCheckInCommand implements GitCommand {
66  
67      protected static final String GIT_MAVEN_SECTION = "maven-scm";
68  
69      protected static final String GIT_MAILDOMAIN = "maildomain";
70  
71      protected static final String GIT_FORCE = "forceUsername";
72  
73      /**
74       * {@inheritDoc}
75       */
76      protected CheckInScmResult executeCheckInCommand(
77              ScmProviderRepository repo, ScmFileSet fileSet, String message, ScmVersion version) throws ScmException {
78  
79          Git git = null;
80          try {
81              File basedir = fileSet.getBasedir();
82              git = JGitUtils.openRepo(basedir);
83  
84              boolean doCommit = false;
85  
86              if (!fileSet.getFileList().isEmpty()) {
87                  // add files first
88                  doCommit = JGitUtils.addAllFiles(git, fileSet).size() > 0;
89                  if (!doCommit) {
90                      doCommit = git.status().call().hasUncommittedChanges();
91                  }
92              } else {
93                  // add all tracked files which are modified manually
94                  Status status = git.status().call();
95                  Set<String> changeds = git.status().call().getModified();
96                  if (changeds.isEmpty()) {
97                      if (!status.hasUncommittedChanges()) {
98                          // warn there is nothing to add
99                          logger.warn("There are neither files to be added nor any uncommitted changes");
100                         doCommit = false;
101                     } else {
102                         logger.debug("There are uncommitted changes in the git index");
103                         doCommit = true;
104                     }
105                 } else {
106                     // TODO: gitexe only adds if fileSet is not empty
107                     AddCommand add = git.add();
108                     for (String changed : changeds) {
109                         logger.debug("Add manually: {}", changed);
110                         add.addFilepattern(changed);
111                         doCommit = true;
112                     }
113                     add.call();
114                 }
115             }
116 
117             List<ScmFile> checkedInFiles = Collections.emptyList();
118             if (doCommit) {
119                 UserInfo author = getAuthor(repo, git);
120                 UserInfo committer = getCommitter(repo, git);
121 
122                 CommitCommand command = git.commit().setMessage(message).setAuthor(author.name, author.email);
123                 command.setCommitter(committer.name, committer.email);
124                 RevCommit commitRev = command.call();
125 
126                 logger.info("commit done: " + commitRev.getShortMessage());
127                 checkedInFiles = JGitUtils.getFilesInCommit(git.getRepository(), commitRev, fileSet.getBasedir());
128                 if (logger.isDebugEnabled()) {
129                     for (ScmFile scmFile : checkedInFiles) {
130                         logger.debug("in commit: " + scmFile);
131                     }
132                 }
133             }
134 
135             if (repo.isPushChanges()) {
136                 String branch = version != null ? version.getName() : null;
137                 if (StringUtils.isBlank(branch)) {
138                     branch = git.getRepository().getBranch();
139                 }
140                 RefSpec refSpec = new RefSpec(Constants.R_HEADS + branch + ":" + Constants.R_HEADS + branch);
141                 logger.info("push changes to remote... " + refSpec);
142                 JGitUtils.push(git, (GitScmProviderRepository) repo, refSpec);
143             }
144 
145             return new CheckInScmResult("JGit checkin", checkedInFiles);
146         } catch (Exception e) {
147             throw new ScmException("JGit checkin failure!", e);
148         } finally {
149             JGitUtils.closeRepo(git);
150         }
151     }
152 
153     private static final class UserInfo {
154         final String name;
155 
156         final String email;
157 
158         UserInfo(String name, String email) {
159             this.name = name;
160             this.email = email;
161         }
162     }
163 
164     private UserInfo getCommitter(ScmProviderRepository repo, Git git) {
165         boolean forceMvnUser = git.getRepository().getConfig().getBoolean(GIT_MAVEN_SECTION, GIT_FORCE, false);
166 
167         // git config
168         UserConfig user = git.getRepository().getConfig().get(UserConfig.KEY);
169         String committerName = null;
170         if (!forceMvnUser && !user.isCommitterNameImplicit()) {
171             committerName = user.getCommitterName();
172         }
173 
174         // mvn parameter
175         if (StringUtils.isBlank(committerName)) {
176             committerName = repo.getUser();
177         }
178 
179         // git default
180         if (StringUtils.isBlank(committerName)) {
181             committerName = user.getCommitterName();
182         }
183 
184         // git config
185         String committerMail = null;
186         if (!user.isCommitterEmailImplicit()) {
187             committerMail = user.getCommitterEmail();
188         }
189 
190         if (StringUtils.isBlank(committerMail)) {
191             String defaultDomain = git.getRepository().getConfig().getString(GIT_MAVEN_SECTION, null, GIT_MAILDOMAIN);
192             defaultDomain = StringUtils.isNotBlank(defaultDomain) ? defaultDomain : getHostname();
193 
194             // mvn parameter (constructed with username) or git default
195             committerMail = StringUtils.isNotBlank(repo.getUser())
196                     ? repo.getUser() + "@" + defaultDomain
197                     : user.getCommitterEmail();
198         }
199 
200         return new UserInfo(committerName, committerMail);
201     }
202 
203     private UserInfo getAuthor(ScmProviderRepository repo, Git git) {
204         boolean forceMvnUser = git.getRepository().getConfig().getBoolean(GIT_MAVEN_SECTION, GIT_FORCE, false);
205 
206         // git config
207         UserConfig user = git.getRepository().getConfig().get(UserConfig.KEY);
208         String authorName = null;
209         if (!forceMvnUser && !user.isAuthorNameImplicit()) {
210             authorName = user.getAuthorName();
211         }
212 
213         // mvn parameter
214         if (StringUtils.isBlank(authorName)) {
215             authorName = repo.getUser();
216         }
217 
218         // git default
219         if (StringUtils.isBlank(authorName)) {
220             authorName = user.getAuthorName();
221         }
222 
223         // git config
224         String authorMail = null;
225         if (!user.isAuthorEmailImplicit()) {
226             authorMail = user.getAuthorEmail();
227         }
228 
229         if (StringUtils.isBlank(authorMail)) {
230             String defaultDomain = git.getRepository().getConfig().getString(GIT_MAVEN_SECTION, null, GIT_MAILDOMAIN);
231             defaultDomain = StringUtils.isNotBlank(defaultDomain) ? defaultDomain : getHostname();
232 
233             // mvn parameter (constructed with username) or git default
234             authorMail = StringUtils.isNotBlank(repo.getUser())
235                     ? repo.getUser() + "@" + defaultDomain
236                     : user.getAuthorEmail();
237         }
238 
239         return new UserInfo(authorName, authorMail);
240     }
241 
242     private String getHostname() {
243         String hostname;
244         try {
245             InetAddress localhost = java.net.InetAddress.getLocalHost();
246             hostname = localhost.getHostName();
247         } catch (UnknownHostException e) {
248             logger.warn(
249                     "failed to resolve hostname to create mail address, " + "defaulting to 'maven-scm-provider-jgit'");
250             hostname = "maven-scm-provider-jgit";
251         }
252         return hostname;
253     }
254 }