提交 e4ed6105 编写于 作者: M Michael Haggerty 提交者: Junio C Hamano

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: NMichael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: NJunio C Hamano <gitster@pobox.com>
上级 f9b1a5b9
...@@ -8,8 +8,8 @@ git-check-ref-format - Ensures that a reference name is well formed ...@@ -8,8 +8,8 @@ git-check-ref-format - Ensures that a reference name is well formed
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git check-ref-format' <refname> 'git check-ref-format' [--print]
'git check-ref-format' --print <refname> [--[no-]allow-onelevel] [--refspec-pattern] <refname>
'git check-ref-format' --branch <branchname-shorthand> 'git check-ref-format' --branch <branchname-shorthand>
DESCRIPTION DESCRIPTION
...@@ -32,14 +32,18 @@ git imposes the following rules on how references are named: ...@@ -32,14 +32,18 @@ git imposes the following rules on how references are named:
. They must contain at least one `/`. This enforces the presence of a . They must contain at least one `/`. This enforces the presence of a
category like `heads/`, `tags/` etc. but the actual names are not category like `heads/`, `tags/` etc. but the actual names are not
restricted. restricted. If the `--allow-onelevel` option is used, this rule
is waived.
. They cannot have two consecutive dots `..` anywhere. . They cannot have two consecutive dots `..` anywhere.
. They cannot have ASCII control characters (i.e. bytes whose . They cannot have ASCII control characters (i.e. bytes whose
values are lower than \040, or \177 `DEL`), space, tilde `~`, values are lower than \040, or \177 `DEL`), space, tilde `~`,
caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`, caret `{caret}`, or colon `:` anywhere.
or open bracket `[` anywhere.
. They cannot have question-mark `?`, asterisk `{asterisk}`, or open
bracket `[` anywhere. See the `--refspec-pattern` option below for
an exception to this rule.
. They cannot end with a slash `/` nor a dot `.`. . They cannot end with a slash `/` nor a dot `.`.
...@@ -78,6 +82,21 @@ were on. This option should be used by porcelains to accept this ...@@ -78,6 +82,21 @@ were on. This option should be used by porcelains to accept this
syntax anywhere a branch name is expected, so they can act as if you syntax anywhere a branch name is expected, so they can act as if you
typed the branch name. typed the branch name.
OPTIONS
-------
--allow-onelevel::
--no-allow-onelevel::
Controls whether one-level refnames are accepted (i.e.,
refnames that do not contain multiple `/`-separated
components). The default is `--no-allow-onelevel`.
--refspec-pattern::
Interpret <refname> as a reference name pattern for a refspec
(as used with remote repositories). If this option is
enabled, <refname> is allowed to contain a single `{asterisk}`
in place of a one full pathname component (e.g.,
`foo/{asterisk}/bar` but not `foo/bar{asterisk}`).
EXAMPLES EXAMPLES
-------- --------
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include "strbuf.h" #include "strbuf.h"
static const char builtin_check_ref_format_usage[] = static const char builtin_check_ref_format_usage[] =
"git check-ref-format [--print] <refname>\n" "git check-ref-format [--print] [options] <refname>\n"
" or: git check-ref-format --branch <branchname-shorthand>"; " or: git check-ref-format --branch <branchname-shorthand>";
/* /*
...@@ -45,27 +45,65 @@ static int check_ref_format_branch(const char *arg) ...@@ -45,27 +45,65 @@ static int check_ref_format_branch(const char *arg)
return 0; return 0;
} }
static int check_ref_format_print(const char *arg) static void refname_format_print(const char *arg)
{ {
char *refname = xmalloc(strlen(arg) + 1); char *refname = xmalloc(strlen(arg) + 1);
if (check_ref_format(arg))
return 1;
collapse_slashes(refname, arg); collapse_slashes(refname, arg);
printf("%s\n", refname); printf("%s\n", refname);
return 0;
} }
#define REFNAME_ALLOW_ONELEVEL 1
#define REFNAME_REFSPEC_PATTERN 2
int cmd_check_ref_format(int argc, const char **argv, const char *prefix) int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
{ {
int i;
int print = 0;
int flags = 0;
if (argc == 2 && !strcmp(argv[1], "-h")) if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_check_ref_format_usage); usage(builtin_check_ref_format_usage);
if (argc == 3 && !strcmp(argv[1], "--branch")) if (argc == 3 && !strcmp(argv[1], "--branch"))
return check_ref_format_branch(argv[2]); return check_ref_format_branch(argv[2]);
if (argc == 3 && !strcmp(argv[1], "--print"))
return check_ref_format_print(argv[2]); for (i = 1; i < argc && argv[i][0] == '-'; i++) {
if (argc != 2) if (!strcmp(argv[i], "--print"))
print = 1;
else if (!strcmp(argv[i], "--allow-onelevel"))
flags |= REFNAME_ALLOW_ONELEVEL;
else if (!strcmp(argv[i], "--no-allow-onelevel"))
flags &= ~REFNAME_ALLOW_ONELEVEL;
else if (!strcmp(argv[i], "--refspec-pattern"))
flags |= REFNAME_REFSPEC_PATTERN;
else
usage(builtin_check_ref_format_usage);
}
if (! (i == argc - 1))
usage(builtin_check_ref_format_usage); usage(builtin_check_ref_format_usage);
return !!check_ref_format(argv[1]);
switch (check_ref_format(argv[i])) {
case CHECK_REF_FORMAT_OK:
break;
case CHECK_REF_FORMAT_ERROR:
return 1;
case CHECK_REF_FORMAT_ONELEVEL:
if (!(flags & REFNAME_ALLOW_ONELEVEL))
return 1;
else
break;
case CHECK_REF_FORMAT_WILDCARD:
if (!(flags & REFNAME_REFSPEC_PATTERN))
return 1;
else
break;
default:
die("internal error: unexpected value from check_ref_format()");
}
if (print)
refname_format_print(argv[i]);
return 0;
} }
...@@ -5,25 +5,38 @@ test_description='Test git check-ref-format' ...@@ -5,25 +5,38 @@ test_description='Test git check-ref-format'
. ./test-lib.sh . ./test-lib.sh
valid_ref() { valid_ref() {
test_expect_success "ref name '$1' is valid" \ if test "$#" = 1
"git check-ref-format '$1'" then
test_expect_success "ref name '$1' is valid" \
"git check-ref-format '$1'"
else
test_expect_success "ref name '$1' is valid with options $2" \
"git check-ref-format $2 '$1'"
fi
} }
invalid_ref() { invalid_ref() {
test_expect_success "ref name '$1' is not valid" \ if test "$#" = 1
"test_must_fail git check-ref-format '$1'" then
test_expect_success "ref name '$1' is invalid" \
"test_must_fail git check-ref-format '$1'"
else
test_expect_success "ref name '$1' is invalid with options $2" \
"test_must_fail git check-ref-format $2 '$1'"
fi
} }
invalid_ref '' invalid_ref ''
invalid_ref '/' invalid_ref '/'
valid_ref 'heads/foo' invalid_ref '/' --allow-onelevel
invalid_ref 'foo'
valid_ref 'foo/bar/baz' valid_ref 'foo/bar/baz'
valid_ref 'refs///heads/foo' valid_ref 'refs///heads/foo'
invalid_ref 'heads/foo/' invalid_ref 'heads/foo/'
valid_ref '/heads/foo' valid_ref '/heads/foo'
valid_ref '///heads/foo' valid_ref '///heads/foo'
invalid_ref '/foo'
invalid_ref './foo' invalid_ref './foo'
invalid_ref './foo/bar'
invalid_ref 'foo/./bar'
invalid_ref 'foo/bar/.'
invalid_ref '.refs/foo' invalid_ref '.refs/foo'
invalid_ref 'heads/foo..bar' invalid_ref 'heads/foo..bar'
invalid_ref 'heads/foo?bar' invalid_ref 'heads/foo?bar'
...@@ -38,6 +51,67 @@ invalid_ref 'heads/foo\bar' ...@@ -38,6 +51,67 @@ invalid_ref 'heads/foo\bar'
invalid_ref "$(printf 'heads/foo\t')" invalid_ref "$(printf 'heads/foo\t')"
invalid_ref "$(printf 'heads/foo\177')" invalid_ref "$(printf 'heads/foo\177')"
valid_ref "$(printf 'heads/fu\303\237')" valid_ref "$(printf 'heads/fu\303\237')"
invalid_ref 'heads/*foo/bar' --refspec-pattern
invalid_ref 'heads/foo*/bar' --refspec-pattern
invalid_ref 'heads/f*o/bar' --refspec-pattern
ref='foo'
invalid_ref "$ref"
valid_ref "$ref" --allow-onelevel
invalid_ref "$ref" --refspec-pattern
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='foo/bar'
valid_ref "$ref"
valid_ref "$ref" --allow-onelevel
valid_ref "$ref" --refspec-pattern
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='foo/*'
invalid_ref "$ref"
invalid_ref "$ref" --allow-onelevel
valid_ref "$ref" --refspec-pattern
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='*/foo'
invalid_ref "$ref"
invalid_ref "$ref" --allow-onelevel
valid_ref "$ref" --refspec-pattern
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='foo/*/bar'
invalid_ref "$ref"
invalid_ref "$ref" --allow-onelevel
valid_ref "$ref" --refspec-pattern
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='*'
invalid_ref "$ref"
#invalid_ref "$ref" --allow-onelevel
test_expect_failure "ref name '$ref' is invalid with options --allow-onelevel" \
"test_must_fail git check-ref-format --allow-onelevel '$ref'"
invalid_ref "$ref" --refspec-pattern
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='foo/*/*'
invalid_ref "$ref" --refspec-pattern
invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='*/foo/*'
invalid_ref "$ref" --refspec-pattern
invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='*/*/foo'
invalid_ref "$ref" --refspec-pattern
invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
ref='/foo'
invalid_ref "$ref"
valid_ref "$ref" --allow-onelevel
invalid_ref "$ref" --refspec-pattern
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
test_expect_success "check-ref-format --branch @{-1}" ' test_expect_success "check-ref-format --branch @{-1}" '
T=$(git write-tree) && T=$(git write-tree) &&
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册