001    package org.apache.maven.scm.provider.git.gitexe.command.add;
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.commons.io.FilenameUtils;
023    import org.apache.maven.scm.ScmException;
024    import org.apache.maven.scm.ScmFile;
025    import org.apache.maven.scm.ScmFileSet;
026    import org.apache.maven.scm.ScmResult;
027    import org.apache.maven.scm.command.add.AbstractAddCommand;
028    import org.apache.maven.scm.command.add.AddScmResult;
029    import org.apache.maven.scm.provider.ScmProviderRepository;
030    import org.apache.maven.scm.provider.git.command.GitCommand;
031    import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils;
032    import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusCommand;
033    import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusConsumer;
034    import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
035    import org.codehaus.plexus.util.Os;
036    import org.codehaus.plexus.util.cli.CommandLineUtils;
037    import org.codehaus.plexus.util.cli.Commandline;
038    
039    import java.io.File;
040    import java.util.ArrayList;
041    import java.util.Collections;
042    import java.util.List;
043    
044    /**
045     * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
046     */
047    public class GitAddCommand
048        extends AbstractAddCommand
049        implements GitCommand
050    {
051        /**
052         * {@inheritDoc}
053         */
054        protected ScmResult executeAddCommand( ScmProviderRepository repo, ScmFileSet fileSet, String message,
055                                               boolean binary )
056            throws ScmException
057        {
058            GitScmProviderRepository repository = (GitScmProviderRepository) repo;
059    
060            if ( fileSet.getFileList().isEmpty() )
061            {
062                throw new ScmException( "You must provide at least one file/directory to add" );
063            }
064    
065            AddScmResult result = executeAddFileSet( fileSet );
066    
067            if ( result != null )
068            {
069                return result;
070            }
071    
072            // git-add doesn't show single files, but only summary :/
073            // so we must run git-status and consume the output
074            // borrow a few things from the git-status command
075            Commandline clStatus = GitStatusCommand.createCommandLine( repository, fileSet );
076    
077            GitStatusConsumer statusConsumer = new GitStatusConsumer( getLogger(), fileSet.getBasedir() );
078            CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
079            int exitCode = GitCommandLineUtils.execute( clStatus, statusConsumer, stderr, getLogger() );
080            if ( exitCode != 0 )
081            {
082                // git-status returns non-zero if nothing to do
083                if ( getLogger().isInfoEnabled() )
084                {
085                    getLogger().info( "nothing added to commit but untracked files present (use \"git add\" to track)" );
086                }
087            }
088    
089            List<ScmFile> changedFiles = new ArrayList<ScmFile>();
090    
091            // rewrite all detected files to now have status 'checked_in'
092            for ( ScmFile scmfile : statusConsumer.getChangedFiles() )
093            {
094                // if a specific fileSet is given, we have to check if the file is really tracked
095                for ( File f : fileSet.getFileList() )
096                {
097                    if ( FilenameUtils.separatorsToUnix( f.getPath() ).equals( scmfile.getPath() ) )
098                    {
099                        changedFiles.add( scmfile );
100                    }
101                }
102            }
103    
104            Commandline cl = createCommandLine( fileSet.getBasedir(), fileSet.getFileList() );
105            return new AddScmResult( cl.toString(), changedFiles );
106        }
107    
108        public static Commandline createCommandLine( File workingDirectory, List<File> files )
109            throws ScmException
110        {
111            Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( workingDirectory, "add" );
112    
113            // use this separator to make clear that the following parameters are files and not revision info.
114            cl.createArg().setValue( "--" );
115    
116            GitCommandLineUtils.addTarget( cl, files );
117    
118            return cl;
119        }
120    
121        private AddScmResult executeAddFileSet( ScmFileSet fileSet )
122            throws ScmException
123        {
124            File workingDirectory = fileSet.getBasedir();
125            List<File> files = fileSet.getFileList();
126    
127            // command line can be too long for windows so add files individually (see SCM-697)
128            if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
129            {
130                for ( File file : files )
131                {
132                    AddScmResult result = executeAddFiles( workingDirectory, Collections.singletonList( file ) );
133    
134                    if ( result != null )
135                    {
136                        return result;
137                    }
138                }
139            }
140            else
141            {
142                AddScmResult result = executeAddFiles( workingDirectory, files );
143    
144                if ( result != null )
145                {
146                    return result;
147                }
148            }
149    
150            return null;
151        }
152    
153        private AddScmResult executeAddFiles( File workingDirectory, List<File> files )
154            throws ScmException
155        {
156            Commandline cl = createCommandLine( workingDirectory, files );
157    
158            CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
159            CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
160    
161            int exitCode = GitCommandLineUtils.execute( cl, stdout, stderr, getLogger() );
162    
163            if ( exitCode != 0 )
164            {
165                return new AddScmResult( cl.toString(), "The git-add command failed.", stderr.getOutput(), false );
166            }
167    
168            return null;
169        }
170    }