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 *
046 */
047public class GitUpdateCommand extends AbstractUpdateCommand implements GitCommand {
048    /** {@inheritDoc} */
049    protected UpdateScmResult executeUpdateCommand(
050            ScmProviderRepository repo, ScmFileSet fileSet, ScmVersion scmVersion) throws ScmException {
051        GitScmProviderRepository repository = (GitScmProviderRepository) repo;
052
053        if (GitScmProviderRepository.PROTOCOL_FILE.equals(
054                        repository.getFetchInfo().getProtocol())
055                && repository
056                                .getFetchInfo()
057                                .getPath()
058                                .indexOf(fileSet.getBasedir().getPath())
059                        >= 0) {
060            throw new ScmException("remote repository must not be the working directory");
061        }
062
063        int exitCode;
064
065        CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
066        CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
067
068        // fir we need to get the current reversion
069        Commandline clRev = createLatestRevisionCommandLine(repository, fileSet.getBasedir(), scmVersion);
070        GitLatestRevisionCommandConsumer consumerRev = new GitLatestRevisionCommandConsumer();
071        exitCode = GitCommandLineUtils.execute(clRev, consumerRev, stderr);
072        if (exitCode != 0) {
073            return new UpdateScmResult(clRev.toString(), "The git-log command failed.", stderr.getOutput(), false);
074        }
075        String origSha1 = consumerRev.getLatestRevision();
076
077        Commandline cl = createCommandLine(repository, fileSet.getBasedir(), scmVersion);
078        exitCode = GitCommandLineUtils.execute(cl, stdout, stderr);
079        if (exitCode != 0) {
080            return new UpdateScmResult(cl.toString(), "The git-pull command failed.", stderr.getOutput(), false);
081        }
082
083        // we also need to log exactly what has been updated
084        GitDiffRawConsumer diffRawConsumer = new GitDiffRawConsumer();
085        Commandline clDiffRaw = GitDiffCommand.createDiffRawCommandLine(fileSet.getBasedir(), origSha1);
086        exitCode = GitCommandLineUtils.execute(clDiffRaw, diffRawConsumer, stderr);
087        if (exitCode != 0) {
088            return new UpdateScmResult(
089                    clDiffRaw.toString(), "The git-diff --raw command failed.", stderr.getOutput(), false);
090        }
091
092        // now let's get the latest version
093        consumerRev = new GitLatestRevisionCommandConsumer();
094        exitCode = GitCommandLineUtils.execute(clRev, consumerRev, stderr);
095        if (exitCode != 0) {
096            return new UpdateScmResult(clRev.toString(), "The git-log command failed.", stderr.getOutput(), false);
097        }
098        String latestRevision = consumerRev.getLatestRevision();
099
100        return new UpdateScmResultWithRevision(cl.toString(), diffRawConsumer.getChangedFiles(), latestRevision);
101    }
102
103    /** {@inheritDoc} */
104    protected ChangeLogCommand getChangeLogCommand() {
105        return new GitChangeLogCommand();
106    }
107
108    /**
109     * create the command line for updating the current branch with the info from the foreign repository.
110     */
111    public static Commandline createCommandLine(
112            GitScmProviderRepository repository, File workingDirectory, ScmVersion scmVersion) {
113        Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(workingDirectory, "pull");
114
115        cl.createArg().setLine(repository.getFetchUrl());
116
117        // now set the branch where we would like to pull from
118        if (scmVersion instanceof ScmBranch) {
119            cl.createArg().setLine(scmVersion.getName());
120        }
121
122        return cl;
123    }
124
125    /**
126     * @param scmVersion a valid branch or <code>null</code> if the master branch should be taken
127     * @return CommandLine for getting the latest commit on the given branch
128     */
129    public static Commandline createLatestRevisionCommandLine(
130            GitScmProviderRepository repository, File workingDirectory, ScmVersion scmVersion) {
131        Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(workingDirectory, "log");
132
133        // only show exactly 1 commit
134        cl.createArg().setValue("-n1");
135
136        // same as --topo-order, but ensure ordering of merges
137        cl.createArg().setValue("--date-order");
138
139        if (scmVersion != null
140                && scmVersion instanceof ScmBranch
141                && scmVersion.getName() != null
142                && scmVersion.getName().length() > 0) {
143            // if any branch is given, lets take em
144            cl.createArg().setValue(scmVersion.getName());
145        }
146        // otherwise we work on HEAD/current branch
147
148        return cl;
149    }
150}