001package org.apache.maven.scm.provider.git.jgit.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
022import org.apache.maven.scm.ScmException;
023import org.apache.maven.scm.ScmFile;
024import org.apache.maven.scm.ScmFileSet;
025import org.apache.maven.scm.ScmVersion;
026import org.apache.maven.scm.command.checkin.AbstractCheckInCommand;
027import org.apache.maven.scm.command.checkin.CheckInScmResult;
028import org.apache.maven.scm.provider.ScmProviderRepository;
029import org.apache.maven.scm.provider.git.command.GitCommand;
030import org.apache.maven.scm.provider.git.jgit.command.JGitUtils;
031import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
032import org.codehaus.plexus.util.StringUtils;
033import org.eclipse.jgit.api.AddCommand;
034import org.eclipse.jgit.api.Git;
035import org.eclipse.jgit.lib.Constants;
036import org.eclipse.jgit.lib.UserConfig;
037import org.eclipse.jgit.revwalk.RevCommit;
038import org.eclipse.jgit.transport.RefSpec;
039
040import java.io.File;
041import java.net.InetAddress;
042import java.net.UnknownHostException;
043import java.util.Collections;
044import java.util.List;
045import java.util.Set;
046
047/**
048 * This provider uses the following strategy to discover the committer and author name/mail for a commit:
049 * <ol>
050 * <li>"user" section in .gitconfig</li>
051 * <li>"username" passed to maven execution</li>
052 * <li>default git config (system user and hostname for email)</li>
053 * </ol>
054 * the "maven-scm" config can be configured like this: <br>
055 * the default email domain to be used (will be used to create an email from the username passed to maven):<br>
056 * <code>git config --global maven-scm.maildomain mycomp.com</code> <br>
057 * you can also enforce the usage of the username for the author and committer:<br>
058 * <code>git config --global maven-scm.forceUsername true</code> <br>
059 * 
060 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
061 * @author Dominik Bartholdi (imod)
062 * @since 1.9
063 */
064public class JGitCheckInCommand
065    extends AbstractCheckInCommand
066    implements GitCommand
067{
068
069    protected static final String GIT_MAVEN_SECTION = "maven-scm";
070
071    protected static final String GIT_MAILDOMAIN = "maildomain";
072
073    protected static final String GIT_FORCE = "forceUsername";
074
075    /**
076     * {@inheritDoc}
077     */
078    protected CheckInScmResult executeCheckInCommand( ScmProviderRepository repo, ScmFileSet fileSet, String message,
079                                                      ScmVersion version )
080        throws ScmException
081    {
082
083        Git git = null;
084        try
085        {
086            File basedir = fileSet.getBasedir();
087            git = Git.open( basedir );
088
089            boolean doCommit = false;
090
091            if ( !fileSet.getFileList().isEmpty() )
092            {
093                doCommit = JGitUtils.addAllFiles( git, fileSet ).size() > 0;
094            }
095            else
096            {
097                // add all tracked files which are modified manually
098                Set<String> changeds = git.status().call().getModified();
099                if ( changeds.isEmpty() )
100                {
101                    // warn there is nothing to add
102                    getLogger().warn( "there are no files to be added" );
103                    doCommit = false;
104                }
105                else
106                {
107                    AddCommand add = git.add();
108                    for ( String changed : changeds )
109                    {
110                        getLogger().debug( "add manualy: " + changed );
111                        add.addFilepattern( changed );
112                        doCommit = true;
113                    }
114                    add.call();
115                }
116            }
117
118            List<ScmFile> checkedInFiles = Collections.emptyList();
119            if ( doCommit )
120            {
121                UserInfo author = getAuthor( repo, git );
122                UserInfo committer = getCommitter( repo, git );
123
124                RevCommit commitRev =
125                    git.commit().setMessage( message ).setAuthor( author.name, author.email ).setCommitter( committer.name,
126                                                                                                            committer.email ).call();
127                getLogger().info( "commit done: " + commitRev.getShortMessage() );
128                checkedInFiles = JGitUtils.getFilesInCommit( git.getRepository(), commitRev );
129                if ( getLogger().isDebugEnabled() )
130                {
131                    for ( ScmFile scmFile : checkedInFiles )
132                    {
133                        getLogger().debug( "in commit: " + scmFile );
134                    }
135                }
136            }
137
138            if ( repo.isPushChanges() )
139            {
140                String branch = version != null ? version.getName() : null;
141                if ( StringUtils.isBlank( branch ) )
142                {
143                    branch = git.getRepository().getBranch();
144                }
145                RefSpec refSpec = new RefSpec( Constants.R_HEADS + branch + ":" + Constants.R_HEADS + branch );
146                getLogger().info( "push changes to remote... " + refSpec.toString() );
147                JGitUtils.push( getLogger(), git, (GitScmProviderRepository) repo, refSpec );
148            }
149
150            return new CheckInScmResult( "JGit checkin", checkedInFiles );
151        }
152        catch ( Exception e )
153        {
154            throw new ScmException( "JGit checkin failure!", e );
155        }
156        finally
157        {
158            JGitUtils.closeRepo( git );
159        }
160    }
161
162    private static final class UserInfo
163    {
164        final String name;
165
166        final String email;
167
168        public UserInfo( String name, String email )
169        {
170            this.name = name;
171            this.email = email;
172        }
173    }
174
175    private UserInfo getCommitter( ScmProviderRepository repo, Git git )
176    {
177        boolean forceMvnUser = git.getRepository().getConfig().getBoolean( GIT_MAVEN_SECTION, GIT_FORCE, false );
178
179        // git config
180        UserConfig user = git.getRepository().getConfig().get( UserConfig.KEY );
181        String committerName = null;
182        if ( !forceMvnUser && !user.isCommitterNameImplicit() )
183        {
184            committerName = user.getCommitterName();
185        }
186
187        // mvn parameter
188        if ( StringUtils.isBlank( committerName ) )
189        {
190            committerName = repo.getUser();
191        }
192
193        // git default
194        if ( StringUtils.isBlank( committerName ) )
195        {
196            committerName = user.getCommitterName();
197        }
198
199        // git config
200        String committerMail = null;
201        if ( !user.isCommitterEmailImplicit() )
202        {
203            committerMail = user.getCommitterEmail();
204        }
205
206        if ( StringUtils.isBlank( committerMail ) )
207        {
208            String defaultDomain = git.getRepository().getConfig().getString( GIT_MAVEN_SECTION, null, GIT_MAILDOMAIN );
209            defaultDomain = StringUtils.isNotBlank( defaultDomain ) ? defaultDomain : getHostname();
210
211            // mvn parameter (constructed with username) or git default
212            committerMail =
213                StringUtils.isNotBlank( repo.getUser() ) ? repo.getUser() + "@" + defaultDomain
214                                : user.getCommitterEmail();
215        }
216
217        return new UserInfo( committerName, committerMail );
218    }
219
220    private UserInfo getAuthor( ScmProviderRepository repo, Git git )
221    {
222        boolean forceMvnUser = git.getRepository().getConfig().getBoolean( GIT_MAVEN_SECTION, GIT_FORCE, false );
223
224        // git config
225        UserConfig user = git.getRepository().getConfig().get( UserConfig.KEY );
226        String authorName = null;
227        if ( !forceMvnUser && !user.isAuthorNameImplicit() )
228        {
229            authorName = user.getAuthorName();
230        }
231
232        // mvn parameter
233        if ( StringUtils.isBlank( authorName ) )
234        {
235            authorName = repo.getUser();
236        }
237
238        // git default
239        if ( StringUtils.isBlank( authorName ) )
240        {
241            authorName = user.getAuthorName();
242        }
243
244        // git config
245        String authorMail = null;
246        if ( !user.isAuthorEmailImplicit() )
247        {
248            authorMail = user.getAuthorEmail();
249        }
250
251        if ( StringUtils.isBlank( authorMail ) )
252        {
253            String defaultDomain = git.getRepository().getConfig().getString( GIT_MAVEN_SECTION, null, GIT_MAILDOMAIN );
254            defaultDomain = StringUtils.isNotBlank( defaultDomain ) ? defaultDomain : getHostname();
255
256            // mvn parameter (constructed with username) or git default
257            authorMail =
258                StringUtils.isNotBlank( repo.getUser() ) ? repo.getUser() + "@" + defaultDomain : user.getAuthorEmail();
259        }
260
261        return new UserInfo( authorName, authorMail );
262    }
263
264    private String getHostname()
265    {
266        String hostname;
267        try
268        {
269            InetAddress localhost = java.net.InetAddress.getLocalHost();
270            hostname = localhost.getHostName();
271        }
272        catch ( UnknownHostException e )
273        {
274            getLogger().warn( "failed to resolve hostname to create mail address, defaulting to 'maven-scm-provider-jgit'" );
275            hostname = "maven-scm-provider-jgit";
276        }
277        return hostname;
278    }
279
280}