001    package org.apache.maven.scm.provider.git.gitexe.command.checkin;
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.ScmFileStatus;
027    import org.apache.maven.scm.ScmVersion;
028    import org.apache.maven.scm.command.checkin.AbstractCheckInCommand;
029    import org.apache.maven.scm.command.checkin.CheckInScmResult;
030    import org.apache.maven.scm.log.ScmLogger;
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.repository.GitScmProviderRepository;
034    import org.apache.maven.scm.provider.git.util.GitUtil;
035    import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils;
036    import org.apache.maven.scm.provider.git.gitexe.command.add.GitAddCommand;
037    import org.apache.maven.scm.provider.git.gitexe.command.branch.GitBranchCommand;
038    import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusCommand;
039    import org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusConsumer;
040    import org.codehaus.plexus.util.FileUtils;
041    import org.codehaus.plexus.util.cli.CommandLineUtils;
042    import org.codehaus.plexus.util.cli.Commandline;
043    
044    import java.io.File;
045    import java.io.IOException;
046    import java.util.ArrayList;
047    import java.util.List;
048    
049    /**
050     * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
051     * @author Olivier Lamy
052     *
053     */
054    public class GitCheckInCommand
055        extends AbstractCheckInCommand
056        implements GitCommand
057    {
058        /** {@inheritDoc} */
059        protected CheckInScmResult executeCheckInCommand( ScmProviderRepository repo, ScmFileSet fileSet, String message,
060                                                          ScmVersion version )
061            throws ScmException
062        {
063            GitScmProviderRepository repository = (GitScmProviderRepository) repo;
064    
065            CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
066            CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
067    
068            int exitCode;
069    
070            File messageFile = FileUtils.createTempFile( "maven-scm-", ".commit", null );
071            try
072            {
073                FileUtils.fileWrite( messageFile.getAbsolutePath(), message );
074            }
075            catch ( IOException ex )
076            {
077                return new CheckInScmResult( null, "Error while making a temporary file for the commit message: "
078                    + ex.getMessage(), null, false );
079            }
080    
081            try
082            {
083                if ( !fileSet.getFileList().isEmpty() )
084                {
085                    // if specific fileSet is given, we have to git-add them first
086                    // otherwise we will use 'git-commit -a' later
087    
088                    Commandline clAdd = GitAddCommand.createCommandLine( fileSet.getBasedir(), fileSet.getFileList() );
089    
090                    exitCode = GitCommandLineUtils.execute( clAdd, stdout, stderr, getLogger() );
091    
092                    if ( exitCode != 0 )
093                    {
094                        return new CheckInScmResult( clAdd.toString(), "The git-add command failed.", stderr.getOutput(),
095                                                     false );
096                    }
097    
098                }
099    
100                // git-commit doesn't show single files, but only summary :/
101                // so we must run git-status and consume the output
102                // borrow a few things from the git-status command
103                Commandline clStatus = GitStatusCommand.createCommandLine( repository, fileSet );
104    
105                GitStatusConsumer statusConsumer = new GitStatusConsumer( getLogger(), fileSet.getBasedir() );
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 " +
113                                "track)" );
114                    }
115                }
116                
117                if ( statusConsumer.getChangedFiles().isEmpty() )
118                {
119                    return new CheckInScmResult( null, statusConsumer.getChangedFiles() );
120                }
121    
122                Commandline clCommit = createCommitCommandLine( repository, fileSet, messageFile );
123    
124                exitCode = GitCommandLineUtils.execute( clCommit, stdout, stderr, getLogger() );
125                if ( exitCode != 0 )
126                {
127                    return new CheckInScmResult( clCommit.toString(), "The git-commit command failed.", stderr.getOutput(),
128                                                 false );
129                }
130    
131                if( repo.isPushChanges() ) 
132                {
133                    Commandline cl = createPushCommandLine( getLogger(), repository, fileSet, version );
134    
135                    exitCode = GitCommandLineUtils.execute( cl, stdout, stderr, getLogger() );
136                    if ( exitCode != 0 )
137                    {
138                        return new CheckInScmResult( cl.toString(), "The git-push command failed.", stderr.getOutput(), false );
139                    }                
140                }
141    
142                List<ScmFile> checkedInFiles = new ArrayList<ScmFile>( statusConsumer.getChangedFiles().size() );
143    
144                // rewrite all detected files to now have status 'checked_in'
145                for ( ScmFile changedFile : statusConsumer.getChangedFiles() )
146                {
147                    ScmFile scmfile = new ScmFile( changedFile.getPath(), ScmFileStatus.CHECKED_IN );
148    
149                    if ( fileSet.getFileList().isEmpty() )
150                    {
151                        checkedInFiles.add( scmfile );
152                    }
153                    else
154                    {
155                        // if a specific fileSet is given, we have to check if the file is really tracked
156                        for ( File f : fileSet.getFileList() )
157                        {
158                            if ( FilenameUtils.separatorsToUnix( f.getPath() ).equals( scmfile.getPath() ) )
159                            {
160                                checkedInFiles.add( scmfile );
161                            }
162    
163                        }
164                    }
165                }
166    
167                return new CheckInScmResult( clCommit.toString(), checkedInFiles );
168            }
169            finally
170            {
171                try
172                {
173                    FileUtils.forceDelete( messageFile );
174                }
175                catch ( IOException ex )
176                {
177                    // ignore
178                }
179            }
180    
181        }
182    
183        // ----------------------------------------------------------------------
184        //
185        // ----------------------------------------------------------------------
186    
187        public static Commandline createPushCommandLine( ScmLogger logger, GitScmProviderRepository repository,
188                                                         ScmFileSet fileSet, ScmVersion version )
189            throws ScmException
190        {
191            Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "push" );
192    
193            String branch = GitBranchCommand.getCurrentBranch( logger, repository, fileSet );
194            
195            if ( branch == null || branch.length() == 0 )
196            {
197                throw new ScmException( "Could not detect the current branch. Don't know where I should push to!" );
198            }
199            
200            cl.createArg().setValue( repository.getPushUrl() );
201            
202            cl.createArg().setValue( branch + ":" + branch );
203    
204            return cl;
205        }
206    
207        public static Commandline createCommitCommandLine( GitScmProviderRepository repository, ScmFileSet fileSet,
208                                                           File messageFile )
209            throws ScmException
210        {
211            Commandline cl = GitCommandLineUtils.getBaseGitCommandLine( fileSet.getBasedir(), "commit" );
212    
213            cl.createArg().setValue( "--verbose" );
214    
215            cl.createArg().setValue( "-F" );
216    
217            cl.createArg().setValue( messageFile.getAbsolutePath() );
218    
219            if ( fileSet.getFileList().isEmpty() )
220            {
221                // commit all tracked files
222                cl.createArg().setValue( "-a" );
223            }
224            else
225            {
226                // specify exactly which files to commit
227                GitCommandLineUtils.addTarget( cl, fileSet.getFileList() );
228            }
229    
230            if ( GitUtil.getSettings().isCommitNoVerify() )
231            {
232                cl.createArg().setValue( "--no-verify" );
233            }
234    
235            return cl;
236        }
237    
238    }