[PATCH 5/5] Support '*' in the middle of a refspec

Subsystems: the rest

STALE3745d

2 messages, 1 author, 2016-06-15 · open the first message on its own page

[PATCH 5/5] Support '*' in the middle of a refspec

From: Daniel Barkalow <hidden>
Date: 2016-06-15 22:46:20

In order to keep the requirements strict, each * has to be a full path
component, and there may only be one * per side. This requirement is
enforced entirely by check_ref_format(); the matching implementation
will substitute the whatever matches the * in the lhs for the * in the
rhs.

Signed-off-by: Daniel Barkalow <redacted>
---
 refs.c             |   11 +++++++----
 remote.c           |   27 ++++++++++++++++++---------
 t/t5511-refspec.sh |   12 ++++++++++++
 3 files changed, 37 insertions(+), 13 deletions(-)
diff --git a/refs.c b/refs.c
index a50ba79..fef7c9f 100644
--- a/refs.c
+++ b/refs.c
@@ -694,6 +694,7 @@ static inline int bad_ref_char(int ch)
 int check_ref_format(const char *ref)
 {
 	int ch, level, bad_type;
+	int ret = CHECK_REF_FORMAT_OK;
 	const char *cp = ref;
 
 	level = 0;
@@ -709,9 +710,11 @@ int check_ref_format(const char *ref)
 			return CHECK_REF_FORMAT_ERROR;
 		bad_type = bad_ref_char(ch);
 		if (bad_type) {
-			return (bad_type == 2 && !*cp)
-				? CHECK_REF_FORMAT_WILDCARD
-				: CHECK_REF_FORMAT_ERROR;
+			if (bad_type == 2 && (!*cp || *cp == '/') &&
+			    ret == CHECK_REF_FORMAT_OK)
+				ret = CHECK_REF_FORMAT_WILDCARD;
+			else
+				return CHECK_REF_FORMAT_ERROR;
 		}
 
 		/* scan the rest of the path component */
@@ -729,7 +732,7 @@ int check_ref_format(const char *ref)
 		if (!ch) {
 			if (level < 2)
 				return CHECK_REF_FORMAT_ONELEVEL;
-			return CHECK_REF_FORMAT_OK;
+			return ret;
 		}
 	}
 }
diff --git a/remote.c b/remote.c
index d0ce4c6..41005cf 100644
--- a/remote.c
+++ b/remote.c
@@ -511,12 +511,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
 
 		if (rhs) {
 			size_t rlen = strlen(++rhs);
-			is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*"));
+			is_glob = (1 <= rlen && strchr(rhs, '*'));
 			rs[i].dst = xstrndup(rhs, rlen);
 		}
 
 		llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
-		if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) {
+		if (1 <= llen && memchr(lhs, '*', llen)) {
 			if ((rhs && !is_glob) || (!rhs && fetch))
 				goto invalid;
 			is_glob = 1;
@@ -716,15 +716,24 @@ int remote_has_url(struct remote *remote, const char *url)
 static int name_fits_pattern(const char *key, const char *name,
 			     const char *value, char **result)
 {
-	size_t klen = strchr(key, '*') - key;
-	int ret = !strncmp(name, key, klen);
+	const char *keystar = strchr(key, '*');
+	size_t klen = keystar - key;
+	size_t ksuffixlen = strlen(keystar + 1);
+	size_t namelen = strlen(name);
+	int ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
+		!memcmp(name + namelen - ksuffixlen, keystar + 1, ksuffixlen);
 	if (ret && value) {
-		size_t vlen = strchr(value, '*') - value;
-		*result = xmalloc(vlen +
+		const char *valuestar = strchr(value, '*');
+		size_t vlen = valuestar - value;
+		size_t vsuffixlen = strlen(valuestar + 1);
+		*result = xmalloc(vlen + vsuffixlen +
 				  strlen(name) -
-				  klen + 1);
-		strcpy(*result, value);
-		strcpy(*result + vlen, name + klen);
+				  klen - ksuffixlen + 1);
+		strncpy(*result, value, vlen);
+		strncpy(*result + vlen,
+			name + klen, namelen - klen - ksuffixlen);
+		strcpy(*result + vlen + namelen - klen - ksuffixlen,
+		       valuestar + 1);
 	}
 	return ret;
 }
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
index 22ba380..c289322 100755
--- a/t/t5511-refspec.sh
+++ b/t/t5511-refspec.sh
@@ -72,4 +72,16 @@ test_refspec fetch ':refs/remotes/frotz/HEAD-to-me'
 test_refspec push ':refs/remotes/frotz/delete me'		invalid
 test_refspec fetch ':refs/remotes/frotz/HEAD to me'		invalid
 
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+
+test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*'
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*'
+
 test_done
-- 
1.6.1.286.gd33a4.dirty

[PATCH v2 5/5] Support '*' in the middle of a refspec

From: Daniel Barkalow <hidden>
Date: 2016-06-15 22:46:21

In order to keep the requirements strict, each * has to be a full path
component, and there may only be one * per side. This requirement is
enforced entirely by check_ref_format(); the matching implementation
will substitute the whatever matches the * in the lhs for the * in the
rhs.

Signed-off-by: Daniel Barkalow <redacted>
---
Rebased on the new version of the earlier patches

 refs.c             |   11 +++++++----
 remote.c           |   24 +++++++++++++++++-------
 t/t5511-refspec.sh |   12 ++++++++++++
 3 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/refs.c b/refs.c
index a50ba79..fef7c9f 100644
--- a/refs.c
+++ b/refs.c
@@ -694,6 +694,7 @@ static inline int bad_ref_char(int ch)
 int check_ref_format(const char *ref)
 {
 	int ch, level, bad_type;
+	int ret = CHECK_REF_FORMAT_OK;
 	const char *cp = ref;
 
 	level = 0;
@@ -709,9 +710,11 @@ int check_ref_format(const char *ref)
 			return CHECK_REF_FORMAT_ERROR;
 		bad_type = bad_ref_char(ch);
 		if (bad_type) {
-			return (bad_type == 2 && !*cp)
-				? CHECK_REF_FORMAT_WILDCARD
-				: CHECK_REF_FORMAT_ERROR;
+			if (bad_type == 2 && (!*cp || *cp == '/') &&
+			    ret == CHECK_REF_FORMAT_OK)
+				ret = CHECK_REF_FORMAT_WILDCARD;
+			else
+				return CHECK_REF_FORMAT_ERROR;
 		}
 
 		/* scan the rest of the path component */
@@ -729,7 +732,7 @@ int check_ref_format(const char *ref)
 		if (!ch) {
 			if (level < 2)
 				return CHECK_REF_FORMAT_ONELEVEL;
-			return CHECK_REF_FORMAT_OK;
+			return ret;
 		}
 	}
 }
diff --git a/remote.c b/remote.c
index d596a48..90203e2 100644
--- a/remote.c
+++ b/remote.c
@@ -511,12 +511,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
 
 		if (rhs) {
 			size_t rlen = strlen(++rhs);
-			is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*"));
+			is_glob = (1 <= rlen && strchr(rhs, '*'));
 			rs[i].dst = xstrndup(rhs, rlen);
 		}
 
 		llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
-		if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) {
+		if (1 <= llen && memchr(lhs, '*', llen)) {
 			if ((rhs && !is_glob) || (!rhs && fetch))
 				goto invalid;
 			is_glob = 1;
@@ -718,22 +718,32 @@ static int match_name_with_pattern(const char *key, const char *name,
 {
 	const char *kstar = strchr(key, '*');
 	size_t klen;
+	size_t ksuffixlen;
+	size_t namelen;
 	int ret;
 	if (!kstar)
 		die("Key '%s' of pattern had no '*'", key);
 	klen = kstar - key;
-	ret = !strncmp(key, name, klen);
+	ksuffixlen = strlen(kstar + 1);
+	namelen = strlen(name);
+	ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
+		!memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
 	if (ret && value) {
 		const char *vstar = strchr(value, '*');
 		size_t vlen;
+		size_t vsuffixlen;
 		if (!vstar)
 			die("Value '%s' of pattern has no '*'", value);
 		vlen = vstar - value;
-		*result = xmalloc(vlen +
+		vsuffixlen = strlen(vstar + 1);
+		*result = xmalloc(vlen + vsuffixlen +
 				  strlen(name) -
-				  klen + 1);
-		strcpy(*result, value);
-		strcpy(*result + vlen, name + klen);
+				  klen - ksuffixlen + 1);
+		strncpy(*result, value, vlen);
+		strncpy(*result + vlen,
+			name + klen, namelen - klen - ksuffixlen);
+		strcpy(*result + vlen + namelen - klen - ksuffixlen,
+		       vstar + 1);
 	}
 	return ret;
 }
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
index 22ba380..c289322 100755
--- a/t/t5511-refspec.sh
+++ b/t/t5511-refspec.sh
@@ -72,4 +72,16 @@ test_refspec fetch ':refs/remotes/frotz/HEAD-to-me'
 test_refspec push ':refs/remotes/frotz/delete me'		invalid
 test_refspec fetch ':refs/remotes/frotz/HEAD to me'		invalid
 
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+
+test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*'
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*'
+
 test_done
-- 
1.6.1.286.gd33a4.dirty
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help