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

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