From a2d3c376a682b540ecd87fe3ff2ffebc1766f938 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 22 Jul 2016 11:39:50 -0700 Subject: [PATCH] RefSpecs: allow construction of weird wildcarded RefSpecs Gerrit's superproject subscription feature uses RefSpecs to formalize the ACLs of when the superproject subscription feature is allowed. As this is a slightly different use case than describing a local/remote pair of refs, we need to be more permissive. Specifically we want to allow: refs/heads/* refs/heads/*:refs/heads/master refs/heads/master:refs/heads/* Introduce a new constructor, that allows constructing these RefSpecs. Change-Id: I46c0bea9d876e61eb2c8d50f404b905792bc72b3 Signed-off-by: Stefan Beller --- .../eclipse/jgit/transport/RefSpecTest.java | 25 ++++ .../eclipse/jgit/internal/JGitText.properties | 1 + .../org/eclipse/jgit/internal/JGitText.java | 1 + .../org/eclipse/jgit/transport/RefSpec.java | 137 ++++++++++++++---- 4 files changed, 133 insertions(+), 31 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java index b14b0b334..c9e44e768 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java @@ -55,6 +55,7 @@ import org.eclipse.jgit.lib.ObjectIdRef; import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.transport.RefSpec.WildcardMode; import org.junit.Test; public class RefSpecTest { @@ -474,4 +475,28 @@ public void invalidSetDestination() { RefSpec a = new RefSpec("refs/heads/*:refs/remotes/origin/*"); a.setDestination("refs/remotes/origin/*/*"); } + + @Test + public void sourceOnlywithWildcard() { + RefSpec a = new RefSpec("refs/heads/*", + WildcardMode.ALLOW_MISMATCH); + assertTrue(a.matchSource("refs/heads/master")); + assertNull(a.getDestination()); + } + + @Test + public void destinationWithWildcard() { + RefSpec a = new RefSpec("refs/heads/master:refs/heads/*", + WildcardMode.ALLOW_MISMATCH); + assertTrue(a.matchSource("refs/heads/master")); + assertTrue(a.matchDestination("refs/heads/master")); + assertTrue(a.matchDestination("refs/heads/foo")); + } + + @Test + public void onlyWildCard() { + RefSpec a = new RefSpec("*", WildcardMode.ALLOW_MISMATCH); + assertTrue(a.matchSource("refs/heads/master")); + assertNull(a.getDestination()); + } } 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 ebe1befee..7bf3d1e32 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -333,6 +333,7 @@ invalidChannel=Invalid channel {0} invalidCharacterInBase64Data=Invalid character in Base64 data. invalidCommitParentNumber=Invalid commit parent number invalidEncryption=Invalid encryption +invalidExpandWildcard=ExpandFromSource on a refspec that can have mismatched wildcards does not make sense. invalidGitdirRef = Invalid .git reference in file ''{0}'' invalidGitType=invalid git type: {0} invalidId=Invalid id: {0} 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 313512f99..3c7dfb4c1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -392,6 +392,7 @@ public static JGitText get() { /***/ public String invalidCharacterInBase64Data; /***/ public String invalidCommitParentNumber; /***/ public String invalidEncryption; + /***/ public String invalidExpandWildcard; /***/ public String invalidGitdirRef; /***/ public String invalidGitType; /***/ public String invalidId; 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 cbc6cc021..aa0118e39 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java @@ -82,6 +82,12 @@ public static boolean isWildcard(final String s) { /** Is this specification actually a wildcard match? */ private boolean wildcard; + enum WildcardMode { + REQUIRE_MATCH, ALLOW_MISMATCH + } + /** Whether a wildcard is allowed on one side but not the other. */ + private WildcardMode allowMismatchedWildcards; + /** Name of the ref(s) we would copy from. */ private String srcName; @@ -99,6 +105,83 @@ public RefSpec() { wildcard = false; srcName = Constants.HEAD; dstName = null; + allowMismatchedWildcards = WildcardMode.REQUIRE_MATCH; + } + + /** + * Parse a ref specification for use during transport operations. + *

+ * Specifications are typically one of the following forms: + *

+ * + * If the wildcard mode allows mismatches, then these ref specs are also + * valid: + * + * + * @param spec + * string describing the specification. + * @param mode + * whether to allow a wildcard on one side without a wildcard on + * the other. + * @throws IllegalArgumentException + * the specification is invalid. + * @since 4.5 + */ + public RefSpec(String spec, WildcardMode mode) { + this.allowMismatchedWildcards = mode; + String s = spec; + if (s.startsWith("+")) { //$NON-NLS-1$ + force = true; + s = s.substring(1); + } + + final int c = s.lastIndexOf(':'); + if (c == 0) { + s = s.substring(1); + if (isWildcard(s)) { + wildcard = true; + if (mode == WildcardMode.REQUIRE_MATCH) { + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().invalidWildcards, spec)); + } + } + dstName = checkValid(s); + } else if (c > 0) { + String src = s.substring(0, c); + String dst = s.substring(c + 1); + if (isWildcard(src) && isWildcard(dst)) { + // Both contain wildcard + wildcard = true; + } else if (isWildcard(src) || isWildcard(dst)) { + wildcard = true; + if (mode == WildcardMode.REQUIRE_MATCH) + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().invalidWildcards, spec)); + } + srcName = checkValid(src); + dstName = checkValid(dst); + } else { + if (isWildcard(s)) { + if (mode == WildcardMode.REQUIRE_MATCH) { + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().invalidWildcards, spec)); + } + wildcard = true; + } + srcName = checkValid(s); + } } /** @@ -122,36 +205,7 @@ public RefSpec() { * the specification is invalid. */ public RefSpec(final String spec) { - String s = spec; - if (s.startsWith("+")) { //$NON-NLS-1$ - force = true; - s = s.substring(1); - } - - final int c = s.lastIndexOf(':'); - if (c == 0) { - s = s.substring(1); - if (isWildcard(s)) - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec)); - dstName = checkValid(s); - } else if (c > 0) { - String src = s.substring(0, c); - String dst = s.substring(c + 1); - if (isWildcard(src) && isWildcard(dst)) { - // Both contain wildcard - wildcard = true; - } else if (isWildcard(src) || isWildcard(dst)) { - // If either source or destination has wildcard, the other one - // must have as well. - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec)); - } - srcName = checkValid(src); - dstName = checkValid(dst); - } else { - if (isWildcard(s)) - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec)); - srcName = checkValid(s); - } + this(spec, WildcardMode.REQUIRE_MATCH); } private RefSpec(final RefSpec p) { @@ -159,6 +213,7 @@ private RefSpec(final RefSpec p) { wildcard = p.isWildcard(); srcName = p.getSource(); dstName = p.getDestination(); + allowMismatchedWildcards = p.allowMismatchedWildcards; } /** @@ -348,8 +403,15 @@ public boolean matchDestination(final Ref r) { * @return a new specification expanded from provided ref name. Result * specification is wildcard if and only if provided ref name is * wildcard. + * @throws IllegalStateException + * when the RefSpec was constructed with wildcard mode that + * doesn't require matching wildcards. */ public RefSpec expandFromSource(final String r) { + if (allowMismatchedWildcards != WildcardMode.REQUIRE_MATCH) { + throw new IllegalStateException( + JGitText.get().invalidExpandWildcard); + } return isWildcard() ? new RefSpec(this).expandFromSourceImp(r) : this; } @@ -373,6 +435,9 @@ private RefSpec expandFromSourceImp(final String name) { * @return a new specification expanded from provided ref name. Result * specification is wildcard if and only if provided ref name is * wildcard. + * @throws IllegalStateException + * when the RefSpec was constructed with wildcard mode that + * doesn't require matching wildcards. */ public RefSpec expandFromSource(final Ref r) { return expandFromSource(r.getName()); @@ -390,8 +455,15 @@ public RefSpec expandFromSource(final Ref r) { * @return a new specification expanded from provided ref name. Result * specification is wildcard if and only if provided ref name is * wildcard. + * @throws IllegalStateException + * when the RefSpec was constructed with wildcard mode that + * doesn't require matching wildcards. */ public RefSpec expandFromDestination(final String r) { + if (allowMismatchedWildcards != WildcardMode.REQUIRE_MATCH) { + throw new IllegalStateException( + JGitText.get().invalidExpandWildcard); + } return isWildcard() ? new RefSpec(this).expandFromDstImp(r) : this; } @@ -414,6 +486,9 @@ private RefSpec expandFromDstImp(final String name) { * @return a new specification expanded from provided ref name. Result * specification is wildcard if and only if provided ref name is * wildcard. + * @throws IllegalStateException + * when the RefSpec was constructed with wildcard mode that + * doesn't require matching wildcards. */ public RefSpec expandFromDestination(final Ref r) { return expandFromDestination(r.getName()); @@ -422,7 +497,7 @@ public RefSpec expandFromDestination(final Ref r) { private boolean match(final String name, final String s) { if (s == null) return false; - if (isWildcard()) { + if (isWildcard(s)) { int wildcardIndex = s.indexOf('*'); String prefix = s.substring(0, wildcardIndex); String suffix = s.substring(wildcardIndex + 1);