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.checkout; 020 021import java.io.File; 022import java.util.Map; 023 024import org.apache.commons.lang3.StringUtils; 025import org.apache.maven.scm.CommandParameter; 026import org.apache.maven.scm.CommandParameters; 027import org.apache.maven.scm.ScmBranch; 028import org.apache.maven.scm.ScmException; 029import org.apache.maven.scm.ScmFileSet; 030import org.apache.maven.scm.ScmFileStatus; 031import org.apache.maven.scm.ScmResult; 032import org.apache.maven.scm.ScmTag; 033import org.apache.maven.scm.ScmVersion; 034import org.apache.maven.scm.command.checkout.AbstractCheckOutCommand; 035import org.apache.maven.scm.command.checkout.CheckOutScmResult; 036import org.apache.maven.scm.command.remoteinfo.RemoteInfoScmResult; 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.list.GitListCommand; 041import org.apache.maven.scm.provider.git.gitexe.command.list.GitListConsumer; 042import org.apache.maven.scm.provider.git.gitexe.command.remoteinfo.GitRemoteInfoCommand; 043import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository; 044import org.codehaus.plexus.util.cli.CommandLineUtils; 045import org.codehaus.plexus.util.cli.Commandline; 046 047/** 048 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a> 049 * 050 */ 051public class GitCheckOutCommand extends AbstractCheckOutCommand implements GitCommand { 052 private final Map<String, String> environmentVariables; 053 054 public GitCheckOutCommand(Map<String, String> environmentVariables) { 055 super(); 056 this.environmentVariables = environmentVariables; 057 } 058 059 /** 060 * For git, the given repository is a remote one. 061 * We have to clone it first if the working directory does not contain a git repo yet, 062 * otherwise we have to git-pull it. 063 * <p> 064 * TODO We currently assume a '.git' directory, so this does not work for --bare repos 065 * {@inheritDoc} 066 */ 067 @Override 068 public ScmResult executeCommand(ScmProviderRepository repo, ScmFileSet fileSet, CommandParameters parameters) 069 throws ScmException { 070 ScmVersion version = parameters.getScmVersion(CommandParameter.SCM_VERSION, null); 071 boolean binary = parameters.getBoolean(CommandParameter.BINARY, false); 072 boolean shallow = parameters.getBoolean(CommandParameter.SHALLOW, false); 073 074 GitScmProviderRepository repository = (GitScmProviderRepository) repo; 075 076 if (GitScmProviderRepository.PROTOCOL_FILE.equals( 077 repository.getFetchInfo().getProtocol()) 078 && repository 079 .getFetchInfo() 080 .getPath() 081 .indexOf(fileSet.getBasedir().getPath()) 082 >= 0) { 083 throw new ScmException("remote repository must not be the working directory"); 084 } 085 086 int exitCode; 087 088 CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer(); 089 CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer(); 090 091 String lastCommandLine = "git-nothing-to-do"; 092 093 if (!fileSet.getBasedir().exists() || !(new File(fileSet.getBasedir(), ".git").exists())) { 094 if (fileSet.getBasedir().exists()) { 095 // git refuses to clone otherwise 096 fileSet.getBasedir().delete(); 097 } 098 099 // no git repo seems to exist, let's clone the original repo 100 Commandline clClone = createCloneCommand(repository, fileSet.getBasedir(), version, binary, shallow); 101 102 exitCode = GitCommandLineUtils.execute(clClone, stdout, stderr); 103 if (exitCode != 0) { 104 return new CheckOutScmResult( 105 clClone.toString(), "The git-clone command failed.", stderr.getOutput(), false); 106 } 107 lastCommandLine = clClone.toString(); 108 } 109 110 GitRemoteInfoCommand gitRemoteInfoCommand = new GitRemoteInfoCommand(environmentVariables); 111 112 RemoteInfoScmResult result = gitRemoteInfoCommand.executeRemoteInfoCommand(repository, null, null); 113 114 if (fileSet.getBasedir().exists() 115 && new File(fileSet.getBasedir(), ".git").exists() 116 && result.getBranches().size() > 0) { 117 // git repo exists, so we must git-pull the changes 118 Commandline clPull = createPullCommand(repository, fileSet.getBasedir(), version); 119 120 exitCode = GitCommandLineUtils.execute(clPull, stdout, stderr); 121 if (exitCode != 0) { 122 return new CheckOutScmResult( 123 clPull.toString(), "The git-pull command failed.", stderr.getOutput(), false); 124 } 125 lastCommandLine = clPull.toString(); 126 127 // and now lets do the git-checkout itself 128 Commandline clCheckout = createCommandLine(repository, fileSet.getBasedir(), version); 129 130 exitCode = GitCommandLineUtils.execute(clCheckout, stdout, stderr); 131 if (exitCode != 0) { 132 return new CheckOutScmResult( 133 clCheckout.toString(), "The git-checkout command failed.", stderr.getOutput(), false); 134 } 135 lastCommandLine = clCheckout.toString(); 136 } 137 138 // and now search for the files 139 GitListConsumer listConsumer = new GitListConsumer(fileSet.getBasedir(), ScmFileStatus.CHECKED_IN); 140 141 Commandline clList = GitListCommand.createCommandLine(repository, fileSet.getBasedir()); 142 143 exitCode = GitCommandLineUtils.execute(clList, listConsumer, stderr); 144 if (exitCode != 0) { 145 return new CheckOutScmResult( 146 clList.toString(), "The git-ls-files command failed.", stderr.getOutput(), false); 147 } 148 149 return new CheckOutScmResult(lastCommandLine, listConsumer.getListedFiles()); 150 } 151 152 // ---------------------------------------------------------------------- 153 // 154 // ---------------------------------------------------------------------- 155 156 public static Commandline createCommandLine( 157 GitScmProviderRepository repository, File workingDirectory, ScmVersion version) { 158 Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(workingDirectory, "checkout"); 159 160 if (version != null && StringUtils.isNotEmpty(version.getName())) { 161 cl.createArg().setValue(version.getName()); 162 } 163 164 return cl; 165 } 166 167 /** 168 * create a git-clone repository command 169 */ 170 private Commandline createCloneCommand( 171 GitScmProviderRepository repository, 172 File workingDirectory, 173 ScmVersion version, 174 boolean binary, 175 boolean shallow) { 176 Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( 177 workingDirectory.getParentFile(), "clone", repository, environmentVariables); 178 179 forceBinary(cl, binary); 180 181 if (shallow) { 182 cl.createArg().setValue("--depth"); 183 184 cl.createArg().setValue("1"); 185 } 186 187 if (version != null && (version instanceof ScmBranch)) { 188 189 cl.createArg().setValue("--branch"); 190 191 cl.createArg().setValue(version.getName()); 192 } 193 194 cl.createArg().setValue(repository.getFetchUrl()); 195 196 cl.createArg().setValue(workingDirectory.getName()); 197 198 return cl; 199 } 200 201 private void forceBinary(Commandline cl, boolean binary) { 202 if (binary) { 203 cl.createArg().setValue("-c"); 204 cl.createArg().setValue("core.autocrlf=false"); 205 } 206 } 207 208 /** 209 * create a git-pull repository command 210 */ 211 private Commandline createPullCommand( 212 GitScmProviderRepository repository, File workingDirectory, ScmVersion version) { 213 Commandline cl; 214 215 if (version != null && StringUtils.isNotEmpty(version.getName())) { 216 if (version instanceof ScmTag) { 217 // A tag will not be pulled but we only fetch all the commits from the upstream repo 218 // This is done because checking out a tag might not happen on the current branch 219 // but create a 'detached HEAD'. 220 // In fact, a tag in git may be in multiple branches. This occurs if 221 // you create a branch after the tag has been created 222 cl = GitCommandLineUtils.getBaseGitCommandLine( 223 workingDirectory, "fetch", repository, environmentVariables); 224 225 cl.createArg().setValue(repository.getFetchUrl()); 226 } else { 227 cl = GitCommandLineUtils.getBaseGitCommandLine( 228 workingDirectory, "pull", repository, environmentVariables); 229 230 cl.createArg().setValue(repository.getFetchUrl()); 231 232 cl.createArg().setValue(version.getName() + ":" + version.getName()); 233 } 234 } else { 235 cl = GitCommandLineUtils.getBaseGitCommandLine(workingDirectory, "pull", repository, environmentVariables); 236 237 cl.createArg().setValue(repository.getFetchUrl()); 238 cl.createArg().setValue("master"); 239 } 240 return cl; 241 } 242 243 /** 244 * The overriden {@link #executeCommand(ScmProviderRepository, ScmFileSet, CommandParameters)} in this class will 245 * not call this method! 246 * <p> 247 * {@inheritDoc} 248 */ 249 protected CheckOutScmResult executeCheckOutCommand( 250 ScmProviderRepository repo, ScmFileSet fileSet, ScmVersion version, boolean recursive, boolean shallow) 251 throws ScmException { 252 throw new UnsupportedOperationException("Should not get here"); 253 } 254}