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.

FetchCommand.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /*
  2. * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.api;
  11. import static java.util.stream.Collectors.toList;
  12. import java.io.IOException;
  13. import java.net.URISyntaxException;
  14. import java.text.MessageFormat;
  15. import java.util.ArrayList;
  16. import java.util.Arrays;
  17. import java.util.List;
  18. import org.eclipse.jgit.annotations.Nullable;
  19. import org.eclipse.jgit.api.errors.GitAPIException;
  20. import org.eclipse.jgit.api.errors.InvalidConfigurationException;
  21. import org.eclipse.jgit.api.errors.InvalidRemoteException;
  22. import org.eclipse.jgit.api.errors.JGitInternalException;
  23. import org.eclipse.jgit.errors.ConfigInvalidException;
  24. import org.eclipse.jgit.errors.NoRemoteRepositoryException;
  25. import org.eclipse.jgit.errors.NotSupportedException;
  26. import org.eclipse.jgit.errors.TransportException;
  27. import org.eclipse.jgit.internal.JGitText;
  28. import org.eclipse.jgit.lib.ConfigConstants;
  29. import org.eclipse.jgit.lib.Constants;
  30. import org.eclipse.jgit.lib.NullProgressMonitor;
  31. import org.eclipse.jgit.lib.ObjectId;
  32. import org.eclipse.jgit.lib.ProgressMonitor;
  33. import org.eclipse.jgit.lib.Repository;
  34. import org.eclipse.jgit.lib.StoredConfig;
  35. import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
  36. import org.eclipse.jgit.revwalk.RevWalk;
  37. import org.eclipse.jgit.submodule.SubmoduleWalk;
  38. import org.eclipse.jgit.transport.FetchResult;
  39. import org.eclipse.jgit.transport.RefSpec;
  40. import org.eclipse.jgit.transport.TagOpt;
  41. import org.eclipse.jgit.transport.Transport;
  42. /**
  43. * A class used to execute a {@code Fetch} command. It has setters for all
  44. * supported options and arguments of this command and a {@link #call()} method
  45. * to finally execute the command.
  46. *
  47. * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-fetch.html"
  48. * >Git documentation about Fetch</a>
  49. */
  50. public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
  51. private String remote = Constants.DEFAULT_REMOTE_NAME;
  52. private List<RefSpec> refSpecs;
  53. private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
  54. private boolean checkFetchedObjects;
  55. private Boolean removeDeletedRefs;
  56. private boolean dryRun;
  57. private boolean thin = Transport.DEFAULT_FETCH_THIN;
  58. private TagOpt tagOption;
  59. private FetchRecurseSubmodulesMode submoduleRecurseMode = null;
  60. private Callback callback;
  61. private boolean isForceUpdate;
  62. private String initialBranch;
  63. /**
  64. * Callback for status of fetch operation.
  65. *
  66. * @since 4.8
  67. *
  68. */
  69. public interface Callback {
  70. /**
  71. * Notify fetching a submodule.
  72. *
  73. * @param name
  74. * the submodule name.
  75. */
  76. void fetchingSubmodule(String name);
  77. }
  78. /**
  79. * Constructor for FetchCommand.
  80. *
  81. * @param repo
  82. * a {@link org.eclipse.jgit.lib.Repository} object.
  83. */
  84. protected FetchCommand(Repository repo) {
  85. super(repo);
  86. refSpecs = new ArrayList<>(3);
  87. }
  88. private FetchRecurseSubmodulesMode getRecurseMode(String path) {
  89. // Use the caller-specified mode, if set
  90. if (submoduleRecurseMode != null) {
  91. return submoduleRecurseMode;
  92. }
  93. // Fall back to submodule.name.fetchRecurseSubmodules, if set
  94. FetchRecurseSubmodulesMode mode = repo.getConfig().getEnum(
  95. FetchRecurseSubmodulesMode.values(),
  96. ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
  97. ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES, null);
  98. if (mode != null) {
  99. return mode;
  100. }
  101. // Fall back to fetch.recurseSubmodules, if set
  102. mode = repo.getConfig().getEnum(FetchRecurseSubmodulesMode.values(),
  103. ConfigConstants.CONFIG_FETCH_SECTION, null,
  104. ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES, null);
  105. if (mode != null) {
  106. return mode;
  107. }
  108. // Default to on-demand mode
  109. return FetchRecurseSubmodulesMode.ON_DEMAND;
  110. }
  111. private void fetchSubmodules(FetchResult results)
  112. throws org.eclipse.jgit.api.errors.TransportException,
  113. GitAPIException, InvalidConfigurationException {
  114. try (SubmoduleWalk walk = new SubmoduleWalk(repo);
  115. RevWalk revWalk = new RevWalk(repo)) {
  116. // Walk over submodules in the parent repository's FETCH_HEAD.
  117. ObjectId fetchHead = repo.resolve(Constants.FETCH_HEAD);
  118. if (fetchHead == null) {
  119. return;
  120. }
  121. walk.setTree(revWalk.parseTree(fetchHead));
  122. while (walk.next()) {
  123. try (Repository submoduleRepo = walk.getRepository()) {
  124. // Skip submodules that don't exist locally (have not been
  125. // cloned), are not registered in the .gitmodules file, or
  126. // not registered in the parent repository's config.
  127. if (submoduleRepo == null || walk.getModulesPath() == null
  128. || walk.getConfigUrl() == null) {
  129. continue;
  130. }
  131. FetchRecurseSubmodulesMode recurseMode = getRecurseMode(
  132. walk.getPath());
  133. // When the fetch mode is "yes" we always fetch. When the
  134. // mode
  135. // is "on demand", we only fetch if the submodule's revision
  136. // was
  137. // updated to an object that is not currently present in the
  138. // submodule.
  139. if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND
  140. && !submoduleRepo.getObjectDatabase()
  141. .has(walk.getObjectId()))
  142. || recurseMode == FetchRecurseSubmodulesMode.YES) {
  143. FetchCommand f = new FetchCommand(submoduleRepo)
  144. .setProgressMonitor(monitor)
  145. .setTagOpt(tagOption)
  146. .setCheckFetchedObjects(checkFetchedObjects)
  147. .setRemoveDeletedRefs(isRemoveDeletedRefs())
  148. .setThin(thin)
  149. .setRefSpecs(applyOptions(refSpecs))
  150. .setDryRun(dryRun)
  151. .setRecurseSubmodules(recurseMode);
  152. configure(f);
  153. if (callback != null) {
  154. callback.fetchingSubmodule(walk.getPath());
  155. }
  156. results.addSubmodule(walk.getPath(), f.call());
  157. }
  158. }
  159. }
  160. } catch (IOException e) {
  161. throw new JGitInternalException(e.getMessage(), e);
  162. } catch (ConfigInvalidException e) {
  163. throw new InvalidConfigurationException(e.getMessage(), e);
  164. }
  165. }
  166. /**
  167. * {@inheritDoc}
  168. * <p>
  169. * Execute the {@code fetch} command with all the options and parameters
  170. * collected by the setter methods of this class. Each instance of this
  171. * class should only be used for one invocation of the command (means: one
  172. * call to {@link #call()})
  173. */
  174. @Override
  175. public FetchResult call() throws GitAPIException, InvalidRemoteException,
  176. org.eclipse.jgit.api.errors.TransportException {
  177. checkCallable();
  178. try (Transport transport = Transport.open(repo, remote)) {
  179. transport.setCheckFetchedObjects(checkFetchedObjects);
  180. transport.setRemoveDeletedRefs(isRemoveDeletedRefs());
  181. transport.setDryRun(dryRun);
  182. if (tagOption != null)
  183. transport.setTagOpt(tagOption);
  184. transport.setFetchThin(thin);
  185. configure(transport);
  186. FetchResult result = transport.fetch(monitor,
  187. applyOptions(refSpecs), initialBranch);
  188. if (!repo.isBare()) {
  189. fetchSubmodules(result);
  190. }
  191. return result;
  192. } catch (NoRemoteRepositoryException e) {
  193. throw new InvalidRemoteException(MessageFormat.format(
  194. JGitText.get().invalidRemote, remote), e);
  195. } catch (TransportException e) {
  196. throw new org.eclipse.jgit.api.errors.TransportException(
  197. e.getMessage(), e);
  198. } catch (URISyntaxException e) {
  199. throw new InvalidRemoteException(MessageFormat.format(
  200. JGitText.get().invalidRemote, remote), e);
  201. } catch (NotSupportedException e) {
  202. throw new JGitInternalException(
  203. JGitText.get().exceptionCaughtDuringExecutionOfFetchCommand,
  204. e);
  205. }
  206. }
  207. private List<RefSpec> applyOptions(List<RefSpec> refSpecs2) {
  208. if (!isForceUpdate()) {
  209. return refSpecs2;
  210. }
  211. List<RefSpec> updated = new ArrayList<>(3);
  212. for (RefSpec refSpec : refSpecs2) {
  213. updated.add(refSpec.setForceUpdate(true));
  214. }
  215. return updated;
  216. }
  217. /**
  218. * Set the mode to be used for recursing into submodules.
  219. *
  220. * @param recurse
  221. * corresponds to the
  222. * --recurse-submodules/--no-recurse-submodules options. If
  223. * {@code null} use the value of the
  224. * {@code submodule.name.fetchRecurseSubmodules} option
  225. * configured per submodule. If not specified there, use the
  226. * value of the {@code fetch.recurseSubmodules} option configured
  227. * in git config. If not configured in either, "on-demand" is the
  228. * built-in default.
  229. * @return {@code this}
  230. * @since 4.7
  231. */
  232. public FetchCommand setRecurseSubmodules(
  233. @Nullable FetchRecurseSubmodulesMode recurse) {
  234. checkCallable();
  235. submoduleRecurseMode = recurse;
  236. return this;
  237. }
  238. /**
  239. * The remote (uri or name) used for the fetch operation. If no remote is
  240. * set, the default value of <code>Constants.DEFAULT_REMOTE_NAME</code> will
  241. * be used.
  242. *
  243. * @see Constants#DEFAULT_REMOTE_NAME
  244. * @param remote
  245. * name of a remote
  246. * @return {@code this}
  247. */
  248. public FetchCommand setRemote(String remote) {
  249. checkCallable();
  250. this.remote = remote;
  251. return this;
  252. }
  253. /**
  254. * Get the remote
  255. *
  256. * @return the remote used for the remote operation
  257. */
  258. public String getRemote() {
  259. return remote;
  260. }
  261. /**
  262. * Get timeout
  263. *
  264. * @return the timeout used for the fetch operation
  265. */
  266. public int getTimeout() {
  267. return timeout;
  268. }
  269. /**
  270. * Whether to check received objects for validity
  271. *
  272. * @return whether to check received objects for validity
  273. */
  274. public boolean isCheckFetchedObjects() {
  275. return checkFetchedObjects;
  276. }
  277. /**
  278. * If set to {@code true}, objects received will be checked for validity
  279. *
  280. * @param checkFetchedObjects
  281. * whether to check objects for validity
  282. * @return {@code this}
  283. */
  284. public FetchCommand setCheckFetchedObjects(boolean checkFetchedObjects) {
  285. checkCallable();
  286. this.checkFetchedObjects = checkFetchedObjects;
  287. return this;
  288. }
  289. /**
  290. * Whether to remove refs which no longer exist in the source
  291. *
  292. * @return whether to remove refs which no longer exist in the source
  293. */
  294. public boolean isRemoveDeletedRefs() {
  295. if (removeDeletedRefs != null) {
  296. return removeDeletedRefs.booleanValue();
  297. }
  298. // fall back to configuration
  299. boolean result = false;
  300. StoredConfig config = repo.getConfig();
  301. result = config.getBoolean(ConfigConstants.CONFIG_FETCH_SECTION, null,
  302. ConfigConstants.CONFIG_KEY_PRUNE, result);
  303. result = config.getBoolean(ConfigConstants.CONFIG_REMOTE_SECTION,
  304. remote, ConfigConstants.CONFIG_KEY_PRUNE, result);
  305. return result;
  306. }
  307. /**
  308. * If set to {@code true}, refs are removed which no longer exist in the
  309. * source
  310. *
  311. * @param removeDeletedRefs
  312. * whether to remove deleted {@code Ref}s
  313. * @return {@code this}
  314. */
  315. public FetchCommand setRemoveDeletedRefs(boolean removeDeletedRefs) {
  316. checkCallable();
  317. this.removeDeletedRefs = Boolean.valueOf(removeDeletedRefs);
  318. return this;
  319. }
  320. /**
  321. * Get progress monitor
  322. *
  323. * @return the progress monitor for the fetch operation
  324. */
  325. public ProgressMonitor getProgressMonitor() {
  326. return monitor;
  327. }
  328. /**
  329. * The progress monitor associated with the fetch operation. By default,
  330. * this is set to <code>NullProgressMonitor</code>
  331. *
  332. * @see NullProgressMonitor
  333. * @param monitor
  334. * a {@link org.eclipse.jgit.lib.ProgressMonitor}
  335. * @return {@code this}
  336. */
  337. public FetchCommand setProgressMonitor(ProgressMonitor monitor) {
  338. checkCallable();
  339. if (monitor == null) {
  340. monitor = NullProgressMonitor.INSTANCE;
  341. }
  342. this.monitor = monitor;
  343. return this;
  344. }
  345. /**
  346. * Get list of {@code RefSpec}s
  347. *
  348. * @return the ref specs
  349. */
  350. public List<RefSpec> getRefSpecs() {
  351. return refSpecs;
  352. }
  353. /**
  354. * The ref specs to be used in the fetch operation
  355. *
  356. * @param specs
  357. * String representation of {@code RefSpec}s
  358. * @return {@code this}
  359. * @since 4.9
  360. */
  361. public FetchCommand setRefSpecs(String... specs) {
  362. return setRefSpecs(
  363. Arrays.stream(specs).map(RefSpec::new).collect(toList()));
  364. }
  365. /**
  366. * The ref specs to be used in the fetch operation
  367. *
  368. * @param specs
  369. * one or multiple {@link org.eclipse.jgit.transport.RefSpec}s
  370. * @return {@code this}
  371. */
  372. public FetchCommand setRefSpecs(RefSpec... specs) {
  373. return setRefSpecs(Arrays.asList(specs));
  374. }
  375. /**
  376. * The ref specs to be used in the fetch operation
  377. *
  378. * @param specs
  379. * list of {@link org.eclipse.jgit.transport.RefSpec}s
  380. * @return {@code this}
  381. */
  382. public FetchCommand setRefSpecs(List<RefSpec> specs) {
  383. checkCallable();
  384. this.refSpecs.clear();
  385. this.refSpecs.addAll(specs);
  386. return this;
  387. }
  388. /**
  389. * Whether to do a dry run
  390. *
  391. * @return the dry run preference for the fetch operation
  392. */
  393. public boolean isDryRun() {
  394. return dryRun;
  395. }
  396. /**
  397. * Sets whether the fetch operation should be a dry run
  398. *
  399. * @param dryRun
  400. * whether to do a dry run
  401. * @return {@code this}
  402. */
  403. public FetchCommand setDryRun(boolean dryRun) {
  404. checkCallable();
  405. this.dryRun = dryRun;
  406. return this;
  407. }
  408. /**
  409. * Get thin-pack preference
  410. *
  411. * @return the thin-pack preference for fetch operation
  412. */
  413. public boolean isThin() {
  414. return thin;
  415. }
  416. /**
  417. * Sets the thin-pack preference for fetch operation.
  418. *
  419. * Default setting is Transport.DEFAULT_FETCH_THIN
  420. *
  421. * @param thin
  422. * the thin-pack preference
  423. * @return {@code this}
  424. */
  425. public FetchCommand setThin(boolean thin) {
  426. checkCallable();
  427. this.thin = thin;
  428. return this;
  429. }
  430. /**
  431. * Sets the specification of annotated tag behavior during fetch
  432. *
  433. * @param tagOpt
  434. * the {@link org.eclipse.jgit.transport.TagOpt}
  435. * @return {@code this}
  436. */
  437. public FetchCommand setTagOpt(TagOpt tagOpt) {
  438. checkCallable();
  439. this.tagOption = tagOpt;
  440. return this;
  441. }
  442. /**
  443. * Set the initial branch
  444. *
  445. * @param branch
  446. * the initial branch to check out when cloning the repository.
  447. * Can be specified as ref name (<code>refs/heads/master</code>),
  448. * branch name (<code>master</code>) or tag name
  449. * (<code>v1.2.3</code>). The default is to use the branch
  450. * pointed to by the cloned repository's HEAD and can be
  451. * requested by passing {@code null} or <code>HEAD</code>.
  452. * @return {@code this}
  453. * @since 5.11
  454. */
  455. public FetchCommand setInitialBranch(String branch) {
  456. this.initialBranch = branch;
  457. return this;
  458. }
  459. /**
  460. * Register a progress callback.
  461. *
  462. * @param callback
  463. * the callback
  464. * @return {@code this}
  465. * @since 4.8
  466. */
  467. public FetchCommand setCallback(Callback callback) {
  468. this.callback = callback;
  469. return this;
  470. }
  471. /**
  472. * Whether fetch --force option is enabled
  473. *
  474. * @return whether refs affected by the fetch are updated forcefully
  475. * @since 5.0
  476. */
  477. public boolean isForceUpdate() {
  478. return this.isForceUpdate;
  479. }
  480. /**
  481. * Set fetch --force option
  482. *
  483. * @param force
  484. * whether to update refs affected by the fetch forcefully
  485. * @return this command
  486. * @since 5.0
  487. */
  488. public FetchCommand setForceUpdate(boolean force) {
  489. this.isForceUpdate = force;
  490. return this;
  491. }
  492. }