Skip to content

Commit 8d3981c

Browse files
committed
Merge branch 'jk/refspec-parse-wildcard'
Allow an asterisk as a substring (as opposed to the entirety) of a path component for both side of a refspec, e.g. "refs/heads/o*:refs/remotes/heads/i*". * jk/refspec-parse-wildcard: refs: loosen restriction on wildcard "*" refspecs refs: cleanup comments regarding check_refname_component()
2 parents 7a06e63 + cd377f4 commit 8d3981c

File tree

5 files changed

+41
-30
lines changed

5 files changed

+41
-30
lines changed

Documentation/git-check-ref-format.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ OPTIONS
9494
Interpret <refname> as a reference name pattern for a refspec
9595
(as used with remote repositories). If this option is
9696
enabled, <refname> is allowed to contain a single `*`
97-
in place of a one full pathname component (e.g.,
98-
`foo/*/bar` but not `foo/bar*`).
97+
in the refspec (e.g., `foo/bar*/baz` or `foo/bar*baz/`
98+
but not `foo/bar*/baz*`).
9999

100100
--normalize::
101101
Normalize 'refname' by removing any leading slash (`/`)

refs.c

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ struct ref_lock {
1919
* 1: End-of-component
2020
* 2: ., look for a preceding . to reject .. in refs
2121
* 3: {, look for a preceding @ to reject @{ in refs
22-
* 4: A bad character: ASCII control characters, "~", "^", ":" or SP
22+
* 4: A bad character: ASCII control characters, and
23+
* ":", "?", "[", "\", "^", "~", SP, or TAB
24+
* 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
2325
*/
2426
static unsigned char refname_disposition[256] = {
2527
1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
2628
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
27-
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
29+
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
2830
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
2931
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3032
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
@@ -75,12 +77,14 @@ static unsigned char refname_disposition[256] = {
7577
*
7678
* - any path component of it begins with ".", or
7779
* - it has double dots "..", or
78-
* - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
79-
* - it ends with a "/".
80-
* - it ends with ".lock"
81-
* - it contains a "\" (backslash)
80+
* - it has ASCII control characters, or
81+
* - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
82+
* - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
83+
* - it ends with a "/", or
84+
* - it ends with ".lock", or
85+
* - it contains a "@{" portion
8286
*/
83-
static int check_refname_component(const char *refname, int flags)
87+
static int check_refname_component(const char *refname, int *flags)
8488
{
8589
const char *cp;
8690
char last = '\0';
@@ -101,6 +105,16 @@ static int check_refname_component(const char *refname, int flags)
101105
break;
102106
case 4:
103107
return -1;
108+
case 5:
109+
if (!(*flags & REFNAME_REFSPEC_PATTERN))
110+
return -1; /* refspec can't be a pattern */
111+
112+
/*
113+
* Unset the pattern flag so that we only accept
114+
* a single asterisk for one side of refspec.
115+
*/
116+
*flags &= ~ REFNAME_REFSPEC_PATTERN;
117+
break;
104118
}
105119
last = ch;
106120
}
@@ -125,18 +139,10 @@ int check_refname_format(const char *refname, int flags)
125139

126140
while (1) {
127141
/* We are at the start of a path component. */
128-
component_len = check_refname_component(refname, flags);
129-
if (component_len <= 0) {
130-
if ((flags & REFNAME_REFSPEC_PATTERN) &&
131-
refname[0] == '*' &&
132-
(refname[1] == '\0' || refname[1] == '/')) {
133-
/* Accept one wildcard as a full refname component. */
134-
flags &= ~REFNAME_REFSPEC_PATTERN;
135-
component_len = 1;
136-
} else {
137-
return -1;
138-
}
139-
}
142+
component_len = check_refname_component(refname, &flags);
143+
if (component_len <= 0)
144+
return -1;
145+
140146
component_count++;
141147
if (refname[component_len] == '\0')
142148
break;

refs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,8 @@ extern int for_each_reflog(each_ref_fn, void *);
279279
* to the rules described in Documentation/git-check-ref-format.txt.
280280
* If REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
281281
* reference names. If REFNAME_REFSPEC_PATTERN is set in flags, then
282-
* allow a "*" wildcard character in place of one of the name
283-
* components. No leading or repeated slashes are accepted.
282+
* allow a single "*" wildcard character in the refspec. No leading or
283+
* repeated slashes are accepted.
284284
*/
285285
extern int check_refname_format(const char *refname, int flags);
286286

t/t1402-check-ref-format.sh

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ invalid_ref 'heads/foo\bar'
6262
invalid_ref "$(printf 'heads/foo\t')"
6363
invalid_ref "$(printf 'heads/foo\177')"
6464
valid_ref "$(printf 'heads/fu\303\237')"
65-
invalid_ref 'heads/*foo/bar' --refspec-pattern
66-
invalid_ref 'heads/foo*/bar' --refspec-pattern
67-
invalid_ref 'heads/f*o/bar' --refspec-pattern
65+
valid_ref 'heads/*foo/bar' --refspec-pattern
66+
valid_ref 'heads/foo*/bar' --refspec-pattern
67+
valid_ref 'heads/f*o/bar' --refspec-pattern
68+
invalid_ref 'heads/f*o*/bar' --refspec-pattern
69+
invalid_ref 'heads/foo*/bar*' --refspec-pattern
6870

6971
ref='foo'
7072
invalid_ref "$ref"

t/t5511-refspec.sh

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,18 @@ test_refspec fetch ':refs/remotes/frotz/HEAD-to-me'
7171
test_refspec push ':refs/remotes/frotz/delete me' invalid
7272
test_refspec fetch ':refs/remotes/frotz/HEAD to me' invalid
7373

74-
test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
75-
test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
74+
test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah'
75+
test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah'
7676

77-
test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
78-
test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
77+
test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*'
78+
test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*'
7979

8080
test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
8181
test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
8282

83+
test_refspec fetch 'refs/heads/*g*/for-linus:refs/remotes/mine/*' invalid
84+
test_refspec push 'refs/heads/*g*/for-linus:refs/remotes/mine/*' invalid
85+
8386
test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*'
8487
test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*'
8588

0 commit comments

Comments
 (0)