diff options
Diffstat (limited to 'connect.c')
-rw-r--r-- | connect.c | 58 |
1 files changed, 52 insertions, 6 deletions
@@ -144,6 +144,7 @@ struct refspec { * +A:B means overwrite remote B with local A. * +A is a shorthand for +A:A. * A is a shorthand for A:A. + * :B means delete remote B. */ static struct refspec *parse_ref_spec(int nr_refspec, char **refspec) { @@ -174,21 +175,58 @@ static int count_refspec_match(const char *pattern, struct ref *refs, struct ref **matched_ref) { - int match; int patlen = strlen(pattern); + struct ref *matched_weak = NULL; + struct ref *matched = NULL; + int weak_match = 0; + int match = 0; - for (match = 0; refs; refs = refs->next) { + for (weak_match = match = 0; refs; refs = refs->next) { char *name = refs->name; int namelen = strlen(name); + int weak_match; + if (namelen < patlen || memcmp(name + namelen - patlen, pattern, patlen)) continue; if (namelen != patlen && name[namelen - patlen - 1] != '/') continue; - match++; - *matched_ref = refs; + + /* A match is "weak" if it is with refs outside + * heads or tags, and did not specify the pattern + * in full (e.g. "refs/remotes/origin/master") or at + * least from the toplevel (e.g. "remotes/origin/master"); + * otherwise "git push $URL master" would result in + * ambiguity between remotes/origin/master and heads/master + * at the remote site. + */ + if (namelen != patlen && + patlen != namelen - 5 && + strncmp(name, "refs/heads/", 11) && + strncmp(name, "refs/tags/", 10)) { + /* We want to catch the case where only weak + * matches are found and there are multiple + * matches, and where more than one strong + * matches are found, as ambiguous. One + * strong match with zero or more weak matches + * are acceptable as a unique match. + */ + matched_weak = refs; + weak_match++; + } + else { + matched = refs; + match++; + } + } + if (!matched) { + *matched_ref = matched_weak; + return weak_match; + } + else { + *matched_ref = matched; + return match; } - return match; } static void link_dst_tail(struct ref *ref, struct ref ***tail) @@ -203,6 +241,13 @@ static struct ref *try_explicit_object_name(const char *name) unsigned char sha1[20]; struct ref *ref; int len; + + if (!*name) { + ref = xcalloc(1, sizeof(*ref) + 20); + strcpy(ref->name, "(delete)"); + hashclr(ref->new_sha1); + return ref; + } if (get_sha1(name, sha1)) return NULL; len = strlen(name) + 1; @@ -225,7 +270,8 @@ static int match_explicit_refs(struct ref *src, struct ref *dst, break; case 0: /* The source could be in the get_sha1() format - * not a reference name. + * not a reference name. :refs/other is a + * way to delete 'other' ref at the remote end. */ matched_src = try_explicit_object_name(rs[i].src); if (matched_src) |