diff options
Diffstat (limited to 'org.eclipse.jgit')
21 files changed, 682 insertions, 85 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index 6eb8bd3732..8aa84f3ac1 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -13,7 +13,13 @@ <filter id="336695337"> <message_arguments> <message_argument value="org.eclipse.jgit.lib.ObjectDatabase"/> - <message_argument value="getApproximateObjectCount()"/> + <message_argument value="getShallowCommits()"/> + </message_arguments> + </filter> + <filter id="336695337"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lib.ObjectDatabase"/> + <message_argument value="setShallowCommits(Set<ObjectId>)"/> </message_arguments> </filter> </resource> @@ -60,14 +66,6 @@ </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/transport/BasePackPushConnection.java" type="org.eclipse.jgit.transport.BasePackPushConnection"> - <filter id="338792546"> - <message_arguments> - <message_argument value="org.eclipse.jgit.transport.BasePackPushConnection"/> - <message_argument value="noRepository()"/> - </message_arguments> - </filter> - </resource> <resource path="src/org/eclipse/jgit/transport/PushConfig.java" type="org.eclipse.jgit.transport.PushConfig"> <filter id="338722907"> <message_arguments> diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 66adad5151..84a7a80d62 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -237,6 +237,8 @@ deletedOrphanInPackDir=Deleted orphaned file {} deleteRequiresZeroNewId=Delete requires new ID to be zero deleteTagUnexpectedResult=Delete tag returned unexpected result {0} deletingNotSupported=Deleting {0} not supported. +depthMustBeAt1=Depth must be >= 1 +depthWithUnshallow=Depth and unshallow can\'t be used together destinationIsNotAWildcard=Destination is not a wildcard. detachedHeadDetected=HEAD is detached diffToolNotGivenError=No diff tool provided and no defaults configured. @@ -518,6 +520,7 @@ notFound=not found. nothingToFetch=Nothing to fetch. nothingToPush=Nothing to push. notMergedExceptionMessage=Branch was not deleted as it has not been merged yet; use the force option to delete it anyway +notShallowedUnshallow=The server sent a unshallow for a commit that wasn''t marked as shallow: {0} noXMLParserAvailable=No XML parser available. objectAtHasBadZlibStream=Object at {0} in {1} has bad zlib stream objectIsCorrupt=Object {0} is corrupt: {1} @@ -662,6 +665,7 @@ serviceNotEnabledNoName=Service not enabled serviceNotPermitted={1} not permitted on ''{0}'' sha1CollisionDetected=SHA-1 collision detected on {0} shallowCommitsAlreadyInitialized=Shallow commits have already been initialized +shallowNotSupported=The server does not support shallow shallowPacksRequireDepthWalk=Shallow packs require a DepthWalk shortCompressedStreamAt=Short compressed stream at {0} shortReadOfBlock=Short read of block. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java index 3aa711455b..1f979a938c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2017 Chris Aniszczyk <caniszczyk@gmail.com> and others + * Copyright (C) 2011, 2022 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 @@ -13,10 +13,13 @@ import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.text.MessageFormat; +import java.time.Instant; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRemoteException; @@ -91,6 +94,12 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { private TagOpt tagOption; + private Integer depth; + + private Instant shallowSince; + + private List<String> shallowExcludes = new ArrayList<>(); + private enum FETCH_TYPE { MULTIPLE_BRANCHES, ALL_BRANCHES, MIRROR } @@ -306,6 +315,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW); } command.setInitialBranch(branch); + if (depth != null) { + command.setDepth(depth.intValue()); + } + command.setShallowSince(shallowSince); + command.setShallowExcludes(shallowExcludes); configure(command); return command.call(); @@ -737,6 +751,82 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { return this; } + /** + * Creates a shallow clone with a history truncated to the specified number + * of commits. + * + * @param depth + * the depth + * @return {@code this} + * + * @since 6.3 + */ + public CloneCommand setDepth(int depth) { + if (depth < 1) { + throw new IllegalArgumentException(JGitText.get().depthMustBeAt1); + } + this.depth = Integer.valueOf(depth); + return this; + } + + /** + * Creates a shallow clone with a history after the specified time. + * + * @param shallowSince + * the timestammp; must not be {@code null} + * @return {@code this} + * + * @since 6.3 + */ + public CloneCommand setShallowSince(@NonNull OffsetDateTime shallowSince) { + this.shallowSince = shallowSince.toInstant(); + return this; + } + + /** + * Creates a shallow clone with a history after the specified time. + * + * @param shallowSince + * the timestammp; must not be {@code null} + * @return {@code this} + * + * @since 6.3 + */ + public CloneCommand setShallowSince(@NonNull Instant shallowSince) { + this.shallowSince = shallowSince; + return this; + } + + /** + * Creates a shallow clone with a history, excluding commits reachable from + * a specified remote branch or tag. + * + * @param shallowExclude + * the ref or commit; must not be {@code null} + * @return {@code this} + * + * @since 6.3 + */ + public CloneCommand addShallowExclude(@NonNull String shallowExclude) { + shallowExcludes.add(shallowExclude); + return this; + } + + /** + * Creates a shallow clone with a history, excluding commits reachable from + * a specified remote branch or tag. + * + * @param shallowExclude + * the commit; must not be {@code null} + * @return {@code this} + * + * @since 6.3 + */ + public CloneCommand addShallowExclude(@NonNull ObjectId shallowExclude) { + shallowExcludes.add(shallowExclude.name()); + return this; + } + private static void validateDirs(File directory, File gitDir, boolean bare) throws IllegalStateException { if (directory != null) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java index 90c1515b06..84bee36204 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others + * Copyright (C) 2010, 2022 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 @@ -14,10 +14,13 @@ import static java.util.stream.Collectors.toList; import java.io.IOException; import java.net.URISyntaxException; import java.text.MessageFormat; +import java.time.Instant; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidConfigurationException; @@ -76,6 +79,14 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> { private String initialBranch; + private Integer depth; + + private Instant deepenSince; + + private List<String> shallowExcludes = new ArrayList<>(); + + private boolean unshallow; + /** * Callback for status of fetch operation. * @@ -156,11 +167,9 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> { 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. + // 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())) @@ -209,6 +218,17 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> { if (tagOption != null) transport.setTagOpt(tagOption); transport.setFetchThin(thin); + if (depth != null) { + transport.setDepth(depth); + } + if (unshallow) { + if (depth != null) { + throw new IllegalStateException(JGitText.get().depthWithUnshallow); + } + transport.setDepth(Constants.INFINITE_DEPTH); + } + transport.setDeepenSince(deepenSince); + transport.setDeepenNots(shallowExcludes); configure(transport); FetchResult result = transport.fetch(monitor, applyOptions(refSpecs), initialBranch); @@ -542,4 +562,105 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> { this.isForceUpdate = force; return this; } + + /** + * Limits fetching to the specified number of commits from the tip of each + * remote branch history. + * + * @param depth + * the depth + * @return {@code this} + * + * @since 6.3 + */ + public FetchCommand setDepth(int depth) { + if (depth < 1) { + throw new IllegalArgumentException(JGitText.get().depthMustBeAt1); + } + this.depth = Integer.valueOf(depth); + return this; + } + + /** + * Deepens or shortens the history of a shallow repository to include all + * reachable commits after a specified time. + * + * @param shallowSince + * the timestammp; must not be {@code null} + * @return {@code this} + * + * @since 6.3 + */ + public FetchCommand setShallowSince(@NonNull OffsetDateTime shallowSince) { + this.deepenSince = shallowSince.toInstant(); + return this; + } + + /** + * Deepens or shortens the history of a shallow repository to include all + * reachable commits after a specified time. + * + * @param shallowSince + * the timestammp; must not be {@code null} + * @return {@code this} + * + * @since 6.3 + */ + public FetchCommand setShallowSince(@NonNull Instant shallowSince) { + this.deepenSince = shallowSince; + return this; + } + + /** + * Deepens or shortens the history of a shallow repository to exclude + * commits reachable from a specified remote branch or tag. + * + * @param shallowExclude + * the ref or commit; must not be {@code null} + * @return {@code this} + * + * @since 6.3 + */ + public FetchCommand addShallowExclude(@NonNull String shallowExclude) { + shallowExcludes.add(shallowExclude); + return this; + } + + /** + * Creates a shallow clone with a history, excluding commits reachable from + * a specified remote branch or tag. + * + * @param shallowExclude + * the commit; must not be {@code null} + * @return {@code this} + * + * @since 6.3 + */ + public FetchCommand addShallowExclude(@NonNull ObjectId shallowExclude) { + shallowExcludes.add(shallowExclude.name()); + return this; + } + + /** + * If the source repository is complete, converts a shallow repository to a + * complete one, removing all the limitations imposed by shallow + * repositories. + * + * If the source repository is shallow, fetches as much as possible so that + * the current repository has the same history as the source repository. + * + * @param unshallow + * whether to unshallow or not + * @return {@code this} + * + * @since 6.3 + */ + public FetchCommand setUnshallow(boolean unshallow) { + this.unshallow = unshallow; + return this; + } + + void setShallowExcludes(List<String> shallowExcludes) { + this.shallowExcludes = shallowExcludes; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index efdb8e42e3..551a5a8a9d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -265,6 +265,8 @@ public class JGitText extends TranslationBundle { /***/ public String deleteRequiresZeroNewId; /***/ public String deleteTagUnexpectedResult; /***/ public String deletingNotSupported; + /***/ public String depthMustBeAt1; + /***/ public String depthWithUnshallow; /***/ public String destinationIsNotAWildcard; /***/ public String detachedHeadDetected; /***/ public String diffToolNotGivenError; @@ -546,6 +548,7 @@ public class JGitText extends TranslationBundle { /***/ public String nothingToFetch; /***/ public String nothingToPush; /***/ public String notMergedExceptionMessage; + /***/ public String notShallowedUnshallow; /***/ public String noXMLParserAvailable; /***/ public String objectAtHasBadZlibStream; /***/ public String objectIsCorrupt; @@ -690,6 +693,7 @@ public class JGitText extends TranslationBundle { /***/ public String serviceNotPermitted; /***/ public String sha1CollisionDetected; /***/ public String shallowCommitsAlreadyInitialized; + /***/ public String shallowNotSupported; /***/ public String shallowPacksRequireDepthWalk; /***/ public String shortCompressedStreamAt; /***/ public String shortReadOfBlock; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java index 99da222395..5a8207ed01 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java @@ -1,3 +1,12 @@ +/* + * Copyright (C) 2011, 2022 Google Inc. 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.internal.storage.dfs; import java.io.ByteArrayOutputStream; @@ -6,13 +15,16 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; import org.eclipse.jgit.internal.storage.pack.PackExt; import org.eclipse.jgit.internal.storage.reftable.ReftableConfig; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.RefDatabase; /** @@ -98,6 +110,7 @@ public class InMemoryRepository extends DfsRepository { public static class MemObjDatabase extends DfsObjDatabase { private List<DfsPackDescription> packs = new ArrayList<>(); private int blockSize; + private Set<ObjectId> shallowCommits = Collections.emptySet(); MemObjDatabase(DfsRepository repo) { super(repo, new DfsReaderOptions()); @@ -167,6 +180,16 @@ public class InMemoryRepository extends DfsRepository { } @Override + public Set<ObjectId> getShallowCommits() throws IOException { + return shallowCommits; + } + + @Override + public void setShallowCommits(Set<ObjectId> shallowCommits) { + this.shallowCommits = shallowCommits; + } + + @Override public long getApproximateObjectCount() { long count = 0; for (DfsPackDescription p : packs) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java index 094fdc1559..9272bf3f59 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com> - * Copyright (C) 2010, JetBrains s.r.o. and others + * Copyright (C) 2010, 2022 JetBrains s.r.o. 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 @@ -117,10 +117,15 @@ class CachedObjectDirectory extends FileObjectDatabase { } @Override - Set<ObjectId> getShallowCommits() throws IOException { + public Set<ObjectId> getShallowCommits() throws IOException { return wrapped.getShallowCommits(); } + @Override + public void setShallowCommits(Set<ObjectId> shallowCommits) throws IOException { + wrapped.setShallowCommits(shallowCommits); + } + private CachedObjectDirectory[] myAlternates() { if (alts == null) { ObjectDirectory.AlternateHandle[] src = wrapped.myAlternates(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java index 01dd27d9fb..e97ed393a1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileObjectDatabase.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Google Inc. and others + * Copyright (C) 2010, 2022 Google Inc. 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 @@ -50,8 +50,6 @@ abstract class FileObjectDatabase extends ObjectDatabase { abstract FS getFS(); - abstract Set<ObjectId> getShallowCommits() throws IOException; - abstract void selectObjectRepresentation(PackWriter packer, ObjectToPack otp, WindowCursor curs) throws IOException; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java index 06c8cad3ac..1a1d31a632 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, Google Inc. and others + * Copyright (C) 2009, 2022 Google Inc. 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 @@ -19,6 +19,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.OutputStream; import java.nio.file.Files; import java.text.MessageFormat; import java.util.ArrayList; @@ -560,7 +561,7 @@ public class ObjectDirectory extends FileObjectDatabase { } @Override - Set<ObjectId> getShallowCommits() throws IOException { + public Set<ObjectId> getShallowCommits() throws IOException { if (shallowFile == null || !shallowFile.isFile()) return Collections.emptySet(); @@ -587,6 +588,43 @@ public class ObjectDirectory extends FileObjectDatabase { return shallowCommitsIds; } + @Override + public void setShallowCommits(Set<ObjectId> shallowCommits) throws IOException { + this.shallowCommitsIds = shallowCommits; + LockFile lock = new LockFile(shallowFile); + if (!lock.lock()) { + throw new IOException(MessageFormat.format(JGitText.get().lockError, + shallowFile.getAbsolutePath())); + } + + try { + if (shallowCommits.isEmpty()) { + if (shallowFile.isFile()) { + shallowFile.delete(); + } + } else { + try (OutputStream out = lock.getOutputStream()) { + for (ObjectId shallowCommit : shallowCommits) { + byte[] buf = new byte[Constants.OBJECT_ID_STRING_LENGTH + 1]; + shallowCommit.copyTo(buf, 0); + buf[Constants.OBJECT_ID_STRING_LENGTH] = '\n'; + out.write(buf); + } + } finally { + lock.commit(); + } + } + } finally { + lock.unlock(); + } + + if (shallowCommits.isEmpty()) { + shallowFileSnapshot = FileSnapshot.DIRTY; + } else { + shallowFileSnapshot = FileSnapshot.save(shallowFile); + } + } + void closeAllPackHandles(File packFile) { // if the packfile already exists (because we are rewriting a // packfile for the same set of objects maybe with different diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index cf2e69dbb5..30a0074195 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -1,7 +1,7 @@ /* * Copyright (C) 2008, Google Inc. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2006-2017, Shawn O. Pearce <spearce@spearce.org> and others + * Copyright (C) 2006, 2022, Shawn O. Pearce <spearce@spearce.org> 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 @@ -747,6 +747,13 @@ public final class Constants { */ public static final String LOCK_SUFFIX = ".lock"; //$NON-NLS-1$ + /** + * Depth used to unshallow a repository + * + * @since 6.3 + */ + public static final int INFINITE_DEPTH = 0x7fffffff; + private Constants() { // Hide the default constructor } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java index 70009cba35..1c0f436090 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, Google Inc. and others + * Copyright (C) 2009, 2022 Google Inc. 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 @@ -11,6 +11,7 @@ package org.eclipse.jgit.lib; import java.io.IOException; +import java.util.Set; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; @@ -72,6 +73,26 @@ public abstract class ObjectDatabase implements AutoCloseable { public abstract ObjectReader newReader(); /** + * @return the shallow commits of the current repository + * + * @throws IOException the database could not be read + * + * @since 6.3 + */ + public abstract Set<ObjectId> getShallowCommits() throws IOException; + + /** + * Update the shallow commits of the current repository + * + * @param shallowCommits the new shallow commits + * + * @throws IOException the database could not be updated + * + * @since 6.3 + */ + public abstract void setShallowCommits(Set<ObjectId> shallowCommits) throws IOException; + + /** * Close any resources held by this database. */ @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java index 3f167ccce2..2aecf63ada 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java @@ -16,12 +16,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.MessageFormat; +import java.time.Instant; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import org.eclipse.jgit.errors.PackProtocolException; @@ -32,6 +34,7 @@ import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ObjectDatabase; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ProgressMonitor; @@ -76,7 +79,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection /** * Maximum number of 'have' lines to send before giving up. * <p> - * During {@link #negotiate(ProgressMonitor)} we send at most this many + * During {@link #negotiate(ProgressMonitor, boolean, Set)} we send at most this many * commits to the remote peer as 'have' lines without an ACK response before * we give up. */ @@ -210,6 +213,12 @@ public abstract class BasePackFetchConnection extends BasePackConnection private int maxHaves; + private Integer depth; + + private Instant deepenSince; + + private List<String> deepenNots; + /** * RPC state, if {@link BasePackConnection#statelessRPC} is true or protocol * V2 is used. @@ -246,6 +255,9 @@ public abstract class BasePackFetchConnection extends BasePackConnection includeTags = transport.getTagOpt() != TagOpt.NO_TAGS; thinPack = transport.isFetchThin(); filterSpec = transport.getFilterSpec(); + depth = transport.getDepth(); + deepenSince = transport.getDeepenSince(); + deepenNots = transport.getDeepenNots(); if (local != null) { walk = new RevWalk(local); @@ -385,9 +397,17 @@ public abstract class BasePackFetchConnection extends BasePackConnection } PacketLineOut output = statelessRPC ? pckState : pckOut; if (sendWants(want, output)) { + boolean mayHaveShallow = depth != null || deepenSince != null || !deepenNots.isEmpty(); + Set<ObjectId> shallowCommits = local.getObjectDatabase().getShallowCommits(); + if (isCapableOf(GitProtocolConstants.CAPABILITY_SHALLOW)) { + sendShallow(shallowCommits, output); + } else if (mayHaveShallow) { + throw new PackProtocolException(JGitText.get().shallowNotSupported); + } output.end(); outNeedsEnd = false; - negotiate(monitor); + + negotiate(monitor, mayHaveShallow, shallowCommits); clearState(); @@ -424,10 +444,18 @@ public abstract class BasePackFetchConnection extends BasePackConnection for (String capability : getCapabilitiesV2(capabilities)) { pckState.writeString(capability); } + if (!sendWants(want, pckState)) { // We already have everything we wanted. return; } + + Set<ObjectId> shallowCommits = local.getObjectDatabase().getShallowCommits(); + if (capabilities.contains(GitProtocolConstants.CAPABILITY_SHALLOW)) { + sendShallow(shallowCommits, pckState); + } else if (depth != null || deepenSince != null || !deepenNots.isEmpty()) { + throw new PackProtocolException(JGitText.get().shallowNotSupported); + } // If we send something, we always close it properly ourselves. outNeedsEnd = false; @@ -458,7 +486,17 @@ public abstract class BasePackFetchConnection extends BasePackConnection if (sentDone && line.startsWith("ERR ")) { //$NON-NLS-1$ throw new RemoteRepositoryException(uri, line.substring(4)); } - // "shallow-info", "wanted-refs", and "packfile-uris" would have to be + + if (GitProtocolConstants.SECTION_SHALLOW_INFO.equals(line)) { + line = handleShallowUnshallow(shallowCommits, pckIn); + if (!PacketLineIn.isDelimiter(line)) { + throw new PackProtocolException(MessageFormat + .format(JGitText.get().expectedGot, "0001", line)); //$NON-NLS-1$ + } + line = pckIn.readString(); + } + + // "wanted-refs" and "packfile-uris" would have to be // handled here in that order. if (!GitProtocolConstants.SECTION_PACKFILE.equals(line)) { throw new PackProtocolException( @@ -672,16 +710,19 @@ public abstract class BasePackFetchConnection extends BasePackConnection if (objectId == null) { continue; } - try { - if (walk.parseAny(objectId).has(REACHABLE)) { - // We already have this object. Asking for it is - // not a very good idea. - // - continue; + // if depth is set we need to fetch the objects even if they are already available + if (transport.getDepth() == null) { + try { + if (walk.parseAny(objectId).has(REACHABLE)) { + // We already have this object. Asking for it is + // not a very good idea. + // + continue; + } + } catch (IOException err) { + // Its OK, we don't have it, but we want to fix that + // by fetching the object from the other side. } - } catch (IOException err) { - // Its OK, we don't have it, but we want to fix that - // by fetching the object from the other side. } final StringBuilder line = new StringBuilder(46); @@ -773,8 +814,8 @@ public abstract class BasePackFetchConnection extends BasePackConnection return line.toString(); } - private void negotiate(ProgressMonitor monitor) throws IOException, - CancelledException { + private void negotiate(ProgressMonitor monitor, boolean mayHaveShallow, Set<ObjectId> shallowCommits) + throws IOException, CancelledException { final MutableObjectId ackId = new MutableObjectId(); int resultsPending = 0; int havesSent = 0; @@ -911,6 +952,14 @@ public abstract class BasePackFetchConnection extends BasePackConnection resultsPending++; } + if (mayHaveShallow) { + String line = handleShallowUnshallow(shallowCommits, pckIn); + if (!PacketLineIn.isEnd(line)) { + throw new PackProtocolException(MessageFormat + .format(JGitText.get().expectedGot, "0000", line)); //$NON-NLS-1$ + } + } + READ_RESULT: while (resultsPending > 0 || multiAck != MultiAck.OFF) { final AckNackResult anr = pckIn.readACK(ackId); resultsPending--; @@ -1025,6 +1074,50 @@ public abstract class BasePackFetchConnection extends BasePackConnection } } + private void sendShallow(Set<ObjectId> shallowCommits, PacketLineOut output) throws IOException { + for (ObjectId shallowCommit : shallowCommits) { + output.writeString("shallow " + shallowCommit.name()); //$NON-NLS-1$ + } + + if (depth != null) { + output.writeString("deepen " + depth); //$NON-NLS-1$ + } + + if (deepenSince != null) { + output.writeString("deepen-since " + deepenSince.getEpochSecond()); //$NON-NLS-1$ + } + + if (deepenNots != null) { + for (String deepenNotRef : deepenNots) { + output.writeString("deepen-not " + deepenNotRef); //$NON-NLS-1$ + } + } + } + + private String handleShallowUnshallow(Set<ObjectId> advertisedShallowCommits, PacketLineIn input) + throws IOException { + String line = input.readString(); + ObjectDatabase objectDatabase = local.getObjectDatabase(); + HashSet<ObjectId> newShallowCommits = new HashSet<>(advertisedShallowCommits); + while (!PacketLineIn.isDelimiter(line) && !PacketLineIn.isEnd(line)) { + if (line.startsWith("shallow ")) { //$NON-NLS-1$ + newShallowCommits.add(ObjectId + .fromString(line.substring("shallow ".length()))); //$NON-NLS-1$ + } else if (line.startsWith("unshallow ")) { //$NON-NLS-1$ + ObjectId unshallow = ObjectId + .fromString(line.substring("unshallow ".length())); //$NON-NLS-1$ + if (!advertisedShallowCommits.contains(unshallow)) { + throw new PackProtocolException(MessageFormat.format(JGitText.get() + .notShallowedUnshallow, unshallow.name())); + } + newShallowCommits.remove(unshallow); + } + line = input.readString(); + } + objectDatabase.setShallowCommits(newShallowCommits); + return line; + } + /** * Notification event delivered just before the pack is received from the * network. This event can be used by RPC such as {@link org.eclipse.jgit.transport.TransportHttp} to diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java index 7bface49d9..28c3b6a0fa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others + * Copyright (C) 2008, 2022 Shawn O. Pearce <spearce@spearce.org> 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 @@ -393,7 +393,7 @@ class FetchProcess { ow.markUninteresting(ow.parseAny(ref.getObjectId())); ow.checkConnectivity(); } - return true; + return transport.getDepth() == null; // if depth is set we need to request objects that are already available } catch (MissingObjectException e) { return false; } catch (IOException e) { @@ -516,8 +516,10 @@ class FetchProcess { } if (spec.getDestination() != null) { final TrackingRefUpdate tru = createUpdate(spec, newId); - if (newId.equals(tru.getOldObjectId())) + // if depth is set we need to update the ref + if (newId.equals(tru.getOldObjectId()) && transport.getDepth() == null) { return; + } localUpdates.add(tru); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java index 9ebc722ffe..0663c5141c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Google LLC. and others + * Copyright (C) 2018, 2022 Google LLC. 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 @@ -35,7 +35,7 @@ abstract class FetchRequest { final int deepenSince; - final List<String> deepenNotRefs; + final List<String> deepenNots; @Nullable final String agent; @@ -53,7 +53,7 @@ abstract class FetchRequest { * the filter spec * @param clientCapabilities * capabilities sent in the request - * @param deepenNotRefs + * @param deepenNots * Requests that the shallow clone/fetch should be cut at these * specific revisions instead of a depth. * @param deepenSince @@ -66,14 +66,14 @@ abstract class FetchRequest { @NonNull Set<ObjectId> clientShallowCommits, @NonNull FilterSpec filterSpec, @NonNull Set<String> clientCapabilities, int deepenSince, - @NonNull List<String> deepenNotRefs, @Nullable String agent) { + @NonNull List<String> deepenNots, @Nullable String agent) { this.wantIds = requireNonNull(wantIds); this.depth = depth; this.clientShallowCommits = requireNonNull(clientShallowCommits); this.filterSpec = requireNonNull(filterSpec); this.clientCapabilities = requireNonNull(clientCapabilities); this.deepenSince = deepenSince; - this.deepenNotRefs = requireNonNull(deepenNotRefs); + this.deepenNots = requireNonNull(deepenNots); this.agent = agent; } @@ -148,8 +148,8 @@ abstract class FetchRequest { * @return refs received in "deepen-not" lines. */ @NonNull - List<String> getDeepenNotRefs() { - return deepenNotRefs; + List<String> getDeepenNots() { + return deepenNots; } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java index 91adb5e6ac..4decb79513 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Google LLC. and others + * Copyright (C) 2018, 2022 Google LLC. 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 @@ -11,9 +11,10 @@ package org.eclipse.jgit.transport; import static java.util.Objects.requireNonNull; +import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.eclipse.jgit.annotations.NonNull; @@ -28,15 +29,20 @@ final class FetchV0Request extends FetchRequest { FetchV0Request(@NonNull Set<ObjectId> wantIds, int depth, @NonNull Set<ObjectId> clientShallowCommits, @NonNull FilterSpec filterSpec, - @NonNull Set<String> clientCapabilities, @Nullable String agent) { + @NonNull Set<String> clientCapabilities, int deepenSince, + @NonNull List<String> deepenNotRefs, @Nullable String agent) { super(wantIds, depth, clientShallowCommits, filterSpec, - clientCapabilities, 0, Collections.emptyList(), agent); + clientCapabilities, deepenSince, deepenNotRefs, agent); } static final class Builder { int depth; + final List<String> deepenNots = new ArrayList<>(); + + int deepenSince; + final Set<ObjectId> wantIds = new HashSet<>(); final Set<ObjectId> clientShallowCommits = new HashSet<>(); @@ -68,6 +74,50 @@ final class FetchV0Request extends FetchRequest { } /** + * @return depth set in the request (via a "deepen" line). Defaulting to + * 0 if not set. + */ + int getDepth() { + return depth; + } + + /** + * @return true if there has been at least one "deepen not" line in the + * request so far + */ + boolean hasDeepenNots() { + return !deepenNots.isEmpty(); + } + + /** + * @param deepenNot + * reference received in a "deepen not" line + * @return this builder + */ + Builder addDeepenNot(String deepenNot) { + deepenNots.add(deepenNot); + return this; + } + + /** + * @param value + * Unix timestamp received in a "deepen since" line + * @return this builder + */ + Builder setDeepenSince(int value) { + deepenSince = value; + return this; + } + + /** + * @return shallow since value, sent before in a "deepen since" line. 0 + * by default. + */ + int getDeepenSince() { + return deepenSince; + } + + /** * @param shallowOid * object id received in a "shallow" line * @return this builder @@ -110,7 +160,7 @@ final class FetchV0Request extends FetchRequest { FetchV0Request build() { return new FetchV0Request(wantIds, depth, clientShallowCommits, - filterSpec, clientCaps, agent); + filterSpec, clientCaps, deepenSince, deepenNots, agent); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java index 50fb9d2262..446a3bcc79 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Google LLC. and others + * Copyright (C) 2018, 2022 Google LLC. 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 @@ -50,7 +50,7 @@ public final class FetchV2Request extends FetchRequest { @NonNull List<String> wantedRefs, @NonNull Set<ObjectId> wantIds, @NonNull Set<ObjectId> clientShallowCommits, int deepenSince, - @NonNull List<String> deepenNotRefs, int depth, + @NonNull List<String> deepenNots, int depth, @NonNull FilterSpec filterSpec, boolean doneReceived, boolean waitForDone, @NonNull Set<String> clientCapabilities, @@ -58,7 +58,7 @@ public final class FetchV2Request extends FetchRequest { boolean sidebandAll, @NonNull List<String> packfileUriProtocols) { super(wantIds, depth, clientShallowCommits, filterSpec, clientCapabilities, deepenSince, - deepenNotRefs, agent); + deepenNots, agent); this.peerHas = requireNonNull(peerHas); this.wantedRefs = requireNonNull(wantedRefs); this.doneReceived = doneReceived; @@ -140,7 +140,7 @@ public final class FetchV2Request extends FetchRequest { final Set<ObjectId> clientShallowCommits = new HashSet<>(); - final List<String> deepenNotRefs = new ArrayList<>(); + final List<String> deepenNots = new ArrayList<>(); final Set<String> clientCapabilities = new HashSet<>(); @@ -240,17 +240,17 @@ public final class FetchV2Request extends FetchRequest { * @return true if there has been at least one "deepen not" line in the * request so far */ - boolean hasDeepenNotRefs() { - return !deepenNotRefs.isEmpty(); + boolean hasDeepenNots() { + return !deepenNots.isEmpty(); } /** - * @param deepenNotRef + * @param deepenNot * reference received in a "deepen not" line * @return this builder */ - Builder addDeepenNotRef(String deepenNotRef) { - deepenNotRefs.add(deepenNotRef); + Builder addDeepenNot(String deepenNot) { + deepenNots.add(deepenNot); return this; } @@ -350,7 +350,7 @@ public final class FetchV2Request extends FetchRequest { */ FetchV2Request build() { return new FetchV2Request(peerHas, wantedRefs, wantIds, - clientShallowCommits, deepenSince, deepenNotRefs, + clientShallowCommits, deepenSince, deepenNots, depth, filterSpec, doneReceived, waitForDone, clientCapabilities, agent, Collections.unmodifiableList(serverOptions), sidebandAll, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java index aaa9308ac3..24ea552ba6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java @@ -1,7 +1,7 @@ /* * Copyright (C) 2008, 2013 Google Inc. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others + * Copyright (C) 2008, 2022 Shawn O. Pearce <spearce@spearce.org> 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 @@ -234,6 +234,13 @@ public final class GitProtocolConstants { public static final String CAPABILITY_SERVER_OPTION = "server-option"; //$NON-NLS-1$ /** + * The server supports the receiving of shallow options. + * + * @since 6.3 + */ + public static final String CAPABILITY_SHALLOW = "shallow"; //$NON-NLS-1$ + + /** * Option for passing application-specific options to the server. * * @since 5.2 @@ -308,6 +315,13 @@ public final class GitProtocolConstants { public static final String SECTION_PACKFILE = "packfile"; //$NON-NLS-1$ /** + * Protocol V2 shallow-info section header. + * + * @since 6.3 + */ + public static final String SECTION_SHALLOW_INFO = "shallow-info"; //$NON-NLS-1$ + + /** * Protocol announcement for protocol version 1. This is the same as V0, * except for this initial line. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java index f8c51c180f..4ddcb99419 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Google LLC. and others + * Copyright (C) 2018, 2022 Google LLC. 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 @@ -77,10 +77,42 @@ final class ProtocolV0Parser { MessageFormat.format(JGitText.get().invalidDepth, Integer.valueOf(depth))); } + if (reqBuilder.getDeepenSince() != 0) { + throw new PackProtocolException( + JGitText.get().deepenSinceWithDeepen); + } + if (reqBuilder.hasDeepenNots()) { + throw new PackProtocolException( + JGitText.get().deepenNotWithDeepen); + } reqBuilder.setDepth(depth); continue; } + if (line.startsWith("deepen-not ")) { //$NON-NLS-1$ + reqBuilder.addDeepenNot(line.substring(11)); + if (reqBuilder.getDepth() != 0) { + throw new PackProtocolException( + JGitText.get().deepenNotWithDeepen); + } + continue; + } + + if (line.startsWith("deepen-since ")) { //$NON-NLS-1$ + // TODO: timestamps should be long + int ts = Integer.parseInt(line.substring(13)); + if (ts <= 0) { + throw new PackProtocolException(MessageFormat + .format(JGitText.get().invalidTimestamp, line)); + } + if (reqBuilder.getDepth() != 0) { + throw new PackProtocolException( + JGitText.get().deepenSinceWithDeepen); + } + reqBuilder.setDeepenSince(ts); + continue; + } + if (line.startsWith("shallow ")) { //$NON-NLS-1$ reqBuilder.addClientShallowCommit( ObjectId.fromString(line.substring(8))); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java index 6cec4b9a3f..e502831a2b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, Google LLC. and others + * Copyright (C) 2018, 2022 Google LLC. 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 @@ -149,13 +149,13 @@ final class ProtocolV2Parser { throw new PackProtocolException( JGitText.get().deepenSinceWithDeepen); } - if (reqBuilder.hasDeepenNotRefs()) { + if (reqBuilder.hasDeepenNots()) { throw new PackProtocolException( JGitText.get().deepenNotWithDeepen); } reqBuilder.setDepth(parsedDepth); } else if (line2.startsWith("deepen-not ")) { //$NON-NLS-1$ - reqBuilder.addDeepenNotRef(line2.substring(11)); + reqBuilder.addDeepenNot(line2.substring(11)); if (reqBuilder.getDepth() != 0) { throw new PackProtocolException( JGitText.get().deepenNotWithDeepen); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java index 3222d6330c..7cea998474 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java @@ -27,6 +27,7 @@ import java.lang.reflect.Modifier; import java.net.URISyntaxException; import java.net.URL; import java.text.MessageFormat; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -784,6 +785,12 @@ public abstract class Transport implements AutoCloseable { private PrePushHook prePush; + private Integer depth; + + private Instant deepenSince; + + private List<String> deepenNots = new ArrayList<>(); + @Nullable TransferConfig.ProtocolVersion protocol; @@ -1086,6 +1093,83 @@ public abstract class Transport implements AutoCloseable { filterSpec = requireNonNull(filter); } + + /** + * Retrieves the depth for a shallow clone. + * + * @return the depth, or {@code null} if none set + * @since 6.3 + */ + public final Integer getDepth() { + return depth; + } + + /** + * Limits fetching to the specified number of commits from the tip of each + * remote branch history. + * + * @param depth + * the depth + * @since 6.3 + */ + public final void setDepth(int depth) { + if (depth < 1) { + throw new IllegalArgumentException(JGitText.get().depthMustBeAt1); + } + this.depth = Integer.valueOf(depth); + } + + /** + * Limits fetching to the specified number of commits from the tip of each + * remote branch history. + * + * @param depth + * the depth, or {@code null} to unset the depth + * @since 6.3 + */ + public final void setDepth(Integer depth) { + if (depth != null && depth.intValue() < 1) { + throw new IllegalArgumentException(JGitText.get().depthMustBeAt1); + } + this.depth = depth; + } + + /** + * @return the deepen-since for a shallow clone + * @since 6.3 + */ + public final Instant getDeepenSince() { + return deepenSince; + } + + /** + * Deepen or shorten the history of a shallow repository to include all reachable commits after a specified time. + * + * @param deepenSince the deepen-since. Must not be {@code null} + * @since 6.3 + */ + public final void setDeepenSince(@NonNull Instant deepenSince) { + this.deepenSince = deepenSince; + } + + /** + * @return the deepen-not for a shallow clone + * @since 6.3 + */ + public final List<String> getDeepenNots() { + return deepenNots; + } + + /** + * Deepen or shorten the history of a shallow repository to exclude commits reachable from a specified remote branch or tag. + * + * @param deepenNots the deepen-not. Must not be {@code null} + * @since 6.3 + */ + public final void setDeepenNots(@NonNull List<String> deepenNots) { + this.deepenNots = deepenNots; + } + /** * Apply provided remote configuration on this transport. * @@ -1230,7 +1314,7 @@ public abstract class Transport implements AutoCloseable { * @param toFetch * specification of refs to fetch locally. May be null or the * empty collection to use the specifications from the - * RemoteConfig. May contains regular and negative + * RemoteConfig. May contains regular and negative * {@link RefSpec}s. Source for each regular RefSpec can't * be null. * @return information describing the tracking refs updated. @@ -1266,7 +1350,7 @@ public abstract class Transport implements AutoCloseable { * @param toFetch * specification of refs to fetch locally. May be null or the * empty collection to use the specifications from the - * RemoteConfig. May contains regular and negative + * RemoteConfig. May contain regular and negative * {@link RefSpec}s. Source for each regular RefSpec can't * be null. * @param branch diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index dcd806a3da..409161d58b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2020 Google Inc. and others + * Copyright (C) 2008, 2022 Google Inc. 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 @@ -10,6 +10,7 @@ package org.eclipse.jgit.transport; +import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableMap; import static java.util.Objects.requireNonNull; import static org.eclipse.jgit.lib.Constants.R_TAGS; @@ -1033,6 +1034,7 @@ public class UploadPack implements Closeable { // writing a response. Buffer the response until then. PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator(); List<ObjectId> unshallowCommits = new ArrayList<>(); + List<ObjectId> deepenNots = emptyList(); FetchRequest req; try { if (biDirectionalPipe) @@ -1071,13 +1073,14 @@ public class UploadPack implements Closeable { verifyClientShallow(req.getClientShallowCommits()); } - if (req.getDepth() != 0 || req.getDeepenSince() != 0) { + deepenNots = parseDeepenNots(req.getDeepenNots()); + if (req.getDepth() != 0 || req.getDeepenSince() != 0 || !req.getDeepenNots().isEmpty()) { computeShallowsAndUnshallows(req, shallow -> { pckOut.writeString("shallow " + shallow.name() + '\n'); //$NON-NLS-1$ }, unshallow -> { pckOut.writeString("unshallow " + unshallow.name() + '\n'); //$NON-NLS-1$ unshallowCommits.add(unshallow); - }, Collections.emptyList()); + }, deepenNots); pckOut.end(); } @@ -1109,7 +1112,7 @@ public class UploadPack implements Closeable { if (sendPack) { sendPack(accumulator, req, refs == null ? null : refs.values(), - unshallowCommits, Collections.emptyList(), pckOut); + unshallowCommits, deepenNots, pckOut); } } @@ -1188,15 +1191,7 @@ public class UploadPack implements Closeable { // TODO(ifrade): Refactor to pass around the Request object, instead of // copying data back to class fields - List<ObjectId> deepenNots = new ArrayList<>(); - for (String s : req.getDeepenNotRefs()) { - Ref ref = findRef(s); - if (ref == null) { - throw new PackProtocolException(MessageFormat - .format(JGitText.get().invalidRefName, s)); - } - deepenNots.add(ref.getObjectId()); - } + List<ObjectId> deepenNots = parseDeepenNots(req.getDeepenNots()); Map<String, ObjectId> wantedRefs = wantedRefs(req); // TODO(ifrade): Avoid mutating the parsed request. @@ -1206,7 +1201,7 @@ public class UploadPack implements Closeable { boolean sectionSent = false; boolean mayHaveShallow = req.getDepth() != 0 || req.getDeepenSince() != 0 - || !req.getDeepenNotRefs().isEmpty(); + || !req.getDeepenNots().isEmpty(); List<ObjectId> shallowCommits = new ArrayList<>(); List<ObjectId> unshallowCommits = new ArrayList<>(); @@ -2476,6 +2471,24 @@ public class UploadPack implements Closeable { } } + private List<ObjectId> parseDeepenNots(List<String> deepenNots) + throws IOException { + List<ObjectId> result = new ArrayList<>(); + for (String s : deepenNots) { + if (ObjectId.isId(s)) { + result.add(ObjectId.fromString(s)); + } else { + Ref ref = findRef(s); + if (ref == null) { + throw new PackProtocolException(MessageFormat + .format(JGitText.get().invalidRefName, s)); + } + result.add(ref.getObjectId()); + } + } + return result; + } + private static class ResponseBufferedOutputStream extends OutputStream { private final OutputStream rawOut; |