]> source.dussan.org Git - jgit.git/commitdiff
RefSpecs: allow construction of weird wildcarded RefSpecs 38/77738/11
authorStefan Beller <sbeller@google.com>
Fri, 22 Jul 2016 18:39:50 +0000 (11:39 -0700)
committerStefan Beller <sbeller@google.com>
Mon, 25 Jul 2016 17:10:06 +0000 (10:10 -0700)
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 <sbeller@google.com>
org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java

index b14b0b33476fbeac9b30e21c4a2e8a746ad5d551..c9e44e768fafa34ee333b8b147db3be7aaee5756 100644 (file)
@@ -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());
+       }
 }
index ebe1befee148400d9b3e00d558e6dd409913b9cd..7bf3d1e3243597a155f026be531a09cf0923c6ec 100644 (file)
@@ -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}
index 313512f990123cc898903b00e8f1e8bb27e3b8c1..3c7dfb4c1bee39a423125eca0c7c1cff8a848830 100644 (file)
@@ -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;
index cbc6cc021ad93746b5e370f4119f388cc0b6e632..aa0118e399a364ff091342e8cb5f8e0e87cc18c7 100644 (file)
@@ -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 {
         * <li><code>:refs/heads/master</code></li>
         * </ul>
         *
+        * If the wildcard mode allows mismatches, then these ref specs are also
+        * valid:
+        * <ul>
+        * <li><code>refs/heads/*</code></li>
+        * <li><code>refs/heads/*:refs/heads/master</code></li>
+        * </ul>
+        *
         * @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.
+        * <p>
+        * Specifications are typically one of the following forms:
+        * <ul>
+        * <li><code>refs/heads/master</code></li>
+        * <li><code>refs/heads/master:refs/remotes/origin/master</code></li>
+        * <li><code>refs/heads/*:refs/remotes/origin/*</code></li>
+        * <li><code>+refs/heads/master</code></li>
+        * <li><code>+refs/heads/master:refs/remotes/origin/master</code></li>
+        * <li><code>+refs/heads/*:refs/remotes/origin/*</code></li>
+        * <li><code>+refs/pull/&#42;/head:refs/remotes/origin/pr/*</code></li>
+        * <li><code>:refs/heads/master</code></li>
+        * </ul>
+        *
+        * @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);