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 }