Browse Source

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 <sbeller@google.com>
tags/v4.5.0.201609210915-r
Stefan Beller 7 years ago
parent
commit
a2d3c376a6

+ 25
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java View 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());
}
}

+ 1
- 0
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties View 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}

+ 1
- 0
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java View 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;

+ 84
- 9
org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java View 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);

Loading…
Cancel
Save