Re: [PATCH] Move refspec pattern matching to match_refs().

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

Re: [PATCH] Move refspec pattern matching to match_refs().

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:43:10

Daniel Barkalow [off-list ref] writes:
This means that send-pack and http-push will support pattern refspecs,
so builtin-push.c doesn't have to expand them, and also git push can
just turn --tags into "refs/tags/*", further simplifying builtin-push.c
Nice.
quoted hunk
@@ -266,5 +174,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
 		usage(push_usage);
 	}
 	set_refspecs(argv + i, argc - i);
+	if (all && refspec)
+		usage(push_usage);
+
 	return do_push(repo);
 }
Is this hunk an independent bugfix?  I think send-pack has its
own check but I guess http-push lacked its own check?
quoted hunk
diff --git a/refs.c b/refs.c
index 2ae3235..cd63f37 100644
--- a/refs.c
+++ b/refs.c
@@ -603,15 +603,18 @@ int get_ref_sha1(const char *ref, unsigned char *sha1)
 
 static inline int bad_ref_char(int ch)
 {
-	return (((unsigned) ch) <= ' ' ||
-		ch == '~' || ch == '^' || ch == ':' ||
-		/* 2.13 Pattern Matching Notation */
-		ch == '?' || ch == '*' || ch == '[');
+	if (((unsigned) ch) <= ' ' ||
+	    ch == '~' || ch == '^' || ch == ':')
+		return 1;
+	/* 2.13 Pattern Matching Notation */
+	if (ch == '?' || ch == '*' || ch == '[')
+		return 2;
+	return 0;
 }
 
 int check_ref_format(const char *ref)
 {
-	int ch, level;
+	int ch, level, bad_type;
 	const char *cp = ref;
 
 	level = 0;
@@ -622,13 +625,19 @@ int check_ref_format(const char *ref)
 			return -1; /* should not end with slashes */
 
 		/* we are at the beginning of the path component */
-		if (ch == '.' || bad_ref_char(ch))
+		if (ch == '.')
 			return -1;
+		bad_type = bad_ref_char(ch);
+		if (bad_type) {
+			return (bad_type == 2 && !*cp) ? -3 : -1;
+		}
 
 		/* scan the rest of the path component */
 		while ((ch = *cp++) != 0) {
-			if (bad_ref_char(ch))
-				return -1;
+			bad_type = bad_ref_char(ch);
+			if (bad_type) {
+				return (bad_type == 2 && !*cp) ? -3 : -1;
+			}
 			if (ch == '/')
 				break;
 			if (ch == '.' && *cp == '.')
diff --git a/remote.c b/remote.c
index 46fe8d9..05b16ad 100644
--- a/remote.c
+++ b/remote.c
...
@@ -497,23 +501,48 @@ static struct ref *find_ref_by_name(struct ref *list, const char *name)
...
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 	       int nr_refspec, char **refspec, int all)
 {
 	struct refspec *rs =
 		parse_ref_spec(nr_refspec, (const char **) refspec);
 
-	if (nr_refspec)
-		return match_explicit_refs(src, dst, dst_tail, rs, nr_refspec);
+	if (nr_refspec) {
+		if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
+			return -1;
+	}
Style?  "if (nr_refspec && match_explicit...)" and then you can
lose the excess braces.
 
 	/* pick the remainder */
 	for ( ; src; src = src->next) {
 		struct ref *dst_peer;
 		if (src->peer_ref)
 			continue;
+		if (!check_pattern_match(rs, nr_refspec, src))
+			continue;
+
 		dst_peer = find_ref_by_name(dst, src->name);
-		if ((dst_peer && dst_peer->peer_ref) || (!dst_peer && !all))
+		if (dst_peer && dst_peer->peer_ref) {
+			/* We're already sending something to this ref. */
+			continue;
+		}
+		if (!dst_peer && !nr_refspec && !all) {
+			/* Remote doesn't have it, and we have no
+			 * explicit pattern, and we don't have
+			 * --all. */
 			continue;
+		}
 		if (!dst_peer) {
 			/* Create a new one and link it */
 			int len = strlen(src->name) + 1;
Style?  Excess braces...
quoted hunk
diff --git a/send-pack.c b/send-pack.c
index 59352c8..697dbbc 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -354,6 +354,7 @@ static void verify_remote_names(int nr_heads, char **heads)
 		case -2: /* ok but a single level -- that is fine for
 			  * a match pattern.
 			  */
+		case -3: /* ok but ends with a pattern-match character */
 			continue;
 		}
 		die("remote part of refspec is not a valid name in %s",
I am not sure what is going on here.  Your new code returns -3
when the pattern has any metacharacter at the end, and
metacharacter in the middle gives -1.  Does that mean the code
would say "alright, that is a pattern" when it sees "refs/heads/foo["?

I think we can go two ways.

 (1) Although the current code does not support it, the intent
     for the globbing refspec "refs/*:refs/remotes/origin/*" was
     to allow "refs/heads/[a-z]*:refs/remotes/origin/[a-z]*" (I
     am not sure about the RHS, but it should be clear that what
     is intended is "grab only the ones that begin with [a-z]
     and track" in that example).  If we were to eventually do
     this, I think check_ref_format() should probably be a bit
     more careful when parsing glob() patterns (e.g. matching
     bra-ket).
     
 (2) As my uncertainty about the RHS above shows, we may not
     support more general glob patterns and stay with only the
     trailing "/*".  At least that is what we have now.  Maybe
     check_ref_format should return "good but ends with meta"
     only when the refspec consists of all good ref_char
     followed by "/*" at the end.

My current preference is the latter.

Re: [PATCH] Move refspec pattern matching to match_refs().

From: Daniel Barkalow <hidden>
Date: 2016-06-15 22:43:10

On Wed, 16 May 2007, Junio C Hamano wrote:
Daniel Barkalow [off-list ref] writes:
quoted
This means that send-pack and http-push will support pattern refspecs,
so builtin-push.c doesn't have to expand them, and also git push can
just turn --tags into "refs/tags/*", further simplifying builtin-push.c
Nice.
quoted
@@ -266,5 +174,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
 		usage(push_usage);
 	}
 	set_refspecs(argv + i, argc - i);
+	if (all && refspec)
+		usage(push_usage);
+
 	return do_push(repo);
 }
Is this hunk an independent bugfix?  I think send-pack has its
own check but I guess http-push lacked its own check?
This replaces the die() in expand_refspecs(), which was at the end of 
set_refspecs(). I think the idea is that "git push --all foo bar" isn't a 
consistancy problem, but it suggests that the user is confused, and so the 
error should be up front if there is one.
quoted
diff --git a/refs.c b/refs.c
index 2ae3235..cd63f37 100644
--- a/refs.c
+++ b/refs.c
@@ -603,15 +603,18 @@ int get_ref_sha1(const char *ref, unsigned char *sha1)
 
 static inline int bad_ref_char(int ch)
 {
-	return (((unsigned) ch) <= ' ' ||
-		ch == '~' || ch == '^' || ch == ':' ||
-		/* 2.13 Pattern Matching Notation */
-		ch == '?' || ch == '*' || ch == '[');
+	if (((unsigned) ch) <= ' ' ||
+	    ch == '~' || ch == '^' || ch == ':')
+		return 1;
+	/* 2.13 Pattern Matching Notation */
+	if (ch == '?' || ch == '*' || ch == '[')
+		return 2;
+	return 0;
 }
 
 int check_ref_format(const char *ref)
 {
-	int ch, level;
+	int ch, level, bad_type;
 	const char *cp = ref;
 
 	level = 0;
@@ -622,13 +625,19 @@ int check_ref_format(const char *ref)
 			return -1; /* should not end with slashes */
 
 		/* we are at the beginning of the path component */
-		if (ch == '.' || bad_ref_char(ch))
+		if (ch == '.')
 			return -1;
+		bad_type = bad_ref_char(ch);
+		if (bad_type) {
+			return (bad_type == 2 && !*cp) ? -3 : -1;
+		}
 
 		/* scan the rest of the path component */
 		while ((ch = *cp++) != 0) {
-			if (bad_ref_char(ch))
-				return -1;
+			bad_type = bad_ref_char(ch);
+			if (bad_type) {
+				return (bad_type == 2 && !*cp) ? -3 : -1;
+			}
 			if (ch == '/')
 				break;
 			if (ch == '.' && *cp == '.')
diff --git a/remote.c b/remote.c
index 46fe8d9..05b16ad 100644
--- a/remote.c
+++ b/remote.c
...
@@ -497,23 +501,48 @@ static struct ref *find_ref_by_name(struct ref *list, const char *name)
...
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 	       int nr_refspec, char **refspec, int all)
 {
 	struct refspec *rs =
 		parse_ref_spec(nr_refspec, (const char **) refspec);
 
-	if (nr_refspec)
-		return match_explicit_refs(src, dst, dst_tail, rs, nr_refspec);
+	if (nr_refspec) {
+		if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
+			return -1;
+	}
Style?  "if (nr_refspec && match_explicit...)" and then you can
lose the excess braces.
Actually, just "if (match_explicit(...))" is fine. It'll do nothing and 
return 0 if !nr_refspec.
quoted
 	/* pick the remainder */
 	for ( ; src; src = src->next) {
 		struct ref *dst_peer;
 		if (src->peer_ref)
 			continue;
+		if (!check_pattern_match(rs, nr_refspec, src))
+			continue;
+
 		dst_peer = find_ref_by_name(dst, src->name);
-		if ((dst_peer && dst_peer->peer_ref) || (!dst_peer && !all))
+		if (dst_peer && dst_peer->peer_ref) {
+			/* We're already sending something to this ref. */
+			continue;
+		}
+		if (!dst_peer && !nr_refspec && !all) {
+			/* Remote doesn't have it, and we have no
+			 * explicit pattern, and we don't have
+			 * --all. */
 			continue;
+		}
 		if (!dst_peer) {
 			/* Create a new one and link it */
 			int len = strlen(src->name) + 1;
Style?  Excess braces...
A comment doesn't count as a second "thing" to be in a conditional for the 
purposes of style? Multiple equally-indented lines without braces 
distracts me with thinking that the actual statement might be misindented.
I am not sure what is going on here.  Your new code returns -3
when the pattern has any metacharacter at the end, and
metacharacter in the middle gives -1.  Does that mean the code
would say "alright, that is a pattern" when it sees "refs/heads/foo["?

I think we can go two ways.

 (1) Although the current code does not support it, the intent
     for the globbing refspec "refs/*:refs/remotes/origin/*" was
     to allow "refs/heads/[a-z]*:refs/remotes/origin/[a-z]*" (I
     am not sure about the RHS, but it should be clear that what
     is intended is "grab only the ones that begin with [a-z]
     and track" in that example).  If we were to eventually do
     this, I think check_ref_format() should probably be a bit
     more careful when parsing glob() patterns (e.g. matching
     bra-ket).
     
 (2) As my uncertainty about the RHS above shows, we may not
     support more general glob patterns and stay with only the
     trailing "/*".  At least that is what we have now.  Maybe
     check_ref_format should return "good but ends with meta"
     only when the refspec consists of all good ref_char
     followed by "/*" at the end.

My current preference is the latter.
The latter is probably the way to go for now. But as far as I can tell, 
refs/heads/db-*:refs/heads/* is currently supported, too.

So, for (2), I'd make bad_ref_char only return 2 for '*', and return 1 for 
'?' and '['.

We can let more stuff get through if we make the parser able to parse it.

	-Daniel
*This .sig left intentionally blank*
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help