View Javadoc
1   package org.apache.maven.scm.provider.git.gitexe.command.checkout;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.scm.ScmBranch;
23  import org.apache.maven.scm.ScmException;
24  import org.apache.maven.scm.ScmFileSet;
25  import org.apache.maven.scm.ScmFileStatus;
26  import org.apache.maven.scm.ScmTag;
27  import org.apache.maven.scm.ScmVersion;
28  import org.apache.maven.scm.command.checkout.AbstractCheckOutCommand;
29  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
30  import org.apache.maven.scm.command.remoteinfo.RemoteInfoScmResult;
31  import org.apache.maven.scm.provider.ScmProviderRepository;
32  import org.apache.maven.scm.provider.git.command.GitCommand;
33  import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils;
34  import org.apache.maven.scm.provider.git.gitexe.command.list.GitListCommand;
35  import org.apache.maven.scm.provider.git.gitexe.command.list.GitListConsumer;
36  import org.apache.maven.scm.provider.git.gitexe.command.remoteinfo.GitRemoteInfoCommand;
37  import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
38  import org.codehaus.plexus.util.StringUtils;
39  import org.codehaus.plexus.util.cli.CommandLineUtils;
40  import org.codehaus.plexus.util.cli.Commandline;
41  
42  import java.io.File;
43  
44  /**
45   * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
46   *
47   */
48  public class GitCheckOutCommand
49      extends AbstractCheckOutCommand
50      implements GitCommand
51  {
52      /**
53       * For git, the given repository is a remote one.
54       * We have to clone it first if the working directory does not contain a git repo yet,
55       * otherwise we have to git-pull it.
56       * <p/>
57       * TODO We currently assume a '.git' directory, so this does not work for --bare repos
58       * {@inheritDoc}
59       */
60      protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repo, ScmFileSet fileSet,
61                                                          ScmVersion version, boolean recursive )
62          throws ScmException
63      {
64          GitScmProviderRepository repository = (GitScmProviderRepository) repo;
65  
66          if ( GitScmProviderRepository.PROTOCOL_FILE.equals( repository.getFetchInfo().getProtocol() )
67              && repository.getFetchInfo().getPath().indexOf( fileSet.getBasedir().getPath() ) >= 0 )
68          {
69              throw new ScmException( "remote repository must not be the working directory" );
70          }
71  
72          int exitCode;
73  
74          CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
75          CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
76  
77          String lastCommandLine = "git-nothing-to-do";
78  
79          if ( !fileSet.getBasedir().exists() || !( new File( fileSet.getBasedir(), ".git" ).exists() ) )
80          {
81              if ( fileSet.getBasedir().exists() )
82              {
83                  // git refuses to clone otherwise
84                  fileSet.getBasedir().delete();
85              }
86  
87              // no git repo seems to exist, let's clone the original repo
88              Commandline clClone = createCloneCommand( repository, fileSet.getBasedir(), version );
89  
90              exitCode = GitCommandLineUtils.execute( clClone, stdout, stderr, getLogger() );
91              if ( exitCode != 0 )
92              {
93                  return new CheckOutScmResult( clClone.toString(), "The git-clone command failed.", stderr.getOutput(),
94                                                false );
95              }
96              lastCommandLine = clClone.toString();
97          }
98  
99          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 }