aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src
diff options
context:
space:
mode:
authoryunjieli <yunjieli@google.com>2022-03-28 14:47:02 -0700
committeryunjieli <yunjieli@google.com>2022-04-13 10:21:20 -0700
commiteca101fc0570e739cc722bf2977973ac2a4ce147 (patch)
treee651226f4908b538bd83a5e1ed32d8a5ad70d053 /org.eclipse.jgit/src
parent68a07258960b13f465b61eb0dd06208034c85cc1 (diff)
downloadjgit-eca101fc0570e739cc722bf2977973ac2a4ce147.tar.gz
jgit-eca101fc0570e739cc722bf2977973ac2a4ce147.zip
Fetch: Introduce negative refspecs.
Implement negative refspecs in JGit fetch, following C Git. Git supports negative refspecs in source only while this change supports them in both source and destination. If one branch is equal to any branch or matches any pattern in the negative refspecs collection, the branch will not be fetched even if it's in the toFetch collection. With this feature, users can express more complex patterns during fetch. Change-Id: Iaa1cd4de5c08c273e198b72e12e3dadae7be709f Sign-off-by: Yunjie Li<yunjieli@google.com>
Diffstat (limited to 'org.eclipse.jgit/src')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java41
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java73
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java8
4 files changed, 115 insertions, 8 deletions
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 16b3f372ef..b81e605c13 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -421,6 +421,7 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidModeFor;
/***/ public String invalidModeForPath;
/***/ public String invalidNameContainsDotDot;
+ /***/ public String invalidNegativeAndForce;
/***/ public String invalidObject;
/***/ public String invalidOldIdSent;
/***/ public String invalidPacketLineHeader;
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 7d7b3ee0a0..87e5476036 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -31,6 +31,7 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NotSupportedException;
@@ -56,6 +57,12 @@ class FetchProcess {
/** List of things we want to fetch from the remote repository. */
private final Collection<RefSpec> toFetch;
+ /**
+ * List of things we don't want to fetch from the remote repository or to
+ * the local repository.
+ */
+ private final Collection<RefSpec> negativeRefSpecs;
+
/** Set of refs we will actually wind up asking to obtain. */
private final HashMap<ObjectId, Ref> askFor = new HashMap<>();
@@ -74,9 +81,12 @@ class FetchProcess {
private Map<String, Ref> localRefs;
- FetchProcess(Transport t, Collection<RefSpec> f) {
+ FetchProcess(Transport t, Collection<RefSpec> refSpecs) {
transport = t;
- toFetch = f;
+ toFetch = refSpecs.stream().filter(refSpec -> !refSpec.isNegative())
+ .collect(Collectors.toList());
+ negativeRefSpecs = refSpecs.stream().filter(RefSpec::isNegative)
+ .collect(Collectors.toList());
}
void execute(ProgressMonitor monitor, FetchResult result,
@@ -389,8 +399,13 @@ class FetchProcess {
private void expandWildcard(RefSpec spec, Set<Ref> matched)
throws TransportException {
for (Ref src : conn.getRefs()) {
- if (spec.matchSource(src) && matched.add(src))
- want(src, spec.expandFromSource(src));
+ if (spec.matchSource(src)) {
+ RefSpec expandedRefSpec = spec.expandFromSource(src);
+ if (!matchNegativeRefSpec(expandedRefSpec)
+ && matched.add(src)) {
+ want(src, expandedRefSpec);
+ }
+ }
}
}
@@ -406,11 +421,27 @@ class FetchProcess {
if (src == null) {
throw new TransportException(MessageFormat.format(JGitText.get().remoteDoesNotHaveSpec, want));
}
- if (matched.add(src)) {
+ if (!matchNegativeRefSpec(spec) && matched.add(src)) {
want(src, spec);
}
}
+ private boolean matchNegativeRefSpec(RefSpec spec) {
+ for (RefSpec negativeRefSpec : negativeRefSpecs) {
+ if (negativeRefSpec.getSource() != null && spec.getSource() != null
+ && negativeRefSpec.matchSource(spec.getSource())) {
+ return true;
+ }
+
+ if (negativeRefSpec.getDestination() != null
+ && spec.getDestination() != null && negativeRefSpec
+ .matchDestination(spec.getDestination())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean localHasObject(ObjectId id) throws TransportException {
try {
return transport.local.getObjectDatabase().has(id);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
index 56d0036a20..b0363147c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
@@ -53,6 +53,9 @@ public class RefSpec implements Serializable {
/** Is this the special ":" RefSpec? */
private boolean matching;
+ /** Is this a negative refspec. */
+ private boolean negative;
+
/**
* How strict to be about wildcards.
*
@@ -96,12 +99,23 @@ public class RefSpec implements Serializable {
wildcard = false;
srcName = Constants.HEAD;
dstName = null;
+ negative =false;
allowMismatchedWildcards = WildcardMode.REQUIRE_MATCH;
}
/**
* Parse a ref specification for use during transport operations.
* <p>
+ * {@link RefSpec}s can be regular or negative, regular RefSpecs indicate
+ * what to include in transport operations while negative RefSpecs indicate
+ * what to exclude in fetch.
+ * <p>
+ * Negative {@link RefSpec}s can't be force, must have only source or
+ * destination. Wildcard patterns are also supported in negative RefSpecs
+ * but they can not go with {@code WildcardMode.REQUIRE_MATCH} because they
+ * are natually one to many mappings.
+ *
+ * <p>
* Specifications are typically one of the following forms:
* <ul>
* <li><code>refs/heads/master</code></li>
@@ -121,6 +135,12 @@ public class RefSpec implements Serializable {
* <li><code>refs/heads/*:refs/heads/master</code></li>
* </ul>
*
+ * Negative specifications are usually like:
+ * <ul>
+ * <li><code>^:refs/heads/master</code></li>
+ * <li><code>^refs/heads/*</code></li>
+ * </ul>
+ *
* @param spec
* string describing the specification.
* @param mode
@@ -133,11 +153,22 @@ public class RefSpec implements Serializable {
public RefSpec(String spec, WildcardMode mode) {
this.allowMismatchedWildcards = mode;
String s = spec;
+
+ if (s.startsWith("^+") || s.startsWith("+^")) {
+ throw new IllegalArgumentException(
+ JGitText.get().invalidNegativeAndForce);
+ }
+
if (s.startsWith("+")) { //$NON-NLS-1$
force = true;
s = s.substring(1);
}
+ if(s.startsWith("^")) {
+ negative = true;
+ s = s.substring(1);
+ }
+
boolean matchPushSpec = false;
final int c = s.lastIndexOf(':');
if (c == 0) {
@@ -181,6 +212,21 @@ public class RefSpec implements Serializable {
}
srcName = checkValid(s);
}
+
+ // Negative refspecs must only have dstName or srcName.
+ if (isNegative()) {
+ if (isNullOrEmpty(srcName) && isNullOrEmpty(dstName)) {
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().invalidRefSpec, spec));
+ }
+ if (!isNullOrEmpty(srcName) && !isNullOrEmpty(dstName)) {
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().invalidRefSpec, spec));
+ }
+ if(wildcard && mode == WildcardMode.REQUIRE_MATCH) {
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().invalidRefSpec, spec));}
+ }
matching = matchPushSpec;
}
@@ -205,13 +251,15 @@ public class RefSpec implements Serializable {
* the specification is invalid.
*/
public RefSpec(String spec) {
- this(spec, WildcardMode.REQUIRE_MATCH);
+ this(spec, spec.startsWith("^") ? WildcardMode.ALLOW_MISMATCH
+ : WildcardMode.REQUIRE_MATCH);
}
private RefSpec(RefSpec p) {
matching = false;
force = p.isForceUpdate();
wildcard = p.isWildcard();
+ negative = p.isNegative();
srcName = p.getSource();
dstName = p.getDestination();
allowMismatchedWildcards = p.allowMismatchedWildcards;
@@ -246,6 +294,10 @@ public class RefSpec implements Serializable {
*/
public RefSpec setForceUpdate(boolean forceUpdate) {
final RefSpec r = new RefSpec(this);
+ if (forceUpdate && isNegative()) {
+ throw new IllegalArgumentException(
+ JGitText.get().invalidNegativeAndForce);
+ }
r.matching = matching;
r.force = forceUpdate;
return r;
@@ -265,6 +317,15 @@ public class RefSpec implements Serializable {
}
/**
+ * Check if this specification is a negative one.
+ *
+ * @return true if this specification is negative.
+ */
+ public boolean isNegative() {
+ return negative;
+ }
+
+ /**
* Get the source ref description.
* <p>
* During a fetch this is the name of the ref on the remote repository we
@@ -435,6 +496,10 @@ public class RefSpec implements Serializable {
return this;
}
+ private static boolean isNullOrEmpty(String refName) {
+ return refName == null || refName.isEmpty();
+ }
+
/**
* Expand this specification to exactly match a ref.
* <p>
@@ -570,6 +635,9 @@ public class RefSpec implements Serializable {
if (isForceUpdate() != b.isForceUpdate()) {
return false;
}
+ if(isNegative() != b.isNegative()) {
+ return false;
+ }
if (isMatching()) {
return b.isMatching();
} else if (b.isMatching()) {
@@ -587,6 +655,9 @@ public class RefSpec implements Serializable {
if (isForceUpdate()) {
r.append('+');
}
+ if(isNegative()) {
+ r.append('^');
+ }
if (isMatching()) {
r.append(':');
} else {
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 0eab4434ed..3222d6330c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -1230,7 +1230,9 @@ 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. Source for each RefSpec can't be null.
+ * 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.
* @throws org.eclipse.jgit.errors.NotSupportedException
* this transport implementation does not support fetching
@@ -1264,7 +1266,9 @@ 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. Source for each RefSpec can't be null.
+ * RemoteConfig. May contains regular and negative
+ * {@link RefSpec}s. Source for each regular RefSpec can't
+ * be null.
* @param branch
* the initial branch to check out when cloning the repository.
* Can be specified as ref name (<code>refs/heads/master</code>),