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

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