Merge branch 'sp/refspec-match'

* sp/refspec-match:
  refactor fetch's ref matching to use refname_match()
  push: use same rules as git-rev-parse to resolve refspecs
  add refname_match()
  push: support pushing HEAD to real branch name
This commit is contained in:
Junio C Hamano 2007-12-04 17:07:10 -08:00
commit 9bbe6db85f
8 changed files with 105 additions and 39 deletions

View File

@ -85,7 +85,9 @@ Each pattern pair consists of the source side (before the colon)
and the destination side (after the colon). The ref to be and the destination side (after the colon). The ref to be
pushed is determined by finding a match that matches the source pushed is determined by finding a match that matches the source
side, and where it is pushed is determined by using the side, and where it is pushed is determined by using the
destination side. destination side. The rules used to match a ref are the same
rules used by gitlink:git-rev-parse[1] to resolve a symbolic ref
name.
- It is an error if <src> does not match exactly one of the - It is an error if <src> does not match exactly one of the
local refs. local refs.

View File

@ -44,6 +44,15 @@ static void set_refspecs(const char **refs, int nr)
strcat(tag, refs[i]); strcat(tag, refs[i]);
ref = tag; ref = tag;
} }
if (!strcmp("HEAD", ref)) {
unsigned char sha1_dummy[20];
ref = resolve_ref(ref, sha1_dummy, 1, NULL);
if (!ref)
die("HEAD cannot be resolved.");
if (prefixcmp(ref, "refs/heads/"))
die("HEAD cannot be resolved to branch.");
ref = xstrdup(ref + 11);
}
add_refspec(ref); add_refspec(ref);
} }
} }

View File

@ -423,6 +423,10 @@ extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *
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 refname_match(const char *abbrev_name, const char *full_name, const char **rules);
extern const char *ref_rev_parse_rules[];
extern const char *ref_fetch_rules[];
extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg); extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
extern int validate_headref(const char *ref); extern int validate_headref(const char *ref);

31
refs.c
View File

@ -643,6 +643,37 @@ int check_ref_format(const char *ref)
} }
} }
const char *ref_rev_parse_rules[] = {
"%.*s",
"refs/%.*s",
"refs/tags/%.*s",
"refs/heads/%.*s",
"refs/remotes/%.*s",
"refs/remotes/%.*s/HEAD",
NULL
};
const char *ref_fetch_rules[] = {
"%.*s",
"refs/%.*s",
"refs/heads/%.*s",
NULL
};
int refname_match(const char *abbrev_name, const char *full_name, const char **rules)
{
const char **p;
const int abbrev_name_len = strlen(abbrev_name);
for (p = rules; *p; p++) {
if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) {
return 1;
}
}
return 0;
}
static struct ref_lock *verify_lock(struct ref_lock *lock, static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist) const unsigned char *old_sha1, int mustexist)
{ {

View File

@ -419,25 +419,6 @@ int remote_has_url(struct remote *remote, const char *url)
return 0; return 0;
} }
/*
* Returns true if, under the matching rules for fetching, name is the
* same as the given full name.
*/
static int ref_matches_abbrev(const char *name, const char *full)
{
if (!prefixcmp(name, "refs/") || !strcmp(name, "HEAD"))
return !strcmp(name, full);
if (prefixcmp(full, "refs/"))
return 0;
if (!prefixcmp(name, "heads/") ||
!prefixcmp(name, "tags/") ||
!prefixcmp(name, "remotes/"))
return !strcmp(name, full + 5);
if (prefixcmp(full + 5, "heads/"))
return 0;
return !strcmp(full + 11, name);
}
int remote_find_tracking(struct remote *remote, struct refspec *refspec) int remote_find_tracking(struct remote *remote, struct refspec *refspec)
{ {
int find_src = refspec->src == NULL; int find_src = refspec->src == NULL;
@ -533,10 +514,7 @@ static int count_refspec_match(const char *pattern,
char *name = refs->name; char *name = refs->name;
int namelen = strlen(name); int namelen = strlen(name);
if (namelen < patlen || if (!refname_match(pattern, name, ref_rev_parse_rules))
memcmp(name + namelen - patlen, pattern, patlen))
continue;
if (namelen != patlen && name[namelen - patlen - 1] != '/')
continue; continue;
/* A match is "weak" if it is with refs outside /* A match is "weak" if it is with refs outside
@ -818,7 +796,7 @@ int branch_merge_matches(struct branch *branch,
{ {
if (!branch || i < 0 || i >= branch->merge_nr) if (!branch || i < 0 || i >= branch->merge_nr)
return 0; return 0;
return ref_matches_abbrev(branch->merge[i]->src, refname); return refname_match(branch->merge[i]->src, refname, ref_fetch_rules);
} }
static struct ref *get_expanded_map(const struct ref *remote_refs, static struct ref *get_expanded_map(const struct ref *remote_refs,
@ -857,7 +835,7 @@ static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const c
{ {
const struct ref *ref; const struct ref *ref;
for (ref = refs; ref; ref = ref->next) { for (ref = refs; ref; ref = ref->next) {
if (ref_matches_abbrev(name, ref->name)) if (refname_match(name, ref->name, ref_fetch_rules))
return ref; return ref;
} }
return NULL; return NULL;

View File

@ -239,23 +239,13 @@ static int ambiguous_path(const char *path, int len)
return slash; return slash;
} }
static const char *ref_fmt[] = {
"%.*s",
"refs/%.*s",
"refs/tags/%.*s",
"refs/heads/%.*s",
"refs/remotes/%.*s",
"refs/remotes/%.*s/HEAD",
NULL
};
int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
{ {
const char **p, *r; const char **p, *r;
int refs_found = 0; int refs_found = 0;
*ref = NULL; *ref = NULL;
for (p = ref_fmt; *p; p++) { for (p = ref_rev_parse_rules; *p; p++) {
unsigned char sha1_from_ref[20]; unsigned char sha1_from_ref[20];
unsigned char *this_result; unsigned char *this_result;
@ -277,7 +267,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
int logs_found = 0; int logs_found = 0;
*log = NULL; *log = NULL;
for (p = ref_fmt; *p; p++) { for (p = ref_rev_parse_rules; *p; p++) {
struct stat st; struct stat st;
unsigned char hash[20]; unsigned char hash[20];
char path[PATH_MAX]; char path[PATH_MAX];

View File

@ -95,6 +95,31 @@ test_expect_success 'fetch following tags' '
' '
test_expect_failure 'fetch must not resolve short tag name' '
cd "$D" &&
mkdir five &&
cd five &&
git init &&
git fetch .. anno:five
'
test_expect_failure 'fetch must not resolve short remote name' '
cd "$D" &&
git-update-ref refs/remotes/six/HEAD HEAD
mkdir six &&
cd six &&
git init &&
git fetch .. six:six
'
test_expect_success 'create bundle 1' ' test_expect_success 'create bundle 1' '
cd "$D" && cd "$D" &&
echo >file updated again by origin && echo >file updated again by origin &&

View File

@ -145,11 +145,21 @@ test_expect_success 'push with no ambiguity (1)' '
test_expect_success 'push with no ambiguity (2)' ' test_expect_success 'push with no ambiguity (2)' '
mk_test remotes/origin/master && mk_test remotes/origin/master &&
git push testrepo master:master && git push testrepo master:origin/master &&
check_push_result $the_commit remotes/origin/master check_push_result $the_commit remotes/origin/master
' '
test_expect_success 'push with colon-less refspec, no ambiguity' '
mk_test heads/master heads/t/master &&
git branch -f t/master master &&
git push testrepo master &&
check_push_result $the_commit heads/master &&
check_push_result $the_first_commit heads/t/master
'
test_expect_success 'push with weak ambiguity (1)' ' test_expect_success 'push with weak ambiguity (1)' '
mk_test heads/master remotes/origin/master && mk_test heads/master remotes/origin/master &&
@ -244,6 +254,23 @@ test_expect_success 'push with colon-less refspec (4)' '
' '
test_expect_success 'push with HEAD' '
mk_test heads/master &&
git checkout master &&
git push testrepo HEAD &&
check_push_result $the_commit heads/master
'
test_expect_success 'push with HEAD nonexisting at remote' '
mk_test heads/master &&
git checkout -b local master &&
git push testrepo HEAD &&
check_push_result $the_commit heads/local
'
test_expect_success 'push with dry-run' ' test_expect_success 'push with dry-run' '
mk_test heads/master && mk_test heads/master &&