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 = GitStatusConsumer.resolveURI( stdout.getOutput().trim() , fileSet.getBasedir().toURI() ); 
95          }
96  
97          // git-add doesn't show single files, but only summary :/
98          // so we must run git-status and consume the output
99          // borrow a few things from the git-status command
100         Commandline clStatus = GitStatusCommand.createCommandLine( repository, fileSet );
101 
102         GitStatusConsumer statusConsumer = new GitStatusConsumer( getLogger(), fileSet.getBasedir(), relativeRepositoryPath );
103         stderr = new CommandLineUtils.StringStreamConsumer();
104         exitCode = GitCommandLineUtils.execute( clStatus, statusConsumer, stderr, getLogger() );
105         if ( exitCode != 0 )
106         {
107             // git-status returns non-zero if nothing to do
108             if ( getLogger().isInfoEnabled() )
109             {
110                 getLogger().info( "nothing added to commit but untracked files present (use \"git add\" to track)" );
111             }
112         }
113 
114         List<ScmFile> changedFiles = new ArrayList<ScmFile>();
115 
116         // rewrite all detected files to now have status 'checked_in'
117         for ( ScmFile scmfile : statusConsumer.getChangedFiles() )
118         {
119             // if a specific fileSet is given, we have to check if the file is really tracked
120             for ( File f : fileSet.getFileList() )
121             {
122                 if ( FilenameUtils.separatorsToUnix( f.getPath() ).equals( scmfile.getPath() ) )
123                 {
124                     changedFiles.add( scmfile );
125                 }
126             }
127         }
128 
129         Commandline cl = createCommandLine( fileSet.getBasedir(), fileSet.getFileList() );
130         return new AddScmResult( cl.toString(), changedFiles );
131     }
132 
133     public static Commandline createCommandLine( File workingDirectory, List<File> files )
134         throws ScmException
135     {
136         Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory, "add" );
137 
138         // use this separator to make clear that the following parameters are files and not revision info.
139         cl.createArg().setValue( "--" );
140 
141         GitCommandLineUtils.addTarget( cl, files );
142 
143         return cl;
144     }
145 
146     private AddScmResult executeAddFileSet( ScmFileSet fileSet )
147         throws ScmException
148     {
149         File workingDirectory = fileSet.getBasedir();
150         List<File> files = fileSet.getFileList();
151 
152         // command line can be too long for windows so add files individually (see SCM-697)
153         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
154         {
155             for ( File file : files )
156             {
157                 AddScmResult result = executeAddFiles( workingDirectory, Collections.singletonList( file ) );
158 
159                 if ( result != null )
160                 {
161                     return result;
162                 }
163             }
164         }
165         else
166         {
167             AddScmResult result = executeAddFiles( workingDirectory, files );
168 
169             if ( result != null )
170             {
171                 return result;
172             }
173         }
174 
175         return null;
176     }
177 
178     private AddScmResult executeAddFiles( File workingDirectory, List<File> files )
179         throws ScmException
180     {
181         Commandline cl = createCommandLine( workingDirectory, files );
182 
183         CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
184         CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
185 
186         int exitCode = GitCommandLineUtils.execute( cl, stdout, stderr, getLogger() );
187 
188         if ( exitCode != 0 )
189         {
190             return new AddScmResult( cl.toString(), "The git-add command failed.", stderr.getOutput(), false );
191         }
192 
193         return null;
194     }
195 }