123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- /*
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- package org.eclipse.jgit.api;
-
- import static java.util.stream.Collectors.toList;
-
- import java.io.IOException;
- import java.net.URISyntaxException;
- import java.text.MessageFormat;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.List;
-
- import org.eclipse.jgit.annotations.Nullable;
- import org.eclipse.jgit.api.errors.GitAPIException;
- import org.eclipse.jgit.api.errors.InvalidConfigurationException;
- import org.eclipse.jgit.api.errors.InvalidRemoteException;
- import org.eclipse.jgit.api.errors.JGitInternalException;
- import org.eclipse.jgit.errors.ConfigInvalidException;
- import org.eclipse.jgit.errors.NoRemoteRepositoryException;
- import org.eclipse.jgit.errors.NotSupportedException;
- import org.eclipse.jgit.errors.TransportException;
- import org.eclipse.jgit.internal.JGitText;
- import org.eclipse.jgit.lib.ConfigConstants;
- import org.eclipse.jgit.lib.Constants;
- import org.eclipse.jgit.lib.NullProgressMonitor;
- import org.eclipse.jgit.lib.ObjectId;
- import org.eclipse.jgit.lib.ProgressMonitor;
- import org.eclipse.jgit.lib.Repository;
- import org.eclipse.jgit.lib.StoredConfig;
- import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
- import org.eclipse.jgit.revwalk.RevWalk;
- import org.eclipse.jgit.submodule.SubmoduleWalk;
- import org.eclipse.jgit.transport.FetchResult;
- import org.eclipse.jgit.transport.RefSpec;
- import org.eclipse.jgit.transport.TagOpt;
- import org.eclipse.jgit.transport.Transport;
-
- /**
- * A class used to execute a {@code Fetch} command. It has setters for all
- * supported options and arguments of this command and a {@link #call()} method
- * to finally execute the command.
- *
- * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-fetch.html"
- * >Git documentation about Fetch</a>
- */
- public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
- private String remote = Constants.DEFAULT_REMOTE_NAME;
-
- private List<RefSpec> refSpecs;
-
- private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
-
- private boolean checkFetchedObjects;
-
- private Boolean removeDeletedRefs;
-
- private boolean dryRun;
-
- private boolean thin = Transport.DEFAULT_FETCH_THIN;
-
- private TagOpt tagOption;
-
- private FetchRecurseSubmodulesMode submoduleRecurseMode = null;
-
- private Callback callback;
-
- private boolean isForceUpdate;
-
- private String initialBranch;
-
- /**
- * Callback for status of fetch operation.
- *
- * @since 4.8
- *
- */
- public interface Callback {
- /**
- * Notify fetching a submodule.
- *
- * @param name
- * the submodule name.
- */
- void fetchingSubmodule(String name);
- }
-
- /**
- * Constructor for FetchCommand.
- *
- * @param repo
- * a {@link org.eclipse.jgit.lib.Repository} object.
- */
- protected FetchCommand(Repository repo) {
- super(repo);
- refSpecs = new ArrayList<>(3);
- }
-
- private FetchRecurseSubmodulesMode getRecurseMode(String path) {
- // Use the caller-specified mode, if set
- if (submoduleRecurseMode != null) {
- return submoduleRecurseMode;
- }
-
- // Fall back to submodule.name.fetchRecurseSubmodules, if set
- FetchRecurseSubmodulesMode mode = repo.getConfig().getEnum(
- FetchRecurseSubmodulesMode.values(),
- ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES, null);
- if (mode != null) {
- return mode;
- }
-
- // Fall back to fetch.recurseSubmodules, if set
- mode = repo.getConfig().getEnum(FetchRecurseSubmodulesMode.values(),
- ConfigConstants.CONFIG_FETCH_SECTION, null,
- ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES, null);
- if (mode != null) {
- return mode;
- }
-
- // Default to on-demand mode
- return FetchRecurseSubmodulesMode.ON_DEMAND;
- }
-
- private void fetchSubmodules(FetchResult results)
- throws org.eclipse.jgit.api.errors.TransportException,
- GitAPIException, InvalidConfigurationException {
- try (SubmoduleWalk walk = new SubmoduleWalk(repo);
- RevWalk revWalk = new RevWalk(repo)) {
- // Walk over submodules in the parent repository's FETCH_HEAD.
- ObjectId fetchHead = repo.resolve(Constants.FETCH_HEAD);
- if (fetchHead == null) {
- return;
- }
- walk.setTree(revWalk.parseTree(fetchHead));
- while (walk.next()) {
- try (Repository submoduleRepo = walk.getRepository()) {
-
- // Skip submodules that don't exist locally (have not been
- // cloned), are not registered in the .gitmodules file, or
- // not registered in the parent repository's config.
- if (submoduleRepo == null || walk.getModulesPath() == null
- || walk.getConfigUrl() == null) {
- continue;
- }
-
- FetchRecurseSubmodulesMode recurseMode = getRecurseMode(
- walk.getPath());
-
- // When the fetch mode is "yes" we always fetch. When the
- // mode
- // is "on demand", we only fetch if the submodule's revision
- // was
- // updated to an object that is not currently present in the
- // submodule.
- if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND
- && !submoduleRepo.getObjectDatabase()
- .has(walk.getObjectId()))
- || recurseMode == FetchRecurseSubmodulesMode.YES) {
- FetchCommand f = new FetchCommand(submoduleRepo)
- .setProgressMonitor(monitor)
- .setTagOpt(tagOption)
- .setCheckFetchedObjects(checkFetchedObjects)
- .setRemoveDeletedRefs(isRemoveDeletedRefs())
- .setThin(thin)
- .setRefSpecs(applyOptions(refSpecs))
- .setDryRun(dryRun)
- .setRecurseSubmodules(recurseMode);
- configure(f);
- if (callback != null) {
- callback.fetchingSubmodule(walk.getPath());
- }
- results.addSubmodule(walk.getPath(), f.call());
- }
- }
- }
- } catch (IOException e) {
- throw new JGitInternalException(e.getMessage(), e);
- } catch (ConfigInvalidException e) {
- throw new InvalidConfigurationException(e.getMessage(), e);
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Execute the {@code fetch} command with all the options and parameters
- * collected by the setter methods of this class. Each instance of this
- * class should only be used for one invocation of the command (means: one
- * call to {@link #call()})
- */
- @Override
- public FetchResult call() throws GitAPIException, InvalidRemoteException,
- org.eclipse.jgit.api.errors.TransportException {
- checkCallable();
-
- try (Transport transport = Transport.open(repo, remote)) {
- transport.setCheckFetchedObjects(checkFetchedObjects);
- transport.setRemoveDeletedRefs(isRemoveDeletedRefs());
- transport.setDryRun(dryRun);
- if (tagOption != null)
- transport.setTagOpt(tagOption);
- transport.setFetchThin(thin);
- configure(transport);
- FetchResult result = transport.fetch(monitor,
- applyOptions(refSpecs), initialBranch);
- if (!repo.isBare()) {
- fetchSubmodules(result);
- }
-
- return result;
- } catch (NoRemoteRepositoryException e) {
- throw new InvalidRemoteException(MessageFormat.format(
- JGitText.get().invalidRemote, remote), e);
- } catch (TransportException e) {
- throw new org.eclipse.jgit.api.errors.TransportException(
- e.getMessage(), e);
- } catch (URISyntaxException e) {
- throw new InvalidRemoteException(MessageFormat.format(
- JGitText.get().invalidRemote, remote), e);
- } catch (NotSupportedException e) {
- throw new JGitInternalException(
- JGitText.get().exceptionCaughtDuringExecutionOfFetchCommand,
- e);
- }
-
- }
-
- private List<RefSpec> applyOptions(List<RefSpec> refSpecs2) {
- if (!isForceUpdate()) {
- return refSpecs2;
- }
- List<RefSpec> updated = new ArrayList<>(3);
- for (RefSpec refSpec : refSpecs2) {
- updated.add(refSpec.setForceUpdate(true));
- }
- return updated;
- }
-
- /**
- * Set the mode to be used for recursing into submodules.
- *
- * @param recurse
- * corresponds to the
- * --recurse-submodules/--no-recurse-submodules options. If
- * {@code null} use the value of the
- * {@code submodule.name.fetchRecurseSubmodules} option
- * configured per submodule. If not specified there, use the
- * value of the {@code fetch.recurseSubmodules} option configured
- * in git config. If not configured in either, "on-demand" is the
- * built-in default.
- * @return {@code this}
- * @since 4.7
- */
- public FetchCommand setRecurseSubmodules(
- @Nullable FetchRecurseSubmodulesMode recurse) {
- checkCallable();
- submoduleRecurseMode = recurse;
- return this;
- }
-
- /**
- * The remote (uri or name) used for the fetch operation. If no remote is
- * set, the default value of <code>Constants.DEFAULT_REMOTE_NAME</code> will
- * be used.
- *
- * @see Constants#DEFAULT_REMOTE_NAME
- * @param remote
- * name of a remote
- * @return {@code this}
- */
- public FetchCommand setRemote(String remote) {
- checkCallable();
- this.remote = remote;
- return this;
- }
-
- /**
- * Get the remote
- *
- * @return the remote used for the remote operation
- */
- public String getRemote() {
- return remote;
- }
-
- /**
- * Get timeout
- *
- * @return the timeout used for the fetch operation
- */
- public int getTimeout() {
- return timeout;
- }
-
- /**
- * Whether to check received objects for validity
- *
- * @return whether to check received objects for validity
- */
- public boolean isCheckFetchedObjects() {
- return checkFetchedObjects;
- }
-
- /**
- * If set to {@code true}, objects received will be checked for validity
- *
- * @param checkFetchedObjects
- * whether to check objects for validity
- * @return {@code this}
- */
- public FetchCommand setCheckFetchedObjects(boolean checkFetchedObjects) {
- checkCallable();
- this.checkFetchedObjects = checkFetchedObjects;
- return this;
- }
-
- /**
- * Whether to remove refs which no longer exist in the source
- *
- * @return whether to remove refs which no longer exist in the source
- */
- public boolean isRemoveDeletedRefs() {
- if (removeDeletedRefs != null) {
- return removeDeletedRefs.booleanValue();
- }
- // fall back to configuration
- boolean result = false;
- StoredConfig config = repo.getConfig();
- result = config.getBoolean(ConfigConstants.CONFIG_FETCH_SECTION, null,
- ConfigConstants.CONFIG_KEY_PRUNE, result);
- result = config.getBoolean(ConfigConstants.CONFIG_REMOTE_SECTION,
- remote, ConfigConstants.CONFIG_KEY_PRUNE, result);
- return result;
- }
-
- /**
- * If set to {@code true}, refs are removed which no longer exist in the
- * source
- *
- * @param removeDeletedRefs
- * whether to remove deleted {@code Ref}s
- * @return {@code this}
- */
- public FetchCommand setRemoveDeletedRefs(boolean removeDeletedRefs) {
- checkCallable();
- this.removeDeletedRefs = Boolean.valueOf(removeDeletedRefs);
- return this;
- }
-
- /**
- * Get progress monitor
- *
- * @return the progress monitor for the fetch operation
- */
- public ProgressMonitor getProgressMonitor() {
- return monitor;
- }
-
- /**
- * The progress monitor associated with the fetch operation. By default,
- * this is set to <code>NullProgressMonitor</code>
- *
- * @see NullProgressMonitor
- * @param monitor
- * a {@link org.eclipse.jgit.lib.ProgressMonitor}
- * @return {@code this}
- */
- public FetchCommand setProgressMonitor(ProgressMonitor monitor) {
- checkCallable();
- if (monitor == null) {
- monitor = NullProgressMonitor.INSTANCE;
- }
- this.monitor = monitor;
- return this;
- }
-
- /**
- * Get list of {@code RefSpec}s
- *
- * @return the ref specs
- */
- public List<RefSpec> getRefSpecs() {
- return refSpecs;
- }
-
- /**
- * The ref specs to be used in the fetch operation
- *
- * @param specs
- * String representation of {@code RefSpec}s
- * @return {@code this}
- * @since 4.9
- */
- public FetchCommand setRefSpecs(String... specs) {
- return setRefSpecs(
- Arrays.stream(specs).map(RefSpec::new).collect(toList()));
- }
-
- /**
- * The ref specs to be used in the fetch operation
- *
- * @param specs
- * one or multiple {@link org.eclipse.jgit.transport.RefSpec}s
- * @return {@code this}
- */
- public FetchCommand setRefSpecs(RefSpec... specs) {
- return setRefSpecs(Arrays.asList(specs));
- }
-
- /**
- * The ref specs to be used in the fetch operation
- *
- * @param specs
- * list of {@link org.eclipse.jgit.transport.RefSpec}s
- * @return {@code this}
- */
- public FetchCommand setRefSpecs(List<RefSpec> specs) {
- checkCallable();
- this.refSpecs.clear();
- this.refSpecs.addAll(specs);
- return this;
- }
-
- /**
- * Whether to do a dry run
- *
- * @return the dry run preference for the fetch operation
- */
- public boolean isDryRun() {
- return dryRun;
- }
-
- /**
- * Sets whether the fetch operation should be a dry run
- *
- * @param dryRun
- * whether to do a dry run
- * @return {@code this}
- */
- public FetchCommand setDryRun(boolean dryRun) {
- checkCallable();
- this.dryRun = dryRun;
- return this;
- }
-
- /**
- * Get thin-pack preference
- *
- * @return the thin-pack preference for fetch operation
- */
- public boolean isThin() {
- return thin;
- }
-
- /**
- * Sets the thin-pack preference for fetch operation.
- *
- * Default setting is Transport.DEFAULT_FETCH_THIN
- *
- * @param thin
- * the thin-pack preference
- * @return {@code this}
- */
- public FetchCommand setThin(boolean thin) {
- checkCallable();
- this.thin = thin;
- return this;
- }
-
- /**
- * Sets the specification of annotated tag behavior during fetch
- *
- * @param tagOpt
- * the {@link org.eclipse.jgit.transport.TagOpt}
- * @return {@code this}
- */
- public FetchCommand setTagOpt(TagOpt tagOpt) {
- checkCallable();
- this.tagOption = tagOpt;
- return this;
- }
-
- /**
- * Set the initial branch
- *
- * @param branch
- * the initial branch to check out when cloning the repository.
- * Can be specified as ref name (<code>refs/heads/master</code>),
- * branch name (<code>master</code>) or tag name
- * (<code>v1.2.3</code>). The default is to use the branch
- * pointed to by the cloned repository's HEAD and can be
- * requested by passing {@code null} or <code>HEAD</code>.
- * @return {@code this}
- * @since 5.11
- */
- public FetchCommand setInitialBranch(String branch) {
- this.initialBranch = branch;
- return this;
- }
-
- /**
- * Register a progress callback.
- *
- * @param callback
- * the callback
- * @return {@code this}
- * @since 4.8
- */
- public FetchCommand setCallback(Callback callback) {
- this.callback = callback;
- return this;
- }
-
- /**
- * Whether fetch --force option is enabled
- *
- * @return whether refs affected by the fetch are updated forcefully
- * @since 5.0
- */
- public boolean isForceUpdate() {
- return this.isForceUpdate;
- }
-
- /**
- * Set fetch --force option
- *
- * @param force
- * whether to update refs affected by the fetch forcefully
- * @return this command
- * @since 5.0
- */
- public FetchCommand setForceUpdate(boolean force) {
- this.isForceUpdate = force;
- return this;
- }
- }
|