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 14KB

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