001package org.apache.maven.scm.provider.git.gitexe.command.checkout; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.File; 023 024import org.apache.maven.scm.CommandParameter; 025import org.apache.maven.scm.CommandParameters; 026import org.apache.maven.scm.ScmBranch; 027import org.apache.maven.scm.ScmException; 028import org.apache.maven.scm.ScmFileSet; 029import org.apache.maven.scm.ScmFileStatus; 030import org.apache.maven.scm.ScmResult; 031import org.apache.maven.scm.ScmTag; 032import org.apache.maven.scm.ScmVersion; 033import org.apache.maven.scm.command.checkout.AbstractCheckOutCommand; 034import org.apache.maven.scm.command.checkout.CheckOutScmResult; 035import org.apache.maven.scm.command.remoteinfo.RemoteInfoScmResult; 036import org.apache.maven.scm.provider.ScmProviderRepository; 037import org.apache.maven.scm.provider.git.command.GitCommand; 038import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils; 039import org.apache.maven.scm.provider.git.gitexe.command.list.GitListCommand; 040import org.apache.maven.scm.provider.git.gitexe.command.list.GitListConsumer; 041import org.apache.maven.scm.provider.git.gitexe.command.remoteinfo.GitRemoteInfoCommand; 042import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository; 043import org.codehaus.plexus.util.StringUtils; 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 052 extends AbstractCheckOutCommand 053 implements GitCommand 054{ 055 /** 056 * For git, the given repository is a remote one. 057 * We have to clone it first if the working directory does not contain a git repo yet, 058 * otherwise we have to git-pull it. 059 * <p> 060 * TODO We currently assume a '.git' directory, so this does not work for --bare repos 061 * {@inheritDoc} 062 */ 063 @Override 064 public ScmResult executeCommand( ScmProviderRepository repo, ScmFileSet fileSet, 065 CommandParameters parameters ) 066 throws ScmException 067 { 068 ScmVersion version = parameters.getScmVersion( CommandParameter.SCM_VERSION, null ); 069 boolean binary = parameters.getBoolean( CommandParameter.BINARY, false ); 070 boolean shallow = parameters.getBoolean( CommandParameter.SHALLOW, false ); 071 072 GitScmProviderRepository repository = (GitScmProviderRepository) repo; 073 074 if ( GitScmProviderRepository.PROTOCOL_FILE.equals( repository.getFetchInfo().getProtocol() ) 075 && repository.getFetchInfo().getPath().indexOf( fileSet.getBasedir().getPath() ) >= 0 ) 076 { 077 throw new ScmException( "remote repository must not be the working directory" ); 078 } 079 080 int exitCode; 081 082 CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer(); 083 CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer(); 084 085 String lastCommandLine = "git-nothing-to-do"; 086 087 if ( !fileSet.getBasedir().exists() || !( new File( fileSet.getBasedir(), ".git" ).exists() ) ) 088 { 089 if ( fileSet.getBasedir().exists() ) 090 { 091 // git refuses to clone otherwise 092 fileSet.getBasedir().delete(); 093 } 094 095 // no git repo seems to exist, let's clone the original repo 096 Commandline clClone = createCloneCommand( repository, fileSet.getBasedir(), version, binary, shallow ); 097 098 exitCode = GitCommandLineUtils.execute( clClone, stdout, stderr, getLogger() ); 099 if ( exitCode != 0 ) 100 { 101 return new CheckOutScmResult( clClone.toString(), "The git-clone command failed.", stderr.getOutput(), 102 false ); 103 } 104 lastCommandLine = clClone.toString(); 105 } 106 107 GitRemoteInfoCommand gitRemoteInfoCommand = new GitRemoteInfoCommand(); 108 gitRemoteInfoCommand.setLogger( getLogger() ); 109 RemoteInfoScmResult result = gitRemoteInfoCommand.executeRemoteInfoCommand( repository, null, null ); 110 111 if ( fileSet.getBasedir().exists() && new File( fileSet.getBasedir(), ".git" ).exists() 112 && result.getBranches().size() > 0 ) 113 { 114 // git repo exists, so we must git-pull the changes 115 Commandline clPull = createPullCommand( repository, fileSet.getBasedir(), version ); 116 117 exitCode = GitCommandLineUtils.execute( clPull, stdout, stderr, getLogger() ); 118 if ( exitCode != 0 ) 119 { 120 return new CheckOutScmResult( clPull.toString(), "The git-pull command failed.", stderr.getOutput(), 121 false ); 122 } 123 lastCommandLine = clPull.toString(); 124 125 // and now lets do the git-checkout itself 126 Commandline clCheckout = createCommandLine( repository, fileSet.getBasedir(), version ); 127 128 exitCode = GitCommandLineUtils.execute( clCheckout, stdout, stderr, getLogger() ); 129 if ( exitCode != 0 ) 130 { 131 return new CheckOutScmResult( clCheckout.toString(), "The git-checkout command failed.", 132 stderr.getOutput(), false ); 133 } 134 lastCommandLine = clCheckout.toString(); 135 } 136 137 // and now search for the files 138 GitListConsumer listConsumer = 139 new GitListConsumer( getLogger(), fileSet.getBasedir(), ScmFileStatus.CHECKED_IN ); 140 141 Commandline clList = GitListCommand.createCommandLine( repository, fileSet.getBasedir() ); 142 143 exitCode = GitCommandLineUtils.execute( clList, listConsumer, stderr, getLogger() ); 144 if ( exitCode != 0 ) 145 { 146 return new CheckOutScmResult( clList.toString(), "The git-ls-files command failed.", stderr.getOutput(), 147 false ); 148 } 149 150 return new CheckOutScmResult( lastCommandLine, listConsumer.getListedFiles() ); 151 } 152 153 // ---------------------------------------------------------------------- 154 // 155 // ---------------------------------------------------------------------- 156 157 public static Commandline createCommandLine( GitScmProviderRepository repository, File workingDirectory, 158 ScmVersion version ) 159 { 160 Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory, "checkout" ); 161 162 if ( version != null && StringUtils.isNotEmpty( version.getName() ) ) 163 { 164 cl.createArg().setValue( version.getName() ); 165 } 166 167 return cl; 168 } 169 170 /** 171 * create a git-clone repository command 172 */ 173 private Commandline createCloneCommand( GitScmProviderRepository repository, File workingDirectory, 174 ScmVersion version, boolean binary, boolean shallow ) 175 { 176 Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory.getParentFile(), "clone" ); 177 178 forceBinary( cl, binary ); 179 180 if ( shallow ) 181 { 182 cl.createArg().setValue( "--depth" ); 183 184 cl.createArg().setValue( "1" ); 185 } 186 187 if ( version != null && ( version instanceof ScmBranch ) ) 188 { 189 190 cl.createArg().setValue( "--branch" ); 191 192 cl.createArg().setValue( version.getName() ); 193 } 194 195 cl.createArg().setValue( repository.getFetchUrl() ); 196 197 cl.createArg().setValue( workingDirectory.getName() ); 198 199 return cl; 200 } 201 202 private void forceBinary( Commandline cl, boolean binary ) 203 { 204 if ( binary ) 205 { 206 cl.createArg().setValue( "-c" ); 207 cl.createArg().setValue( "core.autocrlf=false" ); 208 } 209 } 210 211 /** 212 * create a git-pull repository command 213 */ 214 private Commandline createPullCommand( GitScmProviderRepository repository, File workingDirectory, 215 ScmVersion version ) 216 { 217 Commandline cl; 218 219 if ( version != null && StringUtils.isNotEmpty( version.getName() ) ) 220 { 221 if ( version instanceof ScmTag ) 222 { 223 // A tag will not be pulled but we only fetch all the commits from the upstream repo 224 // This is done because checking out a tag might not happen on the current branch 225 // but create a 'detached HEAD'. 226 // In fact, a tag in git may be in multiple branches. This occurs if 227 // you create a branch after the tag has been created 228 cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory, "fetch" ); 229 230 cl.createArg().setValue( repository.getFetchUrl() ); 231 } 232 else 233 { 234 cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory, "pull" ); 235 236 cl.createArg().setValue( repository.getFetchUrl() ); 237 238 cl.createArg().setValue( version.getName() + ":" + version.getName() ); 239 } 240 } 241 else 242 { 243 cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory, "pull" ); 244 245 cl.createArg().setValue( repository.getFetchUrl() ); 246 cl.createArg().setValue( "master" ); 247 } 248 return cl; 249 } 250 251 /** 252 * The overriden {@link #executeCommand(ScmProviderRepository, ScmFileSet, CommandParameters)} in this class will 253 * not call this method! 254 * <p> 255 * {@inheritDoc} 256 */ 257 protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repo, ScmFileSet fileSet, 258 ScmVersion version, boolean recursive, boolean shallow ) 259 throws ScmException 260 { 261 throw new UnsupportedOperationException( "Should not get here" ); 262 } 263}