PullCommand.java

  1. /*
  2.  * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
  3.  * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
  4.  * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue@obeo.fr> and others
  5.  *
  6.  * This program and the accompanying materials are made available under the
  7.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  8.  * https://www.eclipse.org/org/documents/edl-v10.php.
  9.  *
  10.  * SPDX-License-Identifier: BSD-3-Clause
  11.  */
  12. package org.eclipse.jgit.api;

  13. import java.io.IOException;
  14. import java.text.MessageFormat;

  15. import org.eclipse.jgit.annotations.Nullable;
  16. import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
  17. import org.eclipse.jgit.api.MergeCommand.FastForwardMode.Merge;
  18. import org.eclipse.jgit.api.RebaseCommand.Operation;
  19. import org.eclipse.jgit.api.errors.CanceledException;
  20. import org.eclipse.jgit.api.errors.GitAPIException;
  21. import org.eclipse.jgit.api.errors.InvalidConfigurationException;
  22. import org.eclipse.jgit.api.errors.InvalidRemoteException;
  23. import org.eclipse.jgit.api.errors.JGitInternalException;
  24. import org.eclipse.jgit.api.errors.NoHeadException;
  25. import org.eclipse.jgit.api.errors.RefNotAdvertisedException;
  26. import org.eclipse.jgit.api.errors.RefNotFoundException;
  27. import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
  28. import org.eclipse.jgit.dircache.DirCacheCheckout;
  29. import org.eclipse.jgit.internal.JGitText;
  30. import org.eclipse.jgit.lib.AnyObjectId;
  31. import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
  32. import org.eclipse.jgit.lib.Config;
  33. import org.eclipse.jgit.lib.ConfigConstants;
  34. import org.eclipse.jgit.lib.Constants;
  35. import org.eclipse.jgit.lib.NullProgressMonitor;
  36. import org.eclipse.jgit.lib.ObjectId;
  37. import org.eclipse.jgit.lib.ProgressMonitor;
  38. import org.eclipse.jgit.lib.Ref;
  39. import org.eclipse.jgit.lib.RefUpdate;
  40. import org.eclipse.jgit.lib.RefUpdate.Result;
  41. import org.eclipse.jgit.lib.Repository;
  42. import org.eclipse.jgit.lib.RepositoryState;
  43. import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
  44. import org.eclipse.jgit.merge.MergeStrategy;
  45. import org.eclipse.jgit.revwalk.RevCommit;
  46. import org.eclipse.jgit.revwalk.RevWalk;
  47. import org.eclipse.jgit.transport.FetchResult;
  48. import org.eclipse.jgit.transport.TagOpt;

  49. /**
  50.  * The Pull command
  51.  *
  52.  * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-pull.html"
  53.  *      >Git documentation about Pull</a>
  54.  */
  55. public class PullCommand extends TransportCommand<PullCommand, PullResult> {

  56.     private static final String DOT = "."; //$NON-NLS-1$

  57.     private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;

  58.     private BranchRebaseMode pullRebaseMode = null;

  59.     private String remote;

  60.     private String remoteBranchName;

  61.     private MergeStrategy strategy = MergeStrategy.RECURSIVE;

  62.     private TagOpt tagOption;

  63.     private FastForwardMode fastForwardMode;

  64.     private FetchRecurseSubmodulesMode submoduleRecurseMode = null;

  65.     /**
  66.      * Constructor for PullCommand.
  67.      *
  68.      * @param repo
  69.      *            the {@link org.eclipse.jgit.lib.Repository}
  70.      */
  71.     protected PullCommand(Repository repo) {
  72.         super(repo);
  73.     }

  74.     /**
  75.      * Set progress monitor
  76.      *
  77.      * @param monitor
  78.      *            a progress monitor
  79.      * @return this instance
  80.      */
  81.     public PullCommand setProgressMonitor(ProgressMonitor monitor) {
  82.         if (monitor == null) {
  83.             monitor = NullProgressMonitor.INSTANCE;
  84.         }
  85.         this.monitor = monitor;
  86.         return this;
  87.     }

  88.     /**
  89.      * Set if rebase should be used after fetching. If set to true, rebase is
  90.      * used instead of merge. This is equivalent to --rebase on the command
  91.      * line.
  92.      * <p>
  93.      * If set to false, merge is used after fetching, overriding the
  94.      * configuration file. This is equivalent to --no-rebase on the command
  95.      * line.
  96.      * <p>
  97.      * This setting overrides the settings in the configuration file. By
  98.      * default, the setting in the repository configuration file is used.
  99.      * <p>
  100.      * A branch can be configured to use rebase by default. See
  101.      * branch.[name].rebase and branch.autosetuprebase.
  102.      *
  103.      * @param useRebase
  104.      *            whether to use rebase after fetching
  105.      * @return {@code this}
  106.      */
  107.     public PullCommand setRebase(boolean useRebase) {
  108.         checkCallable();
  109.         pullRebaseMode = useRebase ? BranchRebaseMode.REBASE
  110.                 : BranchRebaseMode.NONE;
  111.         return this;
  112.     }

  113.     /**
  114.      * Sets the {@link org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode} to
  115.      * use after fetching.
  116.      *
  117.      * <dl>
  118.      * <dt>BranchRebaseMode.REBASE</dt>
  119.      * <dd>Equivalent to {@code --rebase} on the command line: use rebase
  120.      * instead of merge after fetching.</dd>
  121.      * <dt>BranchRebaseMode.PRESERVE</dt>
  122.      * <dd>Equivalent to {@code --preserve-merges} on the command line: rebase
  123.      * preserving local merge commits.</dd>
  124.      * <dt>BranchRebaseMode.INTERACTIVE</dt>
  125.      * <dd>Equivalent to {@code --interactive} on the command line: use
  126.      * interactive rebase.</dd>
  127.      * <dt>BranchRebaseMode.NONE</dt>
  128.      * <dd>Equivalent to {@code --no-rebase}: merge instead of rebasing.
  129.      * <dt>{@code null}</dt>
  130.      * <dd>Use the setting defined in the git configuration, either {@code
  131.      * branch.[name].rebase} or, if not set, {@code pull.rebase}</dd>
  132.      * </dl>
  133.      *
  134.      * This setting overrides the settings in the configuration file. By
  135.      * default, the setting in the repository configuration file is used.
  136.      * <p>
  137.      * A branch can be configured to use rebase by default. See
  138.      * {@code branch.[name].rebase}, {@code branch.autosetuprebase}, and
  139.      * {@code pull.rebase}.
  140.      *
  141.      * @param rebaseMode
  142.      *            the {@link org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode}
  143.      *            to use
  144.      * @return {@code this}
  145.      * @since 4.5
  146.      */
  147.     public PullCommand setRebase(BranchRebaseMode rebaseMode) {
  148.         checkCallable();
  149.         pullRebaseMode = rebaseMode;
  150.         return this;
  151.     }

  152.     /**
  153.      * {@inheritDoc}
  154.      * <p>
  155.      * Execute the {@code Pull} command with all the options and parameters
  156.      * collected by the setter methods (e.g.
  157.      * {@link #setProgressMonitor(ProgressMonitor)}) of this class. Each
  158.      * instance of this class should only be used for one invocation of the
  159.      * command. Don't call this method twice on an instance.
  160.      */
  161.     @Override
  162.     public PullResult call() throws GitAPIException,
  163.             WrongRepositoryStateException, InvalidConfigurationException,
  164.             InvalidRemoteException, CanceledException,
  165.             RefNotFoundException, RefNotAdvertisedException, NoHeadException,
  166.             org.eclipse.jgit.api.errors.TransportException {
  167.         checkCallable();

  168.         monitor.beginTask(JGitText.get().pullTaskName, 2);
  169.         Config repoConfig = repo.getConfig();

  170.         String branchName = null;
  171.         try {
  172.             String fullBranch = repo.getFullBranch();
  173.             if (fullBranch != null
  174.                     && fullBranch.startsWith(Constants.R_HEADS)) {
  175.                 branchName = fullBranch.substring(Constants.R_HEADS.length());
  176.             }
  177.         } catch (IOException e) {
  178.             throw new JGitInternalException(
  179.                     JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
  180.                     e);
  181.         }
  182.         if (remoteBranchName == null && branchName != null) {
  183.             // get the name of the branch in the remote repository
  184.             // stored in configuration key branch.<branch name>.merge
  185.             remoteBranchName = repoConfig.getString(
  186.                     ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
  187.                     ConfigConstants.CONFIG_KEY_MERGE);
  188.         }
  189.         if (remoteBranchName == null) {
  190.             remoteBranchName = branchName;
  191.         }
  192.         if (remoteBranchName == null) {
  193.             throw new NoHeadException(
  194.                     JGitText.get().cannotCheckoutFromUnbornBranch);
  195.         }

  196.         if (!repo.getRepositoryState().equals(RepositoryState.SAFE))
  197.             throw new WrongRepositoryStateException(MessageFormat.format(
  198.                     JGitText.get().cannotPullOnARepoWithState, repo
  199.                             .getRepositoryState().name()));

  200.         if (remote == null && branchName != null) {
  201.             // get the configured remote for the currently checked out branch
  202.             // stored in configuration key branch.<branch name>.remote
  203.             remote = repoConfig.getString(
  204.                     ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
  205.                     ConfigConstants.CONFIG_KEY_REMOTE);
  206.         }
  207.         if (remote == null) {
  208.             // fall back to default remote
  209.             remote = Constants.DEFAULT_REMOTE_NAME;
  210.         }

  211.         // determines whether rebase should be used after fetching
  212.         if (pullRebaseMode == null && branchName != null) {
  213.             pullRebaseMode = getRebaseMode(branchName, repoConfig);
  214.         }


  215.         final boolean isRemote = !remote.equals("."); //$NON-NLS-1$
  216.         String remoteUri;
  217.         FetchResult fetchRes;
  218.         if (isRemote) {
  219.             remoteUri = repoConfig.getString(
  220.                     ConfigConstants.CONFIG_REMOTE_SECTION, remote,
  221.                     ConfigConstants.CONFIG_KEY_URL);
  222.             if (remoteUri == null) {
  223.                 String missingKey = ConfigConstants.CONFIG_REMOTE_SECTION + DOT
  224.                         + remote + DOT + ConfigConstants.CONFIG_KEY_URL;
  225.                 throw new InvalidConfigurationException(MessageFormat.format(
  226.                         JGitText.get().missingConfigurationForKey, missingKey));
  227.             }

  228.             if (monitor.isCancelled())
  229.                 throw new CanceledException(MessageFormat.format(
  230.                         JGitText.get().operationCanceled,
  231.                         JGitText.get().pullTaskName));

  232.             FetchCommand fetch = new FetchCommand(repo).setRemote(remote)
  233.                     .setProgressMonitor(monitor).setTagOpt(tagOption)
  234.                     .setRecurseSubmodules(submoduleRecurseMode);
  235.             configure(fetch);

  236.             fetchRes = fetch.call();
  237.         } else {
  238.             // we can skip the fetch altogether
  239.             remoteUri = JGitText.get().localRepository;
  240.             fetchRes = null;
  241.         }

  242.         monitor.update(1);

  243.         if (monitor.isCancelled())
  244.             throw new CanceledException(MessageFormat.format(
  245.                     JGitText.get().operationCanceled,
  246.                     JGitText.get().pullTaskName));

  247.         // we check the updates to see which of the updated branches
  248.         // corresponds
  249.         // to the remote branch name
  250.         AnyObjectId commitToMerge;
  251.         if (isRemote) {
  252.             Ref r = null;
  253.             if (fetchRes != null) {
  254.                 r = fetchRes.getAdvertisedRef(remoteBranchName);
  255.                 if (r == null) {
  256.                     r = fetchRes.getAdvertisedRef(Constants.R_HEADS
  257.                             + remoteBranchName);
  258.                 }
  259.             }
  260.             if (r == null) {
  261.                 throw new RefNotAdvertisedException(MessageFormat.format(
  262.                         JGitText.get().couldNotGetAdvertisedRef, remote,
  263.                         remoteBranchName));
  264.             }
  265.             commitToMerge = r.getObjectId();
  266.         } else {
  267.             try {
  268.                 commitToMerge = repo.resolve(remoteBranchName);
  269.                 if (commitToMerge == null) {
  270.                     throw new RefNotFoundException(MessageFormat.format(
  271.                             JGitText.get().refNotResolved, remoteBranchName));
  272.                 }
  273.             } catch (IOException e) {
  274.                 throw new JGitInternalException(
  275.                         JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
  276.                         e);
  277.             }
  278.         }

  279.         String upstreamName = MessageFormat.format(
  280.                 JGitText.get().upstreamBranchName,
  281.                 Repository.shortenRefName(remoteBranchName), remoteUri);

  282.         PullResult result;
  283.         if (pullRebaseMode != BranchRebaseMode.NONE) {
  284.             try {
  285.                 Ref head = repo.exactRef(Constants.HEAD);
  286.                 if (head == null) {
  287.                     throw new NoHeadException(JGitText
  288.                             .get().commitOnRepoWithoutHEADCurrentlyNotSupported);
  289.                 }
  290.                 ObjectId headId = head.getObjectId();
  291.                 if (headId == null) {
  292.                     // Pull on an unborn branch: checkout
  293.                     try (RevWalk revWalk = new RevWalk(repo)) {
  294.                         RevCommit srcCommit = revWalk
  295.                                 .parseCommit(commitToMerge);
  296.                         DirCacheCheckout dco = new DirCacheCheckout(repo,
  297.                                 repo.lockDirCache(), srcCommit.getTree());
  298.                         dco.setFailOnConflict(true);
  299.                         dco.setProgressMonitor(monitor);
  300.                         dco.checkout();
  301.                         RefUpdate refUpdate = repo
  302.                                 .updateRef(head.getTarget().getName());
  303.                         refUpdate.setNewObjectId(commitToMerge);
  304.                         refUpdate.setExpectedOldObjectId(null);
  305.                         refUpdate.setRefLogMessage("initial pull", false); //$NON-NLS-1$
  306.                         if (refUpdate.update() != Result.NEW) {
  307.                             throw new NoHeadException(JGitText
  308.                                     .get().commitOnRepoWithoutHEADCurrentlyNotSupported);
  309.                         }
  310.                         monitor.endTask();
  311.                         return new PullResult(fetchRes, remote,
  312.                                 RebaseResult.result(
  313.                                         RebaseResult.Status.FAST_FORWARD,
  314.                                         srcCommit));
  315.                     }
  316.                 }
  317.             } catch (NoHeadException e) {
  318.                 throw e;
  319.             } catch (IOException e) {
  320.                 throw new JGitInternalException(JGitText
  321.                         .get().exceptionCaughtDuringExecutionOfPullCommand, e);
  322.             }
  323.             RebaseCommand rebase = new RebaseCommand(repo);
  324.             RebaseResult rebaseRes = rebase.setUpstream(commitToMerge)
  325.                     .setUpstreamName(upstreamName).setProgressMonitor(monitor)
  326.                     .setOperation(Operation.BEGIN).setStrategy(strategy)
  327.                     .setPreserveMerges(
  328.                             pullRebaseMode == BranchRebaseMode.PRESERVE)
  329.                     .call();
  330.             result = new PullResult(fetchRes, remote, rebaseRes);
  331.         } else {
  332.             MergeCommand merge = new MergeCommand(repo);
  333.             MergeResult mergeRes = merge.include(upstreamName, commitToMerge)
  334.                     .setStrategy(strategy).setProgressMonitor(monitor)
  335.                     .setFastForward(getFastForwardMode()).call();
  336.             monitor.update(1);
  337.             result = new PullResult(fetchRes, remote, mergeRes);
  338.         }
  339.         monitor.endTask();
  340.         return result;
  341.     }

  342.     /**
  343.      * The remote (uri or name) to be used for the pull operation. If no remote
  344.      * is set, the branch's configuration will be used. If the branch
  345.      * configuration is missing the default value of
  346.      * <code>Constants.DEFAULT_REMOTE_NAME</code> will be used.
  347.      *
  348.      * @see Constants#DEFAULT_REMOTE_NAME
  349.      * @param remote
  350.      *            name of the remote to pull from
  351.      * @return {@code this}
  352.      * @since 3.3
  353.      */
  354.     public PullCommand setRemote(String remote) {
  355.         checkCallable();
  356.         this.remote = remote;
  357.         return this;
  358.     }

  359.     /**
  360.      * The remote branch name to be used for the pull operation. If no
  361.      * remoteBranchName is set, the branch's configuration will be used. If the
  362.      * branch configuration is missing the remote branch with the same name as
  363.      * the current branch is used.
  364.      *
  365.      * @param remoteBranchName
  366.      *            remote branch name to be used for pull operation
  367.      * @return {@code this}
  368.      * @since 3.3
  369.      */
  370.     public PullCommand setRemoteBranchName(String remoteBranchName) {
  371.         checkCallable();
  372.         this.remoteBranchName = remoteBranchName;
  373.         return this;
  374.     }

  375.     /**
  376.      * Get the remote name used for pull operation
  377.      *
  378.      * @return the remote used for the pull operation if it was set explicitly
  379.      * @since 3.3
  380.      */
  381.     public String getRemote() {
  382.         return remote;
  383.     }

  384.     /**
  385.      * Get the remote branch name for the pull operation
  386.      *
  387.      * @return the remote branch name used for the pull operation if it was set
  388.      *         explicitly
  389.      * @since 3.3
  390.      */
  391.     public String getRemoteBranchName() {
  392.         return remoteBranchName;
  393.     }

  394.     /**
  395.      * Set the @{code MergeStrategy}
  396.      *
  397.      * @param strategy
  398.      *            The merge strategy to use during this pull operation.
  399.      * @return {@code this}
  400.      * @since 3.4
  401.      */
  402.     public PullCommand setStrategy(MergeStrategy strategy) {
  403.         this.strategy = strategy;
  404.         return this;
  405.     }

  406.     /**
  407.      * Set the specification of annotated tag behavior during fetch
  408.      *
  409.      * @param tagOpt
  410.      *            the {@link org.eclipse.jgit.transport.TagOpt}
  411.      * @return {@code this}
  412.      * @since 4.7
  413.      */
  414.     public PullCommand setTagOpt(TagOpt tagOpt) {
  415.         checkCallable();
  416.         this.tagOption = tagOpt;
  417.         return this;
  418.     }

  419.     /**
  420.      * Set the fast forward mode. It is used if pull is configured to do a merge
  421.      * as opposed to rebase. If non-{@code null} takes precedence over the
  422.      * fast-forward mode configured in git config.
  423.      *
  424.      * @param fastForwardMode
  425.      *            corresponds to the --ff/--no-ff/--ff-only options. If
  426.      *            {@code null} use the value of {@code pull.ff} configured in
  427.      *            git config. If {@code pull.ff} is not configured fall back to
  428.      *            the value of {@code merge.ff}. If {@code merge.ff} is not
  429.      *            configured --ff is the built-in default.
  430.      * @return {@code this}
  431.      * @since 4.9
  432.      */
  433.     public PullCommand setFastForward(
  434.             @Nullable FastForwardMode fastForwardMode) {
  435.         checkCallable();
  436.         this.fastForwardMode = fastForwardMode;
  437.         return this;
  438.     }

  439.     /**
  440.      * Set the mode to be used for recursing into submodules.
  441.      *
  442.      * @param recurse
  443.      *            the
  444.      *            {@link org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode}
  445.      *            to be used for recursing into submodules
  446.      * @return {@code this}
  447.      * @since 4.7
  448.      * @see FetchCommand#setRecurseSubmodules(FetchRecurseSubmodulesMode)
  449.      */
  450.     public PullCommand setRecurseSubmodules(
  451.             @Nullable FetchRecurseSubmodulesMode recurse) {
  452.         this.submoduleRecurseMode = recurse;
  453.         return this;
  454.     }

  455.     /**
  456.      * Reads the rebase mode to use for a pull command from the repository
  457.      * configuration. This is the value defined for the configurations
  458.      * {@code branch.[branchName].rebase}, or,if not set, {@code pull.rebase}.
  459.      * If neither is set, yields
  460.      * {@link org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode#NONE}.
  461.      *
  462.      * @param branchName
  463.      *            name of the local branch
  464.      * @param config
  465.      *            the {@link org.eclipse.jgit.lib.Config} to read the value from
  466.      * @return the {@link org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode}
  467.      * @since 4.5
  468.      */
  469.     public static BranchRebaseMode getRebaseMode(String branchName,
  470.             Config config) {
  471.         BranchRebaseMode mode = config.getEnum(BranchRebaseMode.values(),
  472.                 ConfigConstants.CONFIG_BRANCH_SECTION,
  473.                 branchName, ConfigConstants.CONFIG_KEY_REBASE, null);
  474.         if (mode == null) {
  475.             mode = config.getEnum(BranchRebaseMode.values(),
  476.                     ConfigConstants.CONFIG_PULL_SECTION, null,
  477.                     ConfigConstants.CONFIG_KEY_REBASE, BranchRebaseMode.NONE);
  478.         }
  479.         return mode;
  480.     }

  481.     private FastForwardMode getFastForwardMode() {
  482.         if (fastForwardMode != null) {
  483.             return fastForwardMode;
  484.         }
  485.         Config config = repo.getConfig();
  486.         Merge ffMode = config.getEnum(Merge.values(),
  487.                 ConfigConstants.CONFIG_PULL_SECTION, null,
  488.                 ConfigConstants.CONFIG_KEY_FF, null);
  489.         return ffMode != null ? FastForwardMode.valueOf(ffMode) : null;
  490.     }
  491. }