Skip to content

Commit cd377f4

Browse files
jacob-kellergitster
authored andcommitted
refs: loosen restriction on wildcard "*" refspecs
Loosen restrictions on refspecs by allowing patterns that have a "*" within a component instead of only as the whole component. Remove the logic to accept a single "*" as a whole component from check_refname_format(), and implement an extended form of that logic in check_refname_component(). Pass the pointer to the flags argument to the latter, as it has to clear REFNAME_REFSPEC_PATTERN bit when it sees "*". Teach check_refname_component() function to allow an asterisk "*" only when REFNAME_REFSPEC_PATTERN is set in the flags, and drop the bit after seeing a "*", to ensure that one side of a refspec contains at most one asterisk. This will allow us to accept refspecs such as `for/bar*:foo/baz*`. Any refspec which functioned before shall continue functioning with the new logic. Signed-off-by: Jacob Keller <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 53a8555 commit cd377f4

File tree

5 files changed

+36
-27
lines changed

5 files changed

+36
-27
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: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ struct ref_lock {
2121
* 2: ., look for a preceding . to reject .. in refs
2222
* 3: {, look for a preceding @ to reject @{ in refs
2323
* 4: A bad character: ASCII control characters, and
24-
* "*", ":", "?", "[", "\", "^", "~", SP, or TAB
24+
* ":", "?", "[", "\", "^", "~", SP, or TAB
25+
* 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
2526
*/
2627
static unsigned char refname_disposition[256] = {
2728
1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
2829
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
29-
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
30+
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
3031
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
3132
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3233
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
@@ -73,12 +74,13 @@ static unsigned char refname_disposition[256] = {
7374
* - any path component of it begins with ".", or
7475
* - it has double dots "..", or
7576
* - it has ASCII control characters, or
76-
* - it has "*", ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
77+
* - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
78+
* - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
7779
* - it ends with a "/", or
7880
* - it ends with ".lock", or
7981
* - it contains a "@{" portion
8082
*/
81-
static int check_refname_component(const char *refname, int flags)
83+
static int check_refname_component(const char *refname, int *flags)
8284
{
8385
const char *cp;
8486
char last = '\0';
@@ -99,6 +101,16 @@ static int check_refname_component(const char *refname, int flags)
99101
break;
100102
case 4:
101103
return -1;
104+
case 5:
105+
if (!(*flags & REFNAME_REFSPEC_PATTERN))
106+
return -1; /* refspec can't be a pattern */
107+
108+
/*
109+
* Unset the pattern flag so that we only accept
110+
* a single asterisk for one side of refspec.
111+
*/
112+
*flags &= ~ REFNAME_REFSPEC_PATTERN;
113+
break;
102114
}
103115
last = ch;
104116
}
@@ -123,18 +135,10 @@ int check_refname_format(const char *refname, int flags)
123135

124136
while (1) {
125137
/* We are at the start of a path component. */
126-
component_len = check_refname_component(refname, flags);
127-
if (component_len <= 0) {
128-
if ((flags & REFNAME_REFSPEC_PATTERN) &&
129-
refname[0] == '*' &&
130-
(refname[1] == '\0' || refname[1] == '/')) {
131-
/* Accept one wildcard as a full refname component. */
132-
flags &= ~REFNAME_REFSPEC_PATTERN;
133-
component_len = 1;
134-
} else {
135-
return -1;
136-
}
137-
}
138+
component_len = check_refname_component(refname, &flags);
139+
if (component_len <= 0)
140+
return -1;
141+
138142
component_count++;
139143
if (refname[component_len] == '\0')
140144
break;

refs.h

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

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)