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