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.gitexe.command.checkin; 020 021import java.io.File; 022import java.io.IOException; 023import java.net.URI; 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.commons.io.FilenameUtils; 030import org.apache.maven.scm.ScmException; 031import org.apache.maven.scm.ScmFile; 032import org.apache.maven.scm.ScmFileSet; 033import org.apache.maven.scm.ScmFileStatus; 034import org.apache.maven.scm.ScmVersion; 035import org.apache.maven.scm.command.checkin.AbstractCheckInCommand; 036import org.apache.maven.scm.command.checkin.CheckInScmResult; 037import org.apache.maven.scm.provider.ScmProviderRepository; 038import org.apache.maven.scm.provider.git.command.GitCommand; 039import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils; 040import org.apache.maven.scm.provider.git.gitexe.command.add.GitAddCommand; 041import org.apache.maven.scm.provider.git.gitexe.command.branch.GitBranchCommand; 042import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusCommand; 043import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusConsumer; 044import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository; 045import org.apache.maven.scm.provider.git.util.GitUtil; 046import org.codehaus.plexus.util.FileUtils; 047import org.codehaus.plexus.util.Os; 048import org.codehaus.plexus.util.cli.CommandLineUtils; 049import org.codehaus.plexus.util.cli.Commandline; 050 051/** 052 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a> 053 * @author Olivier Lamy 054 * 055 */ 056public class GitCheckInCommand extends AbstractCheckInCommand implements GitCommand { 057 private final Map<String, String> environmentVariables; 058 059 public GitCheckInCommand(Map<String, String> environmentVariables) { 060 super(); 061 this.environmentVariables = environmentVariables; 062 } 063 064 /** {@inheritDoc} */ 065 protected CheckInScmResult executeCheckInCommand( 066 ScmProviderRepository repo, ScmFileSet fileSet, String message, ScmVersion version) throws ScmException { 067 GitScmProviderRepository repository = (GitScmProviderRepository) repo; 068 069 CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer(); 070 CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer(); 071 072 int exitCode = -1; 073 074 File messageFile = FileUtils.createTempFile("maven-scm-", ".commit", null); 075 try { 076 FileUtils.fileWrite(messageFile.getAbsolutePath(), "UTF-8", message); 077 } catch (IOException ex) { 078 return new CheckInScmResult( 079 null, 080 "Error while making a temporary file for the commit message: " + ex.getMessage(), 081 null, 082 false); 083 } 084 085 try { 086 if (!fileSet.getFileList().isEmpty()) { 087 // if specific fileSet is given, we have to git-add them first 088 // otherwise we will use 'git-commit -a' later 089 090 Commandline clAdd = null; 091 092 // SCM-714: Workaround for the Windows terminal command limit 093 if (Os.isFamily(Os.FAMILY_WINDOWS)) { 094 for (File file : fileSet.getFileList()) { 095 clAdd = GitAddCommand.createCommandLine(fileSet.getBasedir(), Collections.singletonList(file)); 096 exitCode = GitCommandLineUtils.execute(clAdd, stdout, stderr); 097 098 if (exitCode != 0) { 099 break; 100 } 101 } 102 } else { 103 clAdd = GitAddCommand.createCommandLine(fileSet.getBasedir(), fileSet.getFileList()); 104 exitCode = GitCommandLineUtils.execute(clAdd, stdout, stderr); 105 } 106 107 if (exitCode != 0) { 108 return new CheckInScmResult( 109 clAdd.toString(), "The git-add command failed.", stderr.getOutput(), false); 110 } 111 } 112 113 // SCM-709: statusCommand uses repositoryRoot instead of workingDirectory, adjust it with 114 // relativeRepositoryPath 115 URI relativeRepositoryPath = GitStatusCommand.getRelativeCWD(logger, fileSet); 116 117 // git-commit doesn't show single files, but only summary :/ 118 // so we must run git-status and consume the output 119 // borrow a few things from the git-status command 120 Commandline clStatus = GitStatusCommand.createCommandLine(repository, fileSet); 121 122 GitStatusConsumer statusConsumer = 123 new GitStatusConsumer(fileSet.getBasedir(), relativeRepositoryPath, fileSet); 124 exitCode = GitCommandLineUtils.execute(clStatus, statusConsumer, stderr); 125 if (exitCode != 0) { 126 // git-status returns non-zero if nothing to do 127 if (logger.isInfoEnabled()) { 128 logger.info("nothing added to commit but untracked files present (use \"git add\" to " + "track)"); 129 } 130 } 131 132 if (statusConsumer.getChangedFiles().isEmpty()) { 133 return new CheckInScmResult(null, statusConsumer.getChangedFiles()); 134 } 135 136 Commandline clCommit = createCommitCommandLine(repository, fileSet, messageFile, environmentVariables); 137 138 exitCode = GitCommandLineUtils.execute(clCommit, stdout, stderr); 139 if (exitCode != 0) { 140 return new CheckInScmResult( 141 clCommit.toString(), "The git-commit command failed.", stderr.getOutput(), false); 142 } 143 144 if (repo.isPushChanges()) { 145 Commandline cl = createPushCommandLine(repository, fileSet, version); 146 147 exitCode = GitCommandLineUtils.execute(cl, stdout, stderr); 148 if (exitCode != 0) { 149 return new CheckInScmResult( 150 cl.toString(), "The git-push command failed.", stderr.getOutput(), false); 151 } 152 } 153 154 List<ScmFile> checkedInFiles = 155 new ArrayList<>(statusConsumer.getChangedFiles().size()); 156 157 // rewrite all detected files to now have status 'checked_in' 158 for (ScmFile changedFile : statusConsumer.getChangedFiles()) { 159 ScmFile scmfile = new ScmFile(changedFile.getPath(), ScmFileStatus.CHECKED_IN); 160 161 if (fileSet.getFileList().isEmpty()) { 162 checkedInFiles.add(scmfile); 163 } else { 164 // if a specific fileSet is given, we have to check if the file is really tracked 165 for (File f : fileSet.getFileList()) { 166 if (FilenameUtils.separatorsToUnix(f.getPath()).equals(scmfile.getPath())) { 167 checkedInFiles.add(scmfile); 168 } 169 } 170 } 171 } 172 173 return new CheckInScmResult(clCommit.toString(), checkedInFiles); 174 } finally { 175 try { 176 FileUtils.forceDelete(messageFile); 177 } catch (IOException ex) { 178 // ignore 179 } 180 } 181 } 182 183 // ---------------------------------------------------------------------- 184 // 185 // ---------------------------------------------------------------------- 186 187 public Commandline createPushCommandLine( 188 GitScmProviderRepository repository, ScmFileSet fileSet, ScmVersion version) throws ScmException { 189 Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( 190 fileSet.getBasedir(), "push", repository, environmentVariables); 191 192 String branch = GitBranchCommand.getCurrentBranch(repository, fileSet); 193 194 if (branch == null || branch.length() == 0) { 195 throw new ScmException("Could not detect the current branch. Don't know where I should push to!"); 196 } 197 198 cl.createArg().setValue(repository.getPushUrl()); 199 200 cl.createArg().setValue("refs/heads/" + branch + ":" + "refs/heads/" + branch); 201 202 return cl; 203 } 204 205 public static Commandline createCommitCommandLine( 206 GitScmProviderRepository repository, ScmFileSet fileSet, File messageFile) throws ScmException { 207 return createCommitCommandLine(repository, fileSet, messageFile, Collections.emptyMap()); 208 } 209 210 public static Commandline createCommitCommandLine( 211 GitScmProviderRepository repository, 212 ScmFileSet fileSet, 213 File messageFile, 214 Map<String, String> environmentVariables) 215 throws ScmException { 216 Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(fileSet.getBasedir(), "commit"); 217 218 cl.createArg().setValue("--verbose"); 219 220 cl.createArg().setValue("-F"); 221 222 cl.createArg().setValue(messageFile.getAbsolutePath()); 223 224 if (fileSet.getFileList().isEmpty()) { 225 // commit all tracked files 226 cl.createArg().setValue("-a"); 227 } 228 229 if (GitUtil.getSettings().isCommitNoVerify()) { 230 cl.createArg().setValue("--no-verify"); 231 } 232 233 return cl; 234 } 235}