You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PullCommand.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  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. }