Merge branch 'mh/check-ref-format-3' into next

* mh/check-ref-format-3: (23 commits)
  add_ref(): verify that the refname is formatted correctly
  resolve_ref(): expand documentation
  resolve_ref(): also treat a too-long SHA1 as invalid
  resolve_ref(): emit warnings for improperly-formatted references
  resolve_ref(): verify that the input refname has the right format
  remote: avoid passing NULL to read_ref()
  remote: use xstrdup() instead of strdup()
  resolve_ref(): do not follow incorrectly-formatted symbolic refs
  resolve_ref(): extract a function get_packed_ref()
  resolve_ref(): turn buffer into a proper string as soon as possible
  resolve_ref(): only follow a symlink that contains a valid, normalized refname
  resolve_ref(): use prefixcmp()
  resolve_ref(): explicitly fail if a symlink is not readable
  Change check_refname_format() to reject unnormalized refnames
  Inline function refname_format_print()
  Make collapse_slashes() allocate memory for its result
  Do not allow ".lock" at the end of any refname component
  Refactor check_refname_format()
  Change check_ref_format() to take a flags argument
  Change bad_ref_char() to return a boolean value
  ...
This commit is contained in:
Junio C Hamano 2011-10-06 15:41:54 -07:00
commit c2774985d8
24 changed files with 428 additions and 227 deletions

View file

@ -8,8 +8,9 @@ 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' [--normalize]
'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
@ -28,22 +29,28 @@ git imposes the following rules on how references are named:
. They can include slash `/` for hierarchical (directory) . They can include slash `/` for hierarchical (directory)
grouping, but no slash-separated component can begin with a grouping, but no slash-separated component can begin with a
dot `.`. dot `.` or end with the sequence `.lock`.
. 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 end with a slash `/` nor a dot `.`. . 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 the sequence `.lock`. . They cannot begin or end with a slash `/` or contain multiple
consecutive slashes (see the `--normalize` option below for an
exception to this rule)
. They cannot end with a dot `.`.
. They cannot contain a sequence `@{`. . They cannot contain a sequence `@{`.
@ -68,16 +75,36 @@ reference name expressions (see linkgit:gitrevisions[7]):
. at-open-brace `@{` is used as a notation to access a reflog entry. . at-open-brace `@{` is used as a notation to access a reflog entry.
With the `--print` option, if 'refname' is acceptable, it prints the
canonicalized name of a hypothetical reference with that name. That is,
it prints 'refname' with any extra `/` characters removed.
With the `--branch` option, it expands the ``previous branch syntax'' With the `--branch` option, it expands the ``previous branch syntax''
`@{-n}`. For example, `@{-1}` is a way to refer the last branch you `@{-n}`. For example, `@{-1}` is a way to refer the last branch you
were on. This option should be used by porcelains to accept this 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}`).
--normalize::
Normalize 'refname' by removing any leading slash (`/`)
characters and collapsing runs of adjacent slashes between
name components into a single slash. Iff the normalized
refname is valid then print it to standard output and exit
with a status of 0. (`--print` is a deprecated way to spell
`--normalize`.)
EXAMPLES EXAMPLES
-------- --------
@ -90,7 +117,7 @@ $ git check-ref-format --branch @{-1}
* Determine the reference name to use for a new branch: * Determine the reference name to use for a new branch:
+ +
------------ ------------
$ ref=$(git check-ref-format --print "refs/heads/$newbranch") || $ ref=$(git check-ref-format --normalize "refs/heads/$newbranch") ||
die "we do not like '$newbranch' as a branch name." die "we do not like '$newbranch' as a branch name."
------------ ------------

View file

@ -8,29 +8,32 @@
#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 [--normalize] [options] <refname>\n"
" or: git check-ref-format --branch <branchname-shorthand>"; " or: git check-ref-format --branch <branchname-shorthand>";
/* /*
* Remove leading slashes and replace each run of adjacent slashes in * Return a copy of refname but with leading slashes removed and runs
* src with a single slash, and write the result to dst. * of adjacent slashes replaced with single slashes.
* *
* This function is similar to normalize_path_copy(), but stripped down * This function is similar to normalize_path_copy(), but stripped down
* to meet check_ref_format's simpler needs. * to meet check_ref_format's simpler needs.
*/ */
static void collapse_slashes(char *dst, const char *src) static char *collapse_slashes(const char *refname)
{ {
char *ret = xmalloc(strlen(refname) + 1);
char ch; char ch;
char prev = '/'; char prev = '/';
char *cp = ret;
while ((ch = *src++) != '\0') { while ((ch = *refname++) != '\0') {
if (prev == '/' && ch == prev) if (prev == '/' && ch == prev)
continue; continue;
*dst++ = ch; *cp++ = ch;
prev = ch; prev = ch;
} }
*dst = '\0'; *cp = '\0';
return ret;
} }
static int check_ref_format_branch(const char *arg) static int check_ref_format_branch(const char *arg)
@ -45,27 +48,41 @@ static int check_ref_format_branch(const char *arg)
return 0; return 0;
} }
static int check_ref_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;
}
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 normalize = 0;
int flags = 0;
const char *refname;
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], "--normalize") || !strcmp(argv[i], "--print"))
normalize = 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]);
refname = argv[i];
if (normalize)
refname = collapse_slashes(refname);
if (check_refname_format(refname, flags))
return 1;
if (normalize)
printf("%s\n", refname);
return 0;
} }

View file

@ -871,7 +871,7 @@ static int parse_branchname_arg(int argc, const char **argv,
new->name = arg; new->name = arg;
setup_branch_path(new); setup_branch_path(new);
if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK && if (!check_refname_format(new->path, 0) &&
resolve_ref(new->path, branch_rev, 1, NULL)) resolve_ref(new->path, branch_rev, 1, NULL))
hashcpy(rev, branch_rev); hashcpy(rev, branch_rev);
else else

View file

@ -546,7 +546,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
for (ref = *refs; ref; ref = next) { for (ref = *refs; ref; ref = next) {
next = ref->next; next = ref->next;
if (!memcmp(ref->name, "refs/", 5) && if (!memcmp(ref->name, "refs/", 5) &&
check_ref_format(ref->name + 5)) check_refname_format(ref->name + 5, 0))
; /* trash */ ; /* trash */
else if (args.fetch_all && else if (args.fetch_all &&
(!args.depth || prefixcmp(ref->name, "refs/tags/") )) { (!args.depth || prefixcmp(ref->name, "refs/tags/") )) {

View file

@ -396,7 +396,7 @@ static const char *update(struct command *cmd)
struct ref_lock *lock; struct ref_lock *lock;
/* only refs/... are allowed */ /* only refs/... are allowed */
if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) { if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) {
rp_error("refusing to create funny ref '%s' remotely", name); rp_error("refusing to create funny ref '%s' remotely", name);
return "funny refname"; return "funny refname";
} }

View file

@ -94,7 +94,7 @@ static int replace_object(const char *object_ref, const char *replace_ref,
"refs/replace/%s", "refs/replace/%s",
sha1_to_hex(object)) > sizeof(ref) - 1) sha1_to_hex(object)) > sizeof(ref) - 1)
die("replace ref name too long: %.*s...", 50, ref); die("replace ref name too long: %.*s...", 50, ref);
if (check_ref_format(ref)) if (check_refname_format(ref, 0))
die("'%s' is not a valid ref name.", ref); die("'%s' is not a valid ref name.", ref);
if (!resolve_ref(ref, prev, 1, NULL)) if (!resolve_ref(ref, prev, 1, NULL))

View file

@ -145,7 +145,7 @@ static int exclude_existing(const char *match)
if (strncmp(ref, match, matchlen)) if (strncmp(ref, match, matchlen))
continue; continue;
} }
if (check_ref_format(ref)) { if (check_refname_format(ref, 0)) {
warning("ref '%s' ignored", ref); warning("ref '%s' ignored", ref);
continue; continue;
} }

View file

@ -407,12 +407,12 @@ static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
static int strbuf_check_tag_ref(struct strbuf *sb, const char *name) static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
{ {
if (name[0] == '-') if (name[0] == '-')
return CHECK_REF_FORMAT_ERROR; return -1;
strbuf_reset(sb); strbuf_reset(sb);
strbuf_addf(sb, "refs/tags/%s", name); strbuf_addf(sb, "refs/tags/%s", name);
return check_ref_format(sb->buf); return check_refname_format(sb->buf, 0);
} }
int cmd_tag(int argc, const char **argv, const char *prefix) int cmd_tag(int argc, const char **argv, const char *prefix)

43
cache.h
View file

@ -819,10 +819,51 @@ static inline int get_sha1_with_context(const char *str, unsigned char *sha1, st
{ {
return get_sha1_with_context_1(str, sha1, orc, 0, NULL); return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
} }
/*
* Try to read a SHA1 in hexadecimal format from the 40 characters
* starting at hex. Write the 20-byte result to sha1 in binary form.
* Return 0 on success. Reading stops if a NUL is encountered in the
* input, so it is safe to pass this function an arbitrary
* null-terminated string.
*/
extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1); extern int read_ref(const char *filename, unsigned char *sha1);
extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
/*
* Resolve a reference, recursively following symbolic refererences.
*
* Store the referred-to object's name in sha1 and return the name of
* the non-symbolic reference that ultimately pointed at it. The
* return value, if not NULL, is a pointer into either a static buffer
* or the input ref.
*
* If the reference cannot be resolved to an object, the behavior
* depends on the "reading" argument:
*
* - If reading is set, return NULL.
*
* - If reading is not set, clear sha1 and return the name of the last
* reference name in the chain, which will either be a non-symbolic
* reference or an undefined reference. If this is a prelude to
* "writing" to the ref, the return value is the name of the ref
* that will actually be created or changed.
*
* If flag is non-NULL, set the value that it points to the
* combination of REF_ISPACKED (if the reference was found among the
* packed references) and REF_ISSYMREF (if the initial reference was a
* symbolic reference).
*
* If ref is not a properly-formatted, normalized reference, return
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
* give up and return NULL.
*
* errno is sometimes set on errors, but not always.
*/
extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
extern int interpret_branch_name(const char *str, struct strbuf *); extern int interpret_branch_name(const char *str, struct strbuf *);

View file

@ -22,7 +22,7 @@ static int check_ref(const char *name, int len, unsigned int flags)
len -= 5; len -= 5;
/* REF_NORMAL means that we don't want the magic fake tag refs */ /* REF_NORMAL means that we don't want the magic fake tag refs */
if ((flags & REF_NORMAL) && check_ref_format(name) < 0) if ((flags & REF_NORMAL) && check_refname_format(name, 0))
return 0; return 0;
/* REF_HEADS means that we want regular branch heads */ /* REF_HEADS means that we want regular branch heads */

View file

@ -106,7 +106,7 @@ static char *expand_namespace(const char *raw_namespace)
if (strcmp((*c)->buf, "/") != 0) if (strcmp((*c)->buf, "/") != 0)
strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf); strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf);
strbuf_list_free(components); strbuf_list_free(components);
if (check_ref_format(buf.buf) != CHECK_REF_FORMAT_OK) if (check_refname_format(buf.buf, 0))
die("bad git namespace path \"%s\"", raw_namespace); die("bad git namespace path \"%s\"", raw_namespace);
strbuf_addch(&buf, '/'); strbuf_addch(&buf, '/');
return strbuf_detach(&buf, NULL); return strbuf_detach(&buf, NULL);

View file

@ -722,13 +722,8 @@ static struct branch *new_branch(const char *name)
if (b) if (b)
die("Invalid attempt to create duplicate branch: %s", name); die("Invalid attempt to create duplicate branch: %s", name);
switch (check_ref_format(name)) { if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL))
case 0: break; /* its valid */
case CHECK_REF_FORMAT_ONELEVEL:
break; /* valid, but too few '/', allow anyway */
default:
die("Branch name doesn't conform to GIT standards: %s", name); die("Branch name doesn't conform to GIT standards: %s", name);
}
b = pool_calloc(1, sizeof(struct branch)); b = pool_calloc(1, sizeof(struct branch));
b->name = pool_strdup(name); b->name = pool_strdup(name);

View file

@ -54,7 +54,7 @@ def valid_git_ref (ref_name):
# The following is a reimplementation of the git check-ref-format # The following is a reimplementation of the git check-ref-format
# command. The rules were derived from the git check-ref-format(1) # command. The rules were derived from the git check-ref-format(1)
# manual page. This code should be replaced by a call to # manual page. This code should be replaced by a call to
# check_ref_format() in the git library, when such is available. # check_refname_format() in the git library, when such is available.
if ref_name.endswith('/') or \ if ref_name.endswith('/') or \
ref_name.startswith('.') or \ ref_name.startswith('.') or \
ref_name.count('/.') or \ ref_name.count('/.') or \

10
hex.c
View file

@ -39,7 +39,15 @@ int get_sha1_hex(const char *hex, unsigned char *sha1)
{ {
int i; int i;
for (i = 0; i < 20; i++) { for (i = 0; i < 20; i++) {
unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]); unsigned int val;
/*
* hex[1]=='\0' is caught when val is checked below,
* but if hex[0] is NUL we have to avoid reading
* past the end of the string:
*/
if (!hex[0])
return -1;
val = (hexval(hex[0]) << 4) | hexval(hex[1]);
if (val & ~0xff) if (val & ~0xff)
return -1; return -1;
*sha1++ = val; *sha1++ = val;

View file

@ -570,7 +570,8 @@ int notes_merge(struct notes_merge_options *o,
/* Dereference o->local_ref into local_sha1 */ /* Dereference o->local_ref into local_sha1 */
if (!resolve_ref(o->local_ref, local_sha1, 0, NULL)) if (!resolve_ref(o->local_ref, local_sha1, 0, NULL))
die("Failed to resolve local notes ref '%s'", o->local_ref); die("Failed to resolve local notes ref '%s'", o->local_ref);
else if (!check_ref_format(o->local_ref) && is_null_sha1(local_sha1)) else if (!check_refname_format(o->local_ref, 0) &&
is_null_sha1(local_sha1))
local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */ local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */
else if (!(local = lookup_commit_reference(local_sha1))) else if (!(local = lookup_commit_reference(local_sha1)))
die("Could not parse local commit %s (%s)", die("Could not parse local commit %s (%s)",
@ -583,7 +584,7 @@ int notes_merge(struct notes_merge_options *o,
* Failed to get remote_sha1. If o->remote_ref looks like an * Failed to get remote_sha1. If o->remote_ref looks like an
* unborn ref, perform the merge using an empty notes tree. * unborn ref, perform the merge using an empty notes tree.
*/ */
if (!check_ref_format(o->remote_ref)) { if (!check_refname_format(o->remote_ref, 0)) {
hashclr(remote_sha1); hashclr(remote_sha1);
remote = NULL; remote = NULL;
} else { } else {

View file

@ -72,7 +72,7 @@ static void try_remove_empty_parents(char *name)
for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */ for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
while (*p && *p != '/') while (*p && *p != '/')
p++; p++;
/* tolerate duplicate slashes; see check_ref_format() */ /* tolerate duplicate slashes; see check_refname_format() */
while (*p == '/') while (*p == '/')
p++; p++;
} }

226
refs.c
View file

@ -56,6 +56,8 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
entry = xmalloc(sizeof(struct ref_list) + len); entry = xmalloc(sizeof(struct ref_list) + len);
hashcpy(entry->sha1, sha1); hashcpy(entry->sha1, sha1);
hashclr(entry->peeled); hashclr(entry->peeled);
if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
die("Reference has invalid format: '%s'", name);
memcpy(entry->name, name, len); memcpy(entry->name, name, len);
entry->flag = flag; entry->flag = flag;
entry->next = list; entry->next = list;
@ -508,29 +510,37 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
} }
/* /*
* If the "reading" argument is set, this function finds out what _object_ * Try to read ref from the packed references. On success, set sha1
* the ref points at by "reading" the ref. The ref, if it is not symbolic, * and return 0; otherwise, return -1.
* has to exist, and if it is symbolic, it has to point at an existing ref,
* because the "read" goes through the symref to the ref it points at.
*
* The access that is not "reading" may often be "writing", but does not
* have to; it can be merely checking _where it leads to_. If it is a
* prelude to "writing" to the ref, a write to a symref that points at
* yet-to-be-born ref will create the real ref pointed by the symref.
* reading=0 allows the caller to check where such a symref leads to.
*/ */
static int get_packed_ref(const char *ref, unsigned char *sha1)
{
struct ref_list *list = get_packed_refs(NULL);
while (list) {
if (!strcmp(ref, list->name)) {
hashcpy(sha1, list->sha1);
return 0;
}
list = list->next;
}
return -1;
}
const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag) const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
{ {
int depth = MAXDEPTH; int depth = MAXDEPTH;
ssize_t len; ssize_t len;
char buffer[256]; char buffer[256];
static char ref_buffer[256]; static char ref_buffer[256];
char path[PATH_MAX];
if (flag) if (flag)
*flag = 0; *flag = 0;
if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))
return NULL;
for (;;) { for (;;) {
char path[PATH_MAX];
struct stat st; struct stat st;
char *buf; char *buf;
int fd; int fd;
@ -539,29 +549,36 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
return NULL; return NULL;
git_snpath(path, sizeof(path), "%s", ref); git_snpath(path, sizeof(path), "%s", ref);
/* Special case: non-existing file. */
if (lstat(path, &st) < 0) { if (lstat(path, &st) < 0) {
struct ref_list *list = get_packed_refs(NULL); if (errno != ENOENT)
while (list) {
if (!strcmp(ref, list->name)) {
hashcpy(sha1, list->sha1);
if (flag)
*flag |= REF_ISPACKED;
return ref;
}
list = list->next;
}
if (reading || errno != ENOENT)
return NULL; return NULL;
hashclr(sha1); /*
return ref; * The loose reference file does not exist;
* check for a packed reference.
*/
if (!get_packed_ref(ref, sha1)) {
if (flag)
*flag |= REF_ISPACKED;
return ref;
}
/* The reference is not a packed reference, either. */
if (reading) {
return NULL;
} else {
hashclr(sha1);
return ref;
}
} }
/* Follow "normalized" - ie "refs/.." symlinks by hand */ /* Follow "normalized" - ie "refs/.." symlinks by hand */
if (S_ISLNK(st.st_mode)) { if (S_ISLNK(st.st_mode)) {
len = readlink(path, buffer, sizeof(buffer)-1); len = readlink(path, buffer, sizeof(buffer)-1);
if (len >= 5 && !memcmp("refs/", buffer, 5)) { if (len < 0)
buffer[len] = 0; return NULL;
buffer[len] = 0;
if (!prefixcmp(buffer, "refs/") &&
!check_refname_format(buffer, 0)) {
strcpy(ref_buffer, buffer); strcpy(ref_buffer, buffer);
ref = ref_buffer; ref = ref_buffer;
if (flag) if (flag)
@ -585,26 +602,34 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
return NULL; return NULL;
len = read_in_full(fd, buffer, sizeof(buffer)-1); len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd); close(fd);
if (len < 0)
return NULL;
while (len && isspace(buffer[len-1]))
len--;
buffer[len] = '\0';
/* /*
* Is it a symbolic ref? * Is it a symbolic ref?
*/ */
if (len < 4 || memcmp("ref:", buffer, 4)) if (prefixcmp(buffer, "ref:"))
break; break;
buf = buffer + 4; buf = buffer + 4;
len -= 4; while (isspace(*buf))
while (len && isspace(*buf)) buf++;
buf++, len--; if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
while (len && isspace(buf[len-1])) warning("symbolic reference in %s is formatted incorrectly",
len--; path);
buf[len] = 0; return NULL;
memcpy(ref_buffer, buf, len + 1); }
ref = ref_buffer; ref = strcpy(ref_buffer, buf);
if (flag) if (flag)
*flag |= REF_ISSYMREF; *flag |= REF_ISSYMREF;
} }
if (len < 40 || get_sha1_hex(buffer, sha1)) /* Please note that FETCH_HEAD has a second line containing other data. */
if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
warning("reference in %s is formatted incorrectly", path);
return NULL; return NULL;
}
return ref; return ref;
} }
@ -902,70 +927,87 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
* - it contains a "\" (backslash) * - it contains a "\" (backslash)
*/ */
/* Return true iff ch is not allowed in reference names. */
static inline int bad_ref_char(int ch) static inline int bad_ref_char(int ch)
{ {
if (((unsigned) ch) <= ' ' || ch == 0x7f || if (((unsigned) ch) <= ' ' || ch == 0x7f ||
ch == '~' || ch == '^' || ch == ':' || ch == '\\') ch == '~' || ch == '^' || ch == ':' || ch == '\\')
return 1; return 1;
/* 2.13 Pattern Matching Notation */ /* 2.13 Pattern Matching Notation */
if (ch == '?' || ch == '[') /* Unsupported */ if (ch == '*' || ch == '?' || ch == '[') /* Unsupported */
return 1; return 1;
if (ch == '*') /* Supported at the end */
return 2;
return 0; return 0;
} }
int check_ref_format(const char *ref) /*
* Try to read one refname component from the front of ref. Return
* the length of the component found, or -1 if the component is not
* legal.
*/
static int check_refname_component(const char *ref, int flags)
{ {
int ch, level, bad_type, last; const char *cp;
int ret = CHECK_REF_FORMAT_OK; char last = '\0';
const char *cp = ref;
level = 0;
while (1) {
while ((ch = *cp++) == '/')
; /* tolerate duplicated slashes */
if (!ch)
/* should not end with slashes */
return CHECK_REF_FORMAT_ERROR;
/* we are at the beginning of the path component */
if (ch == '.')
return CHECK_REF_FORMAT_ERROR;
bad_type = bad_ref_char(ch);
if (bad_type) {
if (bad_type == 2 && (!*cp || *cp == '/') &&
ret == CHECK_REF_FORMAT_OK)
ret = CHECK_REF_FORMAT_WILDCARD;
else
return CHECK_REF_FORMAT_ERROR;
}
for (cp = ref; ; cp++) {
char ch = *cp;
if (ch == '\0' || ch == '/')
break;
if (bad_ref_char(ch))
return -1; /* Illegal character in refname. */
if (last == '.' && ch == '.')
return -1; /* Refname contains "..". */
if (last == '@' && ch == '{')
return -1; /* Refname contains "@{". */
last = ch; last = ch;
/* scan the rest of the path component */
while ((ch = *cp++) != 0) {
bad_type = bad_ref_char(ch);
if (bad_type)
return CHECK_REF_FORMAT_ERROR;
if (ch == '/')
break;
if (last == '.' && ch == '.')
return CHECK_REF_FORMAT_ERROR;
if (last == '@' && ch == '{')
return CHECK_REF_FORMAT_ERROR;
last = ch;
}
level++;
if (!ch) {
if (ref <= cp - 2 && cp[-2] == '.')
return CHECK_REF_FORMAT_ERROR;
if (level < 2)
return CHECK_REF_FORMAT_ONELEVEL;
if (has_extension(ref, ".lock"))
return CHECK_REF_FORMAT_ERROR;
return ret;
}
} }
if (cp == ref)
return -1; /* Component has zero length. */
if (ref[0] == '.') {
if (!(flags & REFNAME_DOT_COMPONENT))
return -1; /* Component starts with '.'. */
/*
* Even if leading dots are allowed, don't allow "."
* as a component (".." is prevented by a rule above).
*/
if (ref[1] == '\0')
return -1; /* Component equals ".". */
}
if (cp - ref >= 5 && !memcmp(cp - 5, ".lock", 5))
return -1; /* Refname ends with ".lock". */
return cp - ref;
}
int check_refname_format(const char *ref, int flags)
{
int component_len, component_count = 0;
while (1) {
/* We are at the start of a path component. */
component_len = check_refname_component(ref, flags);
if (component_len < 0) {
if ((flags & REFNAME_REFSPEC_PATTERN) &&
ref[0] == '*' &&
(ref[1] == '\0' || ref[1] == '/')) {
/* Accept one wildcard as a full refname component. */
flags &= ~REFNAME_REFSPEC_PATTERN;
component_len = 1;
} else {
return -1;
}
}
component_count++;
if (ref[component_len] == '\0')
break;
/* Skip to next component. */
ref += component_len + 1;
}
if (ref[component_len - 1] == '.')
return -1; /* Refname ends with '.'. */
if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
return -1; /* Refname has only one component. */
return 0;
} }
const char *prettify_refname(const char *name) const char *prettify_refname(const char *name)
@ -1148,7 +1190,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
{ {
char refpath[PATH_MAX]; char refpath[PATH_MAX];
if (check_ref_format(ref)) if (check_refname_format(ref, 0))
return NULL; return NULL;
strcpy(refpath, mkpath("refs/%s", ref)); strcpy(refpath, mkpath("refs/%s", ref));
return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL); return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
@ -1156,13 +1198,9 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags) struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags)
{ {
switch (check_ref_format(ref)) { if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))
default:
return NULL; return NULL;
case 0: return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
case CHECK_REF_FORMAT_ONELEVEL:
return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
}
} }
static struct lock_file packlock; static struct lock_file packlock;

21
refs.h
View file

@ -97,11 +97,22 @@ int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long, voi
*/ */
extern int for_each_reflog(each_ref_fn, void *); extern int for_each_reflog(each_ref_fn, void *);
#define CHECK_REF_FORMAT_OK 0 #define REFNAME_ALLOW_ONELEVEL 1
#define CHECK_REF_FORMAT_ERROR (-1) #define REFNAME_REFSPEC_PATTERN 2
#define CHECK_REF_FORMAT_ONELEVEL (-2) #define REFNAME_DOT_COMPONENT 4
#define CHECK_REF_FORMAT_WILDCARD (-3)
extern int check_ref_format(const char *target); /*
* Return 0 iff ref has the correct format for a refname according to
* the rules described in Documentation/git-check-ref-format.txt. If
* REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
* reference names. If REFNAME_REFSPEC_PATTERN is set in flags, then
* allow a "*" wildcard character in place of one of the name
* components. No leading or repeated slashes are accepted. If
* REFNAME_DOT_COMPONENT is set in flags, then allow refname
* components to start with "." (but not a whole component equal to
* "." or "..").
*/
extern int check_refname_format(const char *ref, int flags);
extern const char *prettify_refname(const char *refname); extern const char *prettify_refname(const char *refname);
extern char *shorten_unambiguous_ref(const char *ref, int strict); extern char *shorten_unambiguous_ref(const char *ref, int strict);

View file

@ -492,23 +492,6 @@ static void read_config(void)
alias_all_urls(); alias_all_urls();
} }
/*
* We need to make sure the remote-tracking branches are well formed, but a
* wildcard refspec in "struct refspec" must have a trailing slash. We
* temporarily drop the trailing '/' while calling check_ref_format(),
* and put it back. The caller knows that a CHECK_REF_FORMAT_ONELEVEL
* error return is Ok for a wildcard refspec.
*/
static int verify_refname(char *name, int is_glob)
{
int result;
result = check_ref_format(name);
if (is_glob && result == CHECK_REF_FORMAT_WILDCARD)
result = CHECK_REF_FORMAT_OK;
return result;
}
/* /*
* This function frees a refspec array. * This function frees a refspec array.
* Warning: code paths should be checked to ensure that the src * Warning: code paths should be checked to ensure that the src
@ -532,13 +515,13 @@ static void free_refspecs(struct refspec *refspec, int nr_refspec)
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
{ {
int i; int i;
int st;
struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
for (i = 0; i < nr_refspec; i++) { for (i = 0; i < nr_refspec; i++) {
size_t llen; size_t llen;
int is_glob; int is_glob;
const char *lhs, *rhs; const char *lhs, *rhs;
int flags;
is_glob = 0; is_glob = 0;
@ -576,6 +559,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
rs[i].pattern = is_glob; rs[i].pattern = is_glob;
rs[i].src = xstrndup(lhs, llen); rs[i].src = xstrndup(lhs, llen);
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
if (fetch) { if (fetch) {
/* /*
@ -585,26 +569,20 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
*/ */
if (!*rs[i].src) if (!*rs[i].src)
; /* empty is ok */ ; /* empty is ok */
else { else if (check_refname_format(rs[i].src, flags))
st = verify_refname(rs[i].src, is_glob); goto invalid;
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
goto invalid;
}
/* /*
* RHS * RHS
* - missing is ok, and is same as empty. * - missing is ok, and is same as empty.
* - empty is ok; it means not to store. * - empty is ok; it means not to store.
* - otherwise it must be a valid looking ref. * - otherwise it must be a valid looking ref.
*/ */
if (!rs[i].dst) { if (!rs[i].dst)
; /* ok */ ; /* ok */
} else if (!*rs[i].dst) { else if (!*rs[i].dst)
; /* ok */ ; /* ok */
} else { else if (check_refname_format(rs[i].dst, flags))
st = verify_refname(rs[i].dst, is_glob); goto invalid;
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
goto invalid;
}
} else { } else {
/* /*
* LHS * LHS
@ -616,8 +594,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
if (!*rs[i].src) if (!*rs[i].src)
; /* empty is ok */ ; /* empty is ok */
else if (is_glob) { else if (is_glob) {
st = verify_refname(rs[i].src, is_glob); if (check_refname_format(rs[i].src, flags))
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
goto invalid; goto invalid;
} }
else else
@ -630,14 +607,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
* - otherwise it must be a valid looking ref. * - otherwise it must be a valid looking ref.
*/ */
if (!rs[i].dst) { if (!rs[i].dst) {
st = verify_refname(rs[i].src, is_glob); if (check_refname_format(rs[i].src, flags))
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
goto invalid; goto invalid;
} else if (!*rs[i].dst) { } else if (!*rs[i].dst) {
goto invalid; goto invalid;
} else { } else {
st = verify_refname(rs[i].dst, is_glob); if (check_refname_format(rs[i].dst, flags))
if (st && st != CHECK_REF_FORMAT_ONELEVEL)
goto invalid; goto invalid;
} }
} }
@ -840,7 +815,7 @@ char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
refspec->dst, &ret)) refspec->dst, &ret))
return ret; return ret;
} else if (!strcmp(refspec->src, name)) } else if (!strcmp(refspec->src, name))
return strdup(refspec->dst); return xstrdup(refspec->dst);
} }
return NULL; return NULL;
} }
@ -1427,8 +1402,8 @@ int get_fetch_map(const struct ref *remote_refs,
for (rmp = &ref_map; *rmp; ) { for (rmp = &ref_map; *rmp; ) {
if ((*rmp)->peer_ref) { if ((*rmp)->peer_ref) {
int st = check_ref_format((*rmp)->peer_ref->name + 5); if (check_refname_format((*rmp)->peer_ref->name + 5,
if (st && st != CHECK_REF_FORMAT_ONELEVEL) { REFNAME_ALLOW_ONELEVEL)) {
struct ref *ignore = *rmp; struct ref *ignore = *rmp;
error("* Ignoring funny ref '%s' locally", error("* Ignoring funny ref '%s' locally",
(*rmp)->peer_ref->name); (*rmp)->peer_ref->name);
@ -1620,7 +1595,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1, int fla
int len; int len;
/* we already know it starts with refs/ to get here */ /* we already know it starts with refs/ to get here */
if (check_ref_format(refname + 5)) if (check_refname_format(refname + 5, 0))
return 0; return 0;
len = strlen(refname) + 1; len = strlen(refname) + 1;

View file

@ -966,9 +966,9 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
{ {
strbuf_branchname(sb, name); strbuf_branchname(sb, name);
if (name[0] == '-') if (name[0] == '-')
return CHECK_REF_FORMAT_ERROR; return -1;
strbuf_splice(sb, 0, 0, "refs/heads/", 11); strbuf_splice(sb, 0, 0, "refs/heads/", 11);
return check_ref_format(sb->buf); return check_refname_format(sb->buf, 0);
} }
/* /*

View file

@ -5,34 +5,124 @@ 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
} }
valid_ref 'heads/foo' invalid_ref ''
invalid_ref 'foo' invalid_ref '/'
invalid_ref '/' --allow-onelevel
invalid_ref '/' --normalize
invalid_ref '/' '--allow-onelevel --normalize'
valid_ref 'foo/bar/baz' valid_ref 'foo/bar/baz'
valid_ref 'refs///heads/foo' valid_ref 'foo/bar/baz' --normalize
invalid_ref 'refs///heads/foo'
valid_ref 'refs///heads/foo' --normalize
invalid_ref 'heads/foo/' invalid_ref 'heads/foo/'
valid_ref '/heads/foo' invalid_ref '/heads/foo'
valid_ref '///heads/foo' valid_ref '/heads/foo' --normalize
invalid_ref '/foo' invalid_ref '///heads/foo'
valid_ref '///heads/foo' --normalize
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'
valid_ref 'foo./bar' valid_ref 'foo./bar'
invalid_ref 'heads/foo.lock' invalid_ref 'heads/foo.lock'
invalid_ref 'heads///foo.lock'
invalid_ref 'foo.lock/bar'
invalid_ref 'foo.lock///bar'
valid_ref 'heads/foo@bar' valid_ref 'heads/foo@bar'
invalid_ref 'heads/v@{ation' invalid_ref 'heads/v@{ation'
invalid_ref 'heads/foo\bar' 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'
invalid_ref "$ref" --normalize
valid_ref "$ref" '--allow-onelevel --normalize'
ref='foo/bar'
valid_ref "$ref"
valid_ref "$ref" --allow-onelevel
valid_ref "$ref" --refspec-pattern
valid_ref "$ref" '--refspec-pattern --allow-onelevel'
valid_ref "$ref" --normalize
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'
invalid_ref "$ref" --normalize
valid_ref "$ref" '--refspec-pattern --normalize'
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
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"
invalid_ref "$ref" --allow-onelevel
invalid_ref "$ref" --refspec-pattern
invalid_ref "$ref" '--refspec-pattern --allow-onelevel'
invalid_ref "$ref" --normalize
valid_ref "$ref" '--allow-onelevel --normalize'
invalid_ref "$ref" '--refspec-pattern --normalize'
valid_ref "$ref" '--refspec-pattern --allow-onelevel --normalize'
test_expect_success "check-ref-format --branch @{-1}" ' test_expect_success "check-ref-format --branch @{-1}" '
T=$(git write-tree) && T=$(git write-tree) &&
@ -66,12 +156,12 @@ test_expect_success 'check-ref-format --branch from subdir' '
valid_ref_normalized() { valid_ref_normalized() {
test_expect_success "ref name '$1' simplifies to '$2'" " test_expect_success "ref name '$1' simplifies to '$2'" "
refname=\$(git check-ref-format --print '$1') && refname=\$(git check-ref-format --normalize '$1') &&
test \"\$refname\" = '$2'" test \"\$refname\" = '$2'"
} }
invalid_ref_normalized() { invalid_ref_normalized() {
test_expect_success "check-ref-format --print rejects '$1'" " test_expect_success "check-ref-format --normalize rejects '$1'" "
test_must_fail git check-ref-format --print '$1'" test_must_fail git check-ref-format --normalize '$1'"
} }
valid_ref_normalized 'heads/foo' 'heads/foo' valid_ref_normalized 'heads/foo' 'heads/foo'
@ -83,5 +173,9 @@ invalid_ref_normalized '/foo'
invalid_ref_normalized 'heads/foo/../bar' invalid_ref_normalized 'heads/foo/../bar'
invalid_ref_normalized 'heads/./foo' invalid_ref_normalized 'heads/./foo'
invalid_ref_normalized 'heads\foo' invalid_ref_normalized 'heads\foo'
invalid_ref_normalized 'heads/foo.lock'
invalid_ref_normalized 'heads///foo.lock'
invalid_ref_normalized 'foo.lock/bar'
invalid_ref_normalized 'foo.lock///bar'
test_done test_done

View file

@ -183,7 +183,7 @@ static struct child_process *get_helper(struct transport *transport)
ALLOC_GROW(refspecs, ALLOC_GROW(refspecs,
refspec_nr + 1, refspec_nr + 1,
refspec_alloc); refspec_alloc);
refspecs[refspec_nr++] = strdup(capname + strlen("refspec ")); refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec "));
} else if (!strcmp(capname, "connect")) { } else if (!strcmp(capname, "connect")) {
data->connect = 1; data->connect = 1;
} else if (!prefixcmp(capname, "export-marks ")) { } else if (!prefixcmp(capname, "export-marks ")) {
@ -445,9 +445,11 @@ static int fetch_with_import(struct transport *transport,
if (data->refspecs) if (data->refspecs)
private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name); private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name);
else else
private = strdup(posn->name); private = xstrdup(posn->name);
read_ref(private, posn->old_sha1); if (private) {
free(private); read_ref(private, posn->old_sha1);
free(private);
}
} }
strbuf_release(&buf); strbuf_release(&buf);
return 0; return 0;

View file

@ -755,18 +755,10 @@ void transport_verify_remote_names(int nr_heads, const char **heads)
continue; continue;
remote = remote ? (remote + 1) : local; remote = remote ? (remote + 1) : local;
switch (check_ref_format(remote)) { if (check_refname_format(remote,
case 0: /* ok */ REFNAME_ALLOW_ONELEVEL|REFNAME_REFSPEC_PATTERN))
case CHECK_REF_FORMAT_ONELEVEL: die("remote part of refspec is not a valid name in %s",
/* ok but a single level -- that is fine for heads[i]);
* a match pattern.
*/
case CHECK_REF_FORMAT_WILDCARD:
/* ok but ends with a pattern-match character */
continue;
}
die("remote part of refspec is not a valid name in %s",
heads[i]);
} }
} }

View file

@ -190,7 +190,7 @@ static int interpret_target(struct walker *walker, char *target, unsigned char *
{ {
if (!get_sha1_hex(target, sha1)) if (!get_sha1_hex(target, sha1))
return 0; return 0;
if (!check_ref_format(target)) { if (!check_refname_format(target, 0)) {
struct ref *ref = alloc_ref(target); struct ref *ref = alloc_ref(target);
if (!walker->fetch_ref(walker, ref)) { if (!walker->fetch_ref(walker, ref)) {
hashcpy(sha1, ref->old_sha1); hashcpy(sha1, ref->old_sha1);