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