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>
This commit is contained in:
parent
f9b1a5b9b8
commit
e4ed6105ec
@ -8,8 +8,8 @@ git-check-ref-format - Ensures that a reference name is well formed
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git check-ref-format' <refname>
|
||||
'git check-ref-format' --print <refname>
|
||||
'git check-ref-format' [--print]
|
||||
[--[no-]allow-onelevel] [--refspec-pattern] <refname>
|
||||
'git check-ref-format' --branch <branchname-shorthand>
|
||||
|
||||
DESCRIPTION
|
||||
@ -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
|
||||
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 ASCII control characters (i.e. bytes whose
|
||||
values are lower than \040, or \177 `DEL`), space, tilde `~`,
|
||||
caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
|
||||
or open bracket `[` anywhere.
|
||||
caret `{caret}`, or colon `:` 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 `.`.
|
||||
|
||||
@ -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
|
||||
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
|
||||
--------
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "strbuf.h"
|
||||
|
||||
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>";
|
||||
|
||||
/*
|
||||
@ -45,27 +45,65 @@ static int check_ref_format_branch(const char *arg)
|
||||
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);
|
||||
|
||||
if (check_ref_format(arg))
|
||||
return 1;
|
||||
collapse_slashes(refname, arg);
|
||||
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 i;
|
||||
int print = 0;
|
||||
int flags = 0;
|
||||
|
||||
if (argc == 2 && !strcmp(argv[1], "-h"))
|
||||
usage(builtin_check_ref_format_usage);
|
||||
|
||||
if (argc == 3 && !strcmp(argv[1], "--branch"))
|
||||
return check_ref_format_branch(argv[2]);
|
||||
if (argc == 3 && !strcmp(argv[1], "--print"))
|
||||
return check_ref_format_print(argv[2]);
|
||||
if (argc != 2)
|
||||
|
||||
for (i = 1; i < argc && argv[i][0] == '-'; i++) {
|
||||
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);
|
||||
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'
|
||||
. ./test-lib.sh
|
||||
|
||||
valid_ref() {
|
||||
test_expect_success "ref name '$1' is valid" \
|
||||
"git check-ref-format '$1'"
|
||||
if test "$#" = 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() {
|
||||
test_expect_success "ref name '$1' is not valid" \
|
||||
"test_must_fail git check-ref-format '$1'"
|
||||
if test "$#" = 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 '/'
|
||||
valid_ref 'heads/foo'
|
||||
invalid_ref 'foo'
|
||||
invalid_ref '/' --allow-onelevel
|
||||
valid_ref 'foo/bar/baz'
|
||||
valid_ref 'refs///heads/foo'
|
||||
invalid_ref 'heads/foo/'
|
||||
valid_ref '/heads/foo'
|
||||
valid_ref '///heads/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 'heads/foo..bar'
|
||||
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\177')"
|
||||
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}" '
|
||||
T=$(git write-tree) &&
|
||||
|
Loading…
Reference in New Issue
Block a user