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 | 93 +++++++++++++++++-- 4 files changed, 111 insertions(+), 9 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 b14b0b3347..c9e44e768f 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 static org.junit.Assert.assertTrue; 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 class RefSpecTest { 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 ebe1befee1..7bf3d1e324 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 313512f990..3c7dfb4c1b 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 class JGitText extends TranslationBundle { /***/ 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 cbc6cc021a..aa0118e399 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 class RefSpec implements Serializable { /** 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,7 @@ public class RefSpec implements Serializable { wildcard = false; srcName = Constants.HEAD; dstName = null; + allowMismatchedWildcards = WildcardMode.REQUIRE_MATCH; } /** @@ -116,12 +123,24 @@ public class RefSpec implements Serializable { *
  • :refs/heads/master
  • * * + * 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(final String spec) { + public RefSpec(String spec, WildcardMode mode) { + this.allowMismatchedWildcards = mode; String s = spec; if (s.startsWith("+")) { //$NON-NLS-1$ force = true; @@ -131,8 +150,13 @@ public class RefSpec implements Serializable { final int c = s.lastIndexOf(':'); if (c == 0) { s = s.substring(1); - if (isWildcard(s)) - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec)); + 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); @@ -141,24 +165,55 @@ public class RefSpec implements Serializable { // 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)); + 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)) - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec)); + if (isWildcard(s)) { + if (mode == WildcardMode.REQUIRE_MATCH) { + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().invalidWildcards, spec)); + } + wildcard = true; + } srcName = checkValid(s); } } + /** + * Parse a ref specification for use during transport operations. + *

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

    + * + * @param spec + * string describing the specification. + * @throws IllegalArgumentException + * the specification is invalid. + */ + public RefSpec(final String spec) { + this(spec, WildcardMode.REQUIRE_MATCH); + } + private RefSpec(final RefSpec p) { force = p.isForceUpdate(); wildcard = p.isWildcard(); srcName = p.getSource(); dstName = p.getDestination(); + allowMismatchedWildcards = p.allowMismatchedWildcards; } /** @@ -348,8 +403,15 @@ public class RefSpec implements Serializable { * @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 @@ public class RefSpec implements Serializable { * @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 class RefSpec implements Serializable { * @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 @@ public class RefSpec implements Serializable { * @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 class RefSpec implements Serializable { 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); -- 2.39.5