View Javadoc
1   package org.apache.maven.scm.provider.git.gitexe.command.add;
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.commons.io.FilenameUtils;
23  import org.apache.maven.scm.ScmException;
24  import org.apache.maven.scm.ScmFile;
25  import org.apache.maven.scm.ScmFileSet;
26  import org.apache.maven.scm.ScmResult;
27  import org.apache.maven.scm.command.add.AbstractAddCommand;
28  import org.apache.maven.scm.command.add.AddScmResult;
29  import org.apache.maven.scm.provider.ScmProviderRepository;
30  import org.apache.maven.scm.provider.git.command.GitCommand;
31  import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils;
32  import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusCommand;
33  import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusConsumer;
34  import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
35  import org.codehaus.plexus.util.Os;
36  import org.codehaus.plexus.util.cli.CommandLineUtils;
37  import org.codehaus.plexus.util.cli.Commandline;
38  
39  import java.io.File;
40  import java.net.URI;
41  import java.util.ArrayList;
42  import java.util.Collections;
43  import java.util.List;
44  
45  /**
46   * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
47   */
48  public class GitAddCommand
49      extends AbstractAddCommand
50      implements GitCommand
51  {
52      /**
53       * {@inheritDoc}
54       */
55      protected ScmResult executeAddCommand( ScmProviderRepository repo, ScmFileSet fileSet, String message,
56                                             boolean binary )
57          throws ScmException
58      {
59          GitScmProviderRepository repository = (GitScmProviderRepository) repo;
60  
61          if ( fileSet.getFileList().isEmpty() )
62          {
63              throw new ScmException( "You must provide at least one file/directory to add" );
64          }
65  
66          AddScmResult result = executeAddFileSet( fileSet );
67  
68          if ( result != null )
69          {
70              return result;
71          }
72          
73          // SCM-709: statusCommand uses repositoryRoot instead of workingDirectory, adjust it with relativeRepositoryPath
74          Commandline clRevparse = GitStatusCommand.createRevparseShowToplevelCommand( fileSet );
75          
76          CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
77          CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
78  
79          URI relativeRepositoryPath = null;
80          
81          int exitCode;
82  
83          exitCode = GitCommandLineUtils.execute( clRevparse, stdout, stderr, getLogger() );
84          if ( exitCode != 0 )
85          {
86              // git-status returns non-zero if nothing to do
87              if ( getLogger().isInfoEnabled() )
88              {
89                  getLogger().info( "Could not resolve toplevel" );
90              }
91          }
92          else
93          {
94              relativeRepositoryPath =
95                  GitStatusConsumer.resolveURI( stdout.getOutput().trim(), fileSet.getBasedir().toURI() );
96          }
97  
98          // git-add doesn't show single files, but only summary :/
99          // so we must run git-status and consume the output
100         // borrow a few things from the git-status command
101         Commandline clStatus = GitStatusCommand.createCommandLine( repository, fileSet );
102 
103         GitStatusConsumer statusConsumer =
104             new GitStatusConsumer( getLogger(), fileSet.getBasedir(), relativeRepositoryPath );
105         stderr = new CommandLineUtils.StringStreamConsumer();
106         exitCode = GitCommandLineUtils.execute( clStatus, statusConsumer, stderr, getLogger() );
107         if ( exitCode != 0 )
108         {
109             // git-status returns non-zero if nothing to do
110             if ( getLogger().isInfoEnabled() )
111             {
112                 getLogger().info( "nothing added to commit but untracked files present (use \"git add\" to track)" );
113             }
114         }
115 
116         List<ScmFile> changedFiles = new ArrayList<ScmFile>();
117 
118         // rewrite all detected files to now have status 'checked_in'
119         for ( ScmFile scmfile : statusConsumer.getChangedFiles() )
120         {
121             // if a specific fileSet is given, we have to check if the file is really tracked
122             for ( File f : fileSet.getFileList() )
123             {
124                 if ( FilenameUtils.separatorsToUnix( f.getPath() ).equals( scmfile.getPath() ) )
125                 {
126                     changedFiles.add( scmfile );
127                 }
128             }
129         }
130 
131         Commandline cl = createCommandLine( fileSet.getBasedir(), fileSet.getFileList() );
132         return new AddScmResult( cl.toString(), changedFiles );
133     }
134 
135     public static Commandline createCommandLine( File workingDirectory, List<File> files )
136         throws ScmException
137     {
138         Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory, "add" );
139 
140         // use this separator to make clear that the following parameters are files and not revision info.
141         cl.createArg().setValue( "--" );
142 
143         GitCommandLineUtils.addTarget( cl, files );
144 
145         return cl;
146     }
147 
148     private AddScmResult executeAddFileSet( ScmFileSet fileSet )
149         throws ScmException
150     {
151         File workingDirectory = fileSet.getBasedir();
152         List<File> files = fileSet.getFileList();
153 
154         // command line can be too long for windows so add files individually (see SCM-697)
155         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
156         {
157             for ( File file : files )
158             {
159                 AddScmResult result = executeAddFiles( workingDirectory, Collections.singletonList( file ) );
160 
161                 if ( result != null )
162                 {
163                     return result;
164                 }
165             }
166         }
167         else
168         {
169             AddScmResult result = executeAddFiles( workingDirectory, files );
170 
171             if ( result != null )
172             {
173                 return result;
174             }
175         }
176 
177         return null;
178     }
179 
180     private AddScmResult executeAddFiles( File workingDirectory, List<File> files )
181         throws ScmException
182     {
183         Commandline cl = createCommandLine( workingDirectory, files );
184 
185         CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
186         CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
187 
188         int exitCode = GitCommandLineUtils.execute( cl, stdout, stderr, getLogger() );
189 
190         if ( exitCode != 0 )
191         {
192             return new AddScmResult( cl.toString(), "The git-add command failed.", stderr.getOutput(), false );
193         }
194 
195         return null;
196     }
197 }