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 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 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 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 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 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:
+ *
+ * refs/heads/*
+ * refs/heads/*:refs/heads/master
+ *
+ *
* @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:
+ *
+ * refs/heads/master
+ * refs/heads/master:refs/remotes/origin/master
+ * refs/heads/*:refs/remotes/origin/*
+ * +refs/heads/master
+ * +refs/heads/master:refs/remotes/origin/master
+ * +refs/heads/*:refs/remotes/origin/*
+ * +refs/pull/*/head:refs/remotes/origin/pr/*
+ * :refs/heads/master
+ *
+ *
+ * @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);