001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.scm.provider.git.gitexe.command.update;
020
021import java.io.File;
022
023import org.apache.maven.scm.ScmBranch;
024import org.apache.maven.scm.ScmException;
025import org.apache.maven.scm.ScmFileSet;
026import org.apache.maven.scm.ScmVersion;
027import org.apache.maven.scm.command.changelog.ChangeLogCommand;
028import org.apache.maven.scm.command.update.AbstractUpdateCommand;
029import org.apache.maven.scm.command.update.UpdateScmResult;
030import org.apache.maven.scm.command.update.UpdateScmResultWithRevision;
031import org.apache.maven.scm.provider.ScmProviderRepository;
032import org.apache.maven.scm.provider.git.command.GitCommand;
033import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils;
034import org.apache.maven.scm.provider.git.gitexe.command.changelog.GitChangeLogCommand;
035import org.apache.maven.scm.provider.git.gitexe.command.diff.GitDiffCommand;
036import org.apache.maven.scm.provider.git.gitexe.command.diff.GitDiffRawConsumer;
037import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
038import org.codehaus.plexus.util.cli.CommandLineUtils;
039import org.codehaus.plexus.util.cli.Commandline;
040
041/**
042 * @author Olivier Lamy
043 * @author <a href="mailto:struberg@yahoo.de">struberg</a>
044 * @since 10 august 2008
045 */
046public class GitUpdateCommand extends AbstractUpdateCommand implements GitCommand {
047    /**
048     * {@inheritDoc}
049     */
050    protected UpdateScmResult executeUpdateCommand(
051            ScmProviderRepository repo, ScmFileSet fileSet, ScmVersion scmVersion) throws ScmException {
052        GitScmProviderRepository repository = (GitScmProviderRepository) repo;
053
054        if (GitScmProviderRepository.PROTOCOL_FILE.equals(
055                        repository.getFetchInfo().getProtocol())
056                && repository
057                                .getFetchInfo()
058                                .getPath()
059                                .indexOf(fileSet.getBasedir().getPath())
060                        >= 0) {
061            throw new ScmException("remote repository must not be the working directory");
062        }
063
064        int exitCode;
065
066        CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
067        CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
068
069        // fir we need to get the current reversion
070        Commandline clRev = createLatestRevisionCommandLine(repository, fileSet.getBasedir(), scmVersion);
071        GitLatestRevisionCommandConsumer consumerRev = new GitLatestRevisionCommandConsumer();
072        exitCode = GitCommandLineUtils.execute(clRev, consumerRev, stderr);
073        if (exitCode != 0) {
074            return new UpdateScmResult(clRev.toString(), "The git-log command failed.", stderr.getOutput(), false);
075        }
076        String origSha1 = consumerRev.getLatestRevision();
077
078        Commandline cl = createCommandLine(repository, fileSet.getBasedir(), scmVersion);
079        exitCode = GitCommandLineUtils.execute(cl, stdout, stderr);
080        if (exitCode != 0) {
081            return new UpdateScmResult(cl.toString(), "The git-pull command failed.", stderr.getOutput(), false);
082        }
083
084        // we also need to log exactly what has been updated
085        GitDiffRawConsumer diffRawConsumer = new GitDiffRawConsumer();
086        Commandline clDiffRaw = GitDiffCommand.createDiffRawCommandLine(fileSet.getBasedir(), origSha1);
087        exitCode = GitCommandLineUtils.execute(clDiffRaw, diffRawConsumer, stderr);
088        if (exitCode != 0) {
089            return new UpdateScmResult(
090                    clDiffRaw.toString(), "The git-diff --raw command failed.", stderr.getOutput(), false);
091        }
092
093        // now let's get the latest version
094        consumerRev = new GitLatestRevisionCommandConsumer();
095        exitCode = GitCommandLineUtils.execute(clRev, consumerRev, stderr);
096        if (exitCode != 0) {
097            return new UpdateScmResult(clRev.toString(), "The git-log command failed.", stderr.getOutput(), false);
098        }
099        String latestRevision = consumerRev.getLatestRevision();
100
101        return new UpdateScmResultWithRevision(cl.toString(), diffRawConsumer.getChangedFiles(), latestRevision);
102    }
103
104    /**
105     * {@inheritDoc}
106     */
107    protected ChangeLogCommand getChangeLogCommand() {
108        return new GitChangeLogCommand();
109    }
110
111    /**
112     * Create the command line for updating the current branch with the info from the foreign repository.
113     */
114    public static Commandline createCommandLine(
115            GitScmProviderRepository repository, File workingDirectory, ScmVersion scmVersion) {
116        Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(workingDirectory, "pull");
117
118        cl.createArg().setLine(repository.getFetchUrl());
119
120        // now set the branch where we would like to pull from
121        if (scmVersion instanceof ScmBranch) {
122            cl.createArg().setLine(scmVersion.getName());
123        }
124
125        return cl;
126    }
127
128    /**
129     * @param scmVersion a valid branch or <code>null</code> if the master branch should be taken
130     * @return CommandLine for getting the latest commit on the given branch
131     */
132    public static Commandline createLatestRevisionCommandLine(
133            GitScmProviderRepository repository, File workingDirectory, ScmVersion scmVersion) {
134        Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(workingDirectory, "log");
135
136        // only show exactly 1 commit
137        cl.createArg().setValue("-n1");
138
139        // same as --topo-order, but ensure ordering of merges
140        cl.createArg().setValue("--date-order");
141
142        if (scmVersion instanceof ScmBranch
143                && scmVersion.getName() != null
144                && scmVersion.getName().length() > 0) {
145            // if any branch is given, lets take em
146            cl.createArg().setValue(scmVersion.getName());
147        }
148        // otherwise we work on HEAD/current branch
149
150        return cl;
151    }
152}