001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.scm.provider.git.jgit.command.checkin; 020 021import java.io.File; 022import java.net.InetAddress; 023import java.net.UnknownHostException; 024import java.util.Collections; 025import java.util.List; 026import java.util.Set; 027 028import org.apache.commons.lang3.StringUtils; 029import org.apache.maven.scm.ScmException; 030import org.apache.maven.scm.ScmFile; 031import org.apache.maven.scm.ScmFileSet; 032import org.apache.maven.scm.ScmVersion; 033import org.apache.maven.scm.command.checkin.AbstractCheckInCommand; 034import org.apache.maven.scm.command.checkin.CheckInScmResult; 035import org.apache.maven.scm.provider.ScmProviderRepository; 036import org.apache.maven.scm.provider.git.command.GitCommand; 037import org.apache.maven.scm.provider.git.jgit.command.JGitUtils; 038import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository; 039import org.eclipse.jgit.api.AddCommand; 040import org.eclipse.jgit.api.CommitCommand; 041import org.eclipse.jgit.api.Git; 042import org.eclipse.jgit.api.Status; 043import org.eclipse.jgit.lib.Constants; 044import org.eclipse.jgit.lib.UserConfig; 045import org.eclipse.jgit.revwalk.RevCommit; 046import org.eclipse.jgit.transport.PushResult; 047import org.eclipse.jgit.transport.RefSpec; 048import org.eclipse.jgit.transport.RemoteRefUpdate; 049 050/** 051 * This provider uses the following strategy to discover the committer and author name/mail for a commit: 052 * <ol> 053 * <li>"user" section in .gitconfig</li> 054 * <li>"username" passed to maven execution</li> 055 * <li>default git config (system user and hostname for email)</li> 056 * </ol> 057 * the "maven-scm" config can be configured like this: <br> 058 * the default email domain to be used (will be used to create an email from the username passed to maven):<br> 059 * <code>git config --global maven-scm.maildomain mycomp.com</code> <br> 060 * you can also enforce the usage of the username for the author and committer:<br> 061 * <code>git config --global maven-scm.forceUsername true</code> <br> 062 * 063 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a> 064 * @author Dominik Bartholdi (imod) 065 * @since 1.9 066 */ 067public class JGitCheckInCommand extends AbstractCheckInCommand implements GitCommand { 068 069 protected static final String GIT_MAVEN_SECTION = "maven-scm"; 070 071 protected static final String GIT_MAILDOMAIN = "maildomain"; 072 073 protected static final String GIT_FORCE = "forceUsername"; 074 075 /** 076 * {@inheritDoc} 077 */ 078 protected CheckInScmResult executeCheckInCommand( 079 ScmProviderRepository repo, ScmFileSet fileSet, String message, ScmVersion version) throws ScmException { 080 081 Git git = null; 082 try { 083 File basedir = fileSet.getBasedir(); 084 git = JGitUtils.openRepo(basedir); 085 086 boolean doCommit = false; 087 088 if (!fileSet.getFileList().isEmpty()) { 089 // add files first 090 doCommit = JGitUtils.addAllFiles(git, fileSet).size() > 0; 091 if (!doCommit) { 092 doCommit = git.status().call().hasUncommittedChanges(); 093 } 094 } else { 095 // add all tracked files which are modified manually 096 Status status = git.status().call(); 097 Set<String> changeds = git.status().call().getModified(); 098 if (changeds.isEmpty()) { 099 if (!status.hasUncommittedChanges()) { 100 // warn there is nothing to add 101 logger.warn("There are neither files to be added nor any uncommitted changes"); 102 doCommit = false; 103 } else { 104 logger.debug("There are uncommitted changes in the git index"); 105 doCommit = true; 106 } 107 } else { 108 // TODO: gitexe only adds if fileSet is not empty 109 AddCommand add = git.add(); 110 for (String changed : changeds) { 111 logger.debug("Add manually: {}", changed); 112 add.addFilepattern(changed); 113 doCommit = true; 114 } 115 add.call(); 116 } 117 } 118 119 List<ScmFile> checkedInFiles = Collections.emptyList(); 120 if (doCommit) { 121 UserInfo author = getAuthor(repo, git); 122 UserInfo committer = getCommitter(repo, git); 123 124 CommitCommand command = git.commit().setMessage(message).setAuthor(author.name, author.email); 125 command.setCommitter(committer.name, committer.email); 126 RevCommit commitRev = command.call(); 127 128 logger.info("commit done: " + commitRev.getShortMessage()); 129 checkedInFiles = JGitUtils.getFilesInCommit(git.getRepository(), commitRev, fileSet.getBasedir()); 130 if (logger.isDebugEnabled()) { 131 for (ScmFile scmFile : checkedInFiles) { 132 logger.debug("in commit: " + scmFile); 133 } 134 } 135 } 136 137 if (repo.isPushChanges()) { 138 String branch = version != null ? version.getName() : null; 139 if (StringUtils.isBlank(branch)) { 140 branch = git.getRepository().getBranch(); 141 } 142 RefSpec refSpec = new RefSpec(Constants.R_HEADS + branch + ":" + Constants.R_HEADS + branch); 143 logger.info("push changes to remote... " + refSpec); 144 Iterable<PushResult> pushResultList = JGitUtils.push(git, (GitScmProviderRepository) repo, refSpec); 145 146 for (PushResult pushResult : pushResultList) { 147 for (RemoteRefUpdate remoteRefUpdate : pushResult.getRemoteUpdates()) { 148 if (!isSuccessStatus(remoteRefUpdate.getStatus())) { 149 return new CheckInScmResult( 150 "JGit checkin", 151 "The git-push command failed, with status: " + remoteRefUpdate.getStatus(), 152 remoteRefUpdate.getMessage(), 153 false); 154 } 155 } 156 } 157 } 158 159 return new CheckInScmResult("JGit checkin", checkedInFiles); 160 } catch (Exception e) { 161 throw new ScmException("JGit checkin failure!", e); 162 } finally { 163 JGitUtils.closeRepo(git); 164 } 165 } 166 167 private boolean isSuccessStatus(RemoteRefUpdate.Status remoteRefUpdateStatus) { 168 return remoteRefUpdateStatus == RemoteRefUpdate.Status.OK 169 || remoteRefUpdateStatus == RemoteRefUpdate.Status.UP_TO_DATE; 170 } 171 172 private static final class UserInfo { 173 174 final String name; 175 176 final String email; 177 178 UserInfo(String name, String email) { 179 this.name = name; 180 this.email = email; 181 } 182 } 183 184 private UserInfo getCommitter(ScmProviderRepository repo, Git git) { 185 boolean forceMvnUser = git.getRepository().getConfig().getBoolean(GIT_MAVEN_SECTION, GIT_FORCE, false); 186 187 // git config 188 UserConfig user = git.getRepository().getConfig().get(UserConfig.KEY); 189 String committerName = null; 190 if (!forceMvnUser && !user.isCommitterNameImplicit()) { 191 committerName = user.getCommitterName(); 192 } 193 194 // mvn parameter 195 if (StringUtils.isBlank(committerName)) { 196 committerName = repo.getUser(); 197 } 198 199 // git default 200 if (StringUtils.isBlank(committerName)) { 201 committerName = user.getCommitterName(); 202 } 203 204 // git config 205 String committerMail = null; 206 if (!user.isCommitterEmailImplicit()) { 207 committerMail = user.getCommitterEmail(); 208 } 209 210 if (StringUtils.isBlank(committerMail)) { 211 String defaultDomain = git.getRepository().getConfig().getString(GIT_MAVEN_SECTION, null, GIT_MAILDOMAIN); 212 defaultDomain = StringUtils.isNotBlank(defaultDomain) ? defaultDomain : getHostname(); 213 214 // mvn parameter (constructed with username) or git default 215 committerMail = StringUtils.isNotBlank(repo.getUser()) 216 ? repo.getUser() + "@" + defaultDomain 217 : user.getCommitterEmail(); 218 } 219 220 return new UserInfo(committerName, committerMail); 221 } 222 223 private UserInfo getAuthor(ScmProviderRepository repo, Git git) { 224 boolean forceMvnUser = git.getRepository().getConfig().getBoolean(GIT_MAVEN_SECTION, GIT_FORCE, false); 225 226 // git config 227 UserConfig user = git.getRepository().getConfig().get(UserConfig.KEY); 228 String authorName = null; 229 if (!forceMvnUser && !user.isAuthorNameImplicit()) { 230 authorName = user.getAuthorName(); 231 } 232 233 // mvn parameter 234 if (StringUtils.isBlank(authorName)) { 235 authorName = repo.getUser(); 236 } 237 238 // git default 239 if (StringUtils.isBlank(authorName)) { 240 authorName = user.getAuthorName(); 241 } 242 243 // git config 244 String authorMail = null; 245 if (!user.isAuthorEmailImplicit()) { 246 authorMail = user.getAuthorEmail(); 247 } 248 249 if (StringUtils.isBlank(authorMail)) { 250 String defaultDomain = git.getRepository().getConfig().getString(GIT_MAVEN_SECTION, null, GIT_MAILDOMAIN); 251 defaultDomain = StringUtils.isNotBlank(defaultDomain) ? defaultDomain : getHostname(); 252 253 // mvn parameter (constructed with username) or git default 254 authorMail = StringUtils.isNotBlank(repo.getUser()) 255 ? repo.getUser() + "@" + defaultDomain 256 : user.getAuthorEmail(); 257 } 258 259 return new UserInfo(authorName, authorMail); 260 } 261 262 private String getHostname() { 263 String hostname; 264 try { 265 InetAddress localhost = java.net.InetAddress.getLocalHost(); 266 hostname = localhost.getHostName(); 267 } catch (UnknownHostException e) { 268 logger.warn( 269 "failed to resolve hostname to create mail address, " + "defaulting to 'maven-scm-provider-jgit'"); 270 hostname = "maven-scm-provider-jgit"; 271 } 272 return hostname; 273 } 274}