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.maven.scm.ScmException; 029import org.apache.maven.scm.ScmFile; 030import org.apache.maven.scm.ScmFileSet; 031import org.apache.maven.scm.ScmVersion; 032import org.apache.maven.scm.command.checkin.AbstractCheckInCommand; 033import org.apache.maven.scm.command.checkin.CheckInScmResult; 034import org.apache.maven.scm.provider.ScmProviderRepository; 035import org.apache.maven.scm.provider.git.command.GitCommand; 036import org.apache.maven.scm.provider.git.jgit.command.JGitUtils; 037import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository; 038import org.codehaus.plexus.util.StringUtils; 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.RefSpec; 047 048/** 049 * This provider uses the following strategy to discover the committer and author name/mail for a commit: 050 * <ol> 051 * <li>"user" section in .gitconfig</li> 052 * <li>"username" passed to maven execution</li> 053 * <li>default git config (system user and hostname for email)</li> 054 * </ol> 055 * the "maven-scm" config can be configured like this: <br> 056 * the default email domain to be used (will be used to create an email from the username passed to maven):<br> 057 * <code>git config --global maven-scm.maildomain mycomp.com</code> <br> 058 * you can also enforce the usage of the username for the author and committer:<br> 059 * <code>git config --global maven-scm.forceUsername true</code> <br> 060 * 061 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a> 062 * @author Dominik Bartholdi (imod) 063 * @since 1.9 064 */ 065public class JGitCheckInCommand extends AbstractCheckInCommand implements GitCommand { 066 067 protected static final String GIT_MAVEN_SECTION = "maven-scm"; 068 069 protected static final String GIT_MAILDOMAIN = "maildomain"; 070 071 protected static final String GIT_FORCE = "forceUsername"; 072 073 /** 074 * {@inheritDoc} 075 */ 076 protected CheckInScmResult executeCheckInCommand( 077 ScmProviderRepository repo, ScmFileSet fileSet, String message, ScmVersion version) throws ScmException { 078 079 Git git = null; 080 try { 081 File basedir = fileSet.getBasedir(); 082 git = JGitUtils.openRepo(basedir); 083 084 boolean doCommit = false; 085 086 if (!fileSet.getFileList().isEmpty()) { 087 // add files first 088 doCommit = JGitUtils.addAllFiles(git, fileSet).size() > 0; 089 if (!doCommit) { 090 doCommit = git.status().call().hasUncommittedChanges(); 091 } 092 } else { 093 // add all tracked files which are modified manually 094 Status status = git.status().call(); 095 Set<String> changeds = git.status().call().getModified(); 096 if (changeds.isEmpty()) { 097 if (!status.hasUncommittedChanges()) { 098 // warn there is nothing to add 099 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}