Skip to content

Commit

Permalink
git check-ref-format: add options --allow-onelevel and --refspec-pattern
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael Haggerty authored and Junio C Hamano committed Oct 5, 2011
1 parent f9b1a5b commit e4ed610
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 21 deletions.
29 changes: 24 additions & 5 deletions Documentation/git-check-ref-format.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 `.`.

Expand Down Expand Up @@ -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
--------

Expand Down
56 changes: 47 additions & 9 deletions builtin/check-ref-format.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>";

/*
Expand Down Expand Up @@ -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;
}
88 changes: 81 additions & 7 deletions t/t1402-check-ref-format.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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) &&
Expand Down

0 comments on commit e4ed610

Please sign in to comment.