Skip to content

Commit e4ed610

Browse files
mhaggergitster
authored andcommitted
git check-ref-format: add options --allow-onelevel and --refspec-pattern
Also add tests of the new options. (Actually, one big reason to add the new options is to make it easy to test check_ref_format(), though the options should also be useful to other scripts.) Interpret the result of check_ref_format() based on which types of refnames are allowed. However, because check_ref_format() can only return a single value, one test case is still broken. Specifically, the case "git check-ref-format --onelevel '*'" incorrectly succeeds because check_ref_format() returns CHECK_REF_FORMAT_ONELEVEL for this refname even though the refname is also CHECK_REF_FORMAT_WILDCARD. The type of check that leads to this failure is used elsewhere in "real" code and could lead to bugs; it will be fixed over the next few commits. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent f9b1a5b commit e4ed610

File tree

3 files changed

+152
-21
lines changed

3 files changed

+152
-21
lines changed

Documentation/git-check-ref-format.txt

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ git-check-ref-format - Ensures that a reference name is well formed
88
SYNOPSIS
99
--------
1010
[verse]
11-
'git check-ref-format' <refname>
12-
'git check-ref-format' --print <refname>
11+
'git check-ref-format' [--print]
12+
[--[no-]allow-onelevel] [--refspec-pattern] <refname>
1313
'git check-ref-format' --branch <branchname-shorthand>
1414

1515
DESCRIPTION
@@ -32,14 +32,18 @@ git imposes the following rules on how references are named:
3232

3333
. They must contain at least one `/`. This enforces the presence of a
3434
category like `heads/`, `tags/` etc. but the actual names are not
35-
restricted.
35+
restricted. If the `--allow-onelevel` option is used, this rule
36+
is waived.
3637

3738
. They cannot have two consecutive dots `..` anywhere.
3839

3940
. They cannot have ASCII control characters (i.e. bytes whose
4041
values are lower than \040, or \177 `DEL`), space, tilde `~`,
41-
caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
42-
or open bracket `[` anywhere.
42+
caret `{caret}`, or colon `:` anywhere.
43+
44+
. They cannot have question-mark `?`, asterisk `{asterisk}`, or open
45+
bracket `[` anywhere. See the `--refspec-pattern` option below for
46+
an exception to this rule.
4347

4448
. They cannot end with a slash `/` nor a dot `.`.
4549

@@ -78,6 +82,21 @@ were on. This option should be used by porcelains to accept this
7882
syntax anywhere a branch name is expected, so they can act as if you
7983
typed the branch name.
8084

85+
OPTIONS
86+
-------
87+
--allow-onelevel::
88+
--no-allow-onelevel::
89+
Controls whether one-level refnames are accepted (i.e.,
90+
refnames that do not contain multiple `/`-separated
91+
components). The default is `--no-allow-onelevel`.
92+
93+
--refspec-pattern::
94+
Interpret <refname> as a reference name pattern for a refspec
95+
(as used with remote repositories). If this option is
96+
enabled, <refname> is allowed to contain a single `{asterisk}`
97+
in place of a one full pathname component (e.g.,
98+
`foo/{asterisk}/bar` but not `foo/bar{asterisk}`).
99+
81100
EXAMPLES
82101
--------
83102

builtin/check-ref-format.c

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "strbuf.h"
99

1010
static const char builtin_check_ref_format_usage[] =
11-
"git check-ref-format [--print] <refname>\n"
11+
"git check-ref-format [--print] [options] <refname>\n"
1212
" or: git check-ref-format --branch <branchname-shorthand>";
1313

1414
/*
@@ -45,27 +45,65 @@ static int check_ref_format_branch(const char *arg)
4545
return 0;
4646
}
4747

48-
static int check_ref_format_print(const char *arg)
48+
static void refname_format_print(const char *arg)
4949
{
5050
char *refname = xmalloc(strlen(arg) + 1);
5151

52-
if (check_ref_format(arg))
53-
return 1;
5452
collapse_slashes(refname, arg);
5553
printf("%s\n", refname);
56-
return 0;
5754
}
5855

56+
#define REFNAME_ALLOW_ONELEVEL 1
57+
#define REFNAME_REFSPEC_PATTERN 2
58+
5959
int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
6060
{
61+
int i;
62+
int print = 0;
63+
int flags = 0;
64+
6165
if (argc == 2 && !strcmp(argv[1], "-h"))
6266
usage(builtin_check_ref_format_usage);
6367

6468
if (argc == 3 && !strcmp(argv[1], "--branch"))
6569
return check_ref_format_branch(argv[2]);
66-
if (argc == 3 && !strcmp(argv[1], "--print"))
67-
return check_ref_format_print(argv[2]);
68-
if (argc != 2)
70+
71+
for (i = 1; i < argc && argv[i][0] == '-'; i++) {
72+
if (!strcmp(argv[i], "--print"))
73+
print = 1;
74+
else if (!strcmp(argv[i], "--allow-onelevel"))
75+
flags |= REFNAME_ALLOW_ONELEVEL;
76+
else if (!strcmp(argv[i], "--no-allow-onelevel"))
77+
flags &= ~REFNAME_ALLOW_ONELEVEL;
78+
else if (!strcmp(argv[i], "--refspec-pattern"))
79+
flags |= REFNAME_REFSPEC_PATTERN;
80+
else
81+
usage(builtin_check_ref_format_usage);
82+
}
83+
if (! (i == argc - 1))
6984
usage(builtin_check_ref_format_usage);
70-
return !!check_ref_format(argv[1]);
85+
86+
switch (check_ref_format(argv[i])) {
87+
case CHECK_REF_FORMAT_OK:
88+
break;
89+
case CHECK_REF_FORMAT_ERROR:
90+
return 1;
91+
case CHECK_REF_FORMAT_ONELEVEL:
92+
if (!(flags & REFNAME_ALLOW_ONELEVEL))
93+
return 1;
94+
else
95+
break;
96+
case CHECK_REF_FORMAT_WILDCARD:
97+
if (!(flags & REFNAME_REFSPEC_PATTERN))
98+
return 1;
99+
else
100+
break;
101+
default:
102+
die("internal error: unexpected value from check_ref_format()");
103+
}
104+
105+
if (print)
106+
refname_format_print(argv[i]);
107+
108+
return 0;
71109
}

t/t1402-check-ref-format.sh

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,38 @@ test_description='Test git check-ref-format'
55
. ./test-lib.sh
66

77
valid_ref() {
8-
test_expect_success "ref name '$1' is valid" \
9-
"git check-ref-format '$1'"
8+
if test "$#" = 1
9+
then
10+
test_expect_success "ref name '$1' is valid" \
11+
"git check-ref-format '$1'"
12+
else
13+
test_expect_success "ref name '$1' is valid with options $2" \
14+
"git check-ref-format $2 '$1'"
15+
fi
1016
}
1117
invalid_ref() {
12-
test_expect_success "ref name '$1' is not valid" \
13-
"test_must_fail git check-ref-format '$1'"
18+
if test "$#" = 1
19+
then
20+
test_expect_success "ref name '$1' is invalid" \
21+
"test_must_fail git check-ref-format '$1'"
22+
else
23+
test_expect_success "ref name '$1' is invalid with options $2" \
24+
"test_must_fail git check-ref-format $2 '$1'"
25+
fi
1426
}
1527

1628
invalid_ref ''
1729
invalid_ref '/'
18-
valid_ref 'heads/foo'
19-
invalid_ref 'foo'
30+
invalid_ref '/' --allow-onelevel
2031
valid_ref 'foo/bar/baz'
2132
valid_ref 'refs///heads/foo'
2233
invalid_ref 'heads/foo/'
2334
valid_ref '/heads/foo'
2435
valid_ref '///heads/foo'
25-
invalid_ref '/foo'
2636
invalid_ref './foo'
37+
invalid_ref './foo/bar'
38+
invalid_ref 'foo/./bar'
39+
invalid_ref 'foo/bar/.'
2740
invalid_ref '.refs/foo'
2841
invalid_ref 'heads/foo..bar'
2942
invalid_ref 'heads/foo?bar'
@@ -38,6 +51,67 @@ invalid_ref 'heads/foo\bar'
3851
invalid_ref "$(printf 'heads/foo\t')"
3952
invalid_ref "$(printf 'heads/foo\177')"
4053
valid_ref "$(printf 'heads/fu\303\237')"
54+
invalid_ref 'heads/*foo/bar' --refspec-pattern
55+
invalid_ref 'heads/foo*/bar' --refspec-pattern
56+
invalid_ref 'heads/f*o/bar' --refspec-pattern
57+
58+
ref='foo'
59+
invalid_ref "$ref"
60+
valid_ref "$ref" --allow-onelevel
61+
invalid_ref "$ref" --refspec-pattern
62+
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
63+
64+
ref='foo/bar'
65+
valid_ref "$ref"
66+
valid_ref "$ref" --allow-onelevel
67+
valid_ref "$ref" --refspec-pattern
68+
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
69+
70+
ref='foo/*'
71+
invalid_ref "$ref"
72+
invalid_ref "$ref" --allow-onelevel
73+
valid_ref "$ref" --refspec-pattern
74+
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
75+
76+
ref='*/foo'
77+
invalid_ref "$ref"
78+
invalid_ref "$ref" --allow-onelevel
79+
valid_ref "$ref" --refspec-pattern
80+
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
81+
82+
ref='foo/*/bar'
83+
invalid_ref "$ref"
84+
invalid_ref "$ref" --allow-onelevel
85+
valid_ref "$ref" --refspec-pattern
86+
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
87+
88+
ref='*'
89+
invalid_ref "$ref"
90+
91+
#invalid_ref "$ref" --allow-onelevel
92+
test_expect_failure "ref name '$ref' is invalid with options --allow-onelevel" \
93+
"test_must_fail git check-ref-format --allow-onelevel '$ref'"
94+
95+
invalid_ref "$ref" --refspec-pattern
96+
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
97+
98+
ref='foo/*/*'
99+
invalid_ref "$ref" --refspec-pattern
100+
invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
101+
102+
ref='*/foo/*'
103+
invalid_ref "$ref" --refspec-pattern
104+
invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
105+
106+
ref='*/*/foo'
107+
invalid_ref "$ref" --refspec-pattern
108+
invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
109+
110+
ref='/foo'
111+
invalid_ref "$ref"
112+
valid_ref "$ref" --allow-onelevel
113+
invalid_ref "$ref" --refspec-pattern
114+
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
41115

42116
test_expect_success "check-ref-format --branch @{-1}" '
43117
T=$(git write-tree) &&

0 commit comments

Comments
 (0)