diff options
Diffstat (limited to 'remote.c')
-rw-r--r-- | remote.c | 215 |
1 files changed, 117 insertions, 98 deletions
@@ -8,6 +8,8 @@ #include "tag.h" #include "string-list.h" +enum map_direction { FROM_SRC, FROM_DST }; + static struct refspec s_tag_refspec = { 0, 1, @@ -482,7 +484,7 @@ static void read_config(void) return; default_remote_name = xstrdup("origin"); current_branch = NULL; - head_ref = resolve_ref("HEAD", sha1, 0, &flag); + head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag); if (head_ref && (flag & REF_ISSYMREF) && !prefixcmp(head_ref, "refs/heads/")) { current_branch = @@ -493,23 +495,6 @@ static void read_config(void) } /* - * 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. * Warning: code paths should be checked to ensure that the src * and dst pointers are always freeable pointers as well @@ -532,13 +517,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) { int i; - int st; struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); for (i = 0; i < nr_refspec; i++) { size_t llen; int is_glob; const char *lhs, *rhs; + int flags; is_glob = 0; @@ -576,6 +561,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp rs[i].pattern = is_glob; rs[i].src = xstrndup(lhs, llen); + flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); if (fetch) { /* @@ -585,26 +571,20 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp */ if (!*rs[i].src) ; /* empty is ok */ - else { - st = verify_refname(rs[i].src, is_glob); - if (st && st != CHECK_REF_FORMAT_ONELEVEL) - goto invalid; - } + else if (check_refname_format(rs[i].src, flags)) + goto invalid; /* * RHS * - missing is ok, and is same as empty. * - empty is ok; it means not to store. * - otherwise it must be a valid looking ref. */ - if (!rs[i].dst) { + if (!rs[i].dst) ; /* ok */ - } else if (!*rs[i].dst) { + else if (!*rs[i].dst) ; /* ok */ - } else { - st = verify_refname(rs[i].dst, is_glob); - if (st && st != CHECK_REF_FORMAT_ONELEVEL) - goto invalid; - } + else if (check_refname_format(rs[i].dst, flags)) + goto invalid; } else { /* * LHS @@ -616,8 +596,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (!*rs[i].src) ; /* empty is ok */ else if (is_glob) { - st = verify_refname(rs[i].src, is_glob); - if (st && st != CHECK_REF_FORMAT_ONELEVEL) + if (check_refname_format(rs[i].src, flags)) goto invalid; } else @@ -630,14 +609,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp * - otherwise it must be a valid looking ref. */ if (!rs[i].dst) { - st = verify_refname(rs[i].src, is_glob); - if (st && st != CHECK_REF_FORMAT_ONELEVEL) + if (check_refname_format(rs[i].src, flags)) goto invalid; } else if (!*rs[i].dst) { goto invalid; } else { - st = verify_refname(rs[i].dst, is_glob); - if (st && st != CHECK_REF_FORMAT_ONELEVEL) + if (check_refname_format(rs[i].dst, flags)) goto invalid; } } @@ -1003,16 +980,20 @@ static void tail_link_ref(struct ref *ref, struct ref ***tail) *tail = &ref->next; } +static struct ref *alloc_delete_ref(void) +{ + struct ref *ref = alloc_ref("(delete)"); + hashclr(ref->new_sha1); + return ref; +} + static struct ref *try_explicit_object_name(const char *name) { unsigned char sha1[20]; struct ref *ref; - if (!*name) { - ref = alloc_ref("(delete)"); - hashclr(ref->new_sha1); - return ref; - } + if (!*name) + return alloc_delete_ref(); if (get_sha1(name, sha1)) return NULL; ref = alloc_ref(name); @@ -1032,7 +1013,7 @@ static char *guess_ref(const char *name, struct ref *peer) struct strbuf buf = STRBUF_INIT; unsigned char sha1[20]; - const char *r = resolve_ref(peer->name, sha1, 1, NULL); + const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL); if (!r) return NULL; @@ -1083,7 +1064,7 @@ static int match_explicit(struct ref *src, struct ref *dst, unsigned char sha1[20]; int flag; - dst_value = resolve_ref(matched_src->name, sha1, 1, &flag); + dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag); if (!dst_value || ((flag & REF_ISSYMREF) && prefixcmp(dst_value, "refs/heads/"))) @@ -1135,10 +1116,11 @@ static int match_explicit_refs(struct ref *src, struct ref *dst, return errs; } -static const struct refspec *check_pattern_match(const struct refspec *rs, - int rs_nr, - const struct ref *src) +static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref, + int send_mirror, int direction, const struct refspec **ret_pat) { + const struct refspec *pat; + char *name; int i; int matching_refs = -1; for (i = 0; i < rs_nr; i++) { @@ -1148,14 +1130,36 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, continue; } - if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name, - NULL, NULL)) - return rs + i; + if (rs[i].pattern) { + const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src; + int match; + if (direction == FROM_SRC) + match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name); + else + match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name); + if (match) { + matching_refs = i; + break; + } + } } - if (matching_refs != -1) - return rs + matching_refs; - else + if (matching_refs == -1) return NULL; + + pat = rs + matching_refs; + if (pat->matching) { + /* + * "matching refs"; traditionally we pushed everything + * including refs outside refs/heads/ hierarchy, but + * that does not make much sense these days. + */ + if (!send_mirror && prefixcmp(ref->name, "refs/heads/")) + return NULL; + name = xstrdup(ref->name); + } + if (ret_pat) + *ret_pat = pat; + return name; } static struct ref **tail_ref(struct ref **head) @@ -1167,19 +1171,23 @@ static struct ref **tail_ref(struct ref **head) } /* - * Note. This is used only by "push"; refspec matching rules for - * push and fetch are subtly different, so do not try to reuse it - * without thinking. + * Given the set of refs the local repository has, the set of refs the + * remote repository has, and the refspec used for push, determine + * what remote refs we will update and with what value by setting + * peer_ref (which object is being pushed) and force (if the push is + * forced) in elements of "dst". The function may add new elements to + * dst (e.g. pushing to a new branch, done in match_explicit_refs). */ -int match_refs(struct ref *src, struct ref **dst, - int nr_refspec, const char **refspec, int flags) +int match_push_refs(struct ref *src, struct ref **dst, + int nr_refspec, const char **refspec, int flags) { struct refspec *rs; int send_all = flags & MATCH_REFS_ALL; int send_mirror = flags & MATCH_REFS_MIRROR; + int send_prune = flags & MATCH_REFS_PRUNE; int errs; static const char *default_refspec[] = { ":", NULL }; - struct ref **dst_tail = tail_ref(dst); + struct ref *ref, **dst_tail = tail_ref(dst); if (!nr_refspec) { nr_refspec = 1; @@ -1189,39 +1197,23 @@ int match_refs(struct ref *src, struct ref **dst, errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec); /* pick the remainder */ - for ( ; src; src = src->next) { + for (ref = src; ref; ref = ref->next) { struct ref *dst_peer; const struct refspec *pat = NULL; char *dst_name; - if (src->peer_ref) - continue; - pat = check_pattern_match(rs, nr_refspec, src); - if (!pat) + if (ref->peer_ref) continue; - if (pat->matching) { - /* - * "matching refs"; traditionally we pushed everything - * including refs outside refs/heads/ hierarchy, but - * that does not make much sense these days. - */ - if (!send_mirror && prefixcmp(src->name, "refs/heads/")) - continue; - dst_name = xstrdup(src->name); + dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat); + if (!dst_name) + continue; - } else { - const char *dst_side = pat->dst ? pat->dst : pat->src; - if (!match_name_with_pattern(pat->src, src->name, - dst_side, &dst_name)) - die("Didn't think it matches any more"); - } dst_peer = find_ref_by_name(*dst, dst_name); if (dst_peer) { if (dst_peer->peer_ref) /* We're already sending something to this ref. */ goto free_name; - } else { if (pat->matching && !(send_all || send_mirror)) /* @@ -1233,13 +1225,30 @@ int match_refs(struct ref *src, struct ref **dst, /* Create a new one and link it */ dst_peer = make_linked_ref(dst_name, &dst_tail); - hashcpy(dst_peer->new_sha1, src->new_sha1); + hashcpy(dst_peer->new_sha1, ref->new_sha1); } - dst_peer->peer_ref = copy_ref(src); + dst_peer->peer_ref = copy_ref(ref); dst_peer->force = pat->force; free_name: free(dst_name); } + if (send_prune) { + /* check for missing refs on the remote */ + for (ref = *dst; ref; ref = ref->next) { + char *src_name; + + if (ref->peer_ref) + /* We're already sending something to this ref. */ + continue; + + src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL); + if (src_name) { + if (!find_ref_by_name(src, src_name)) + ref->peer_ref = alloc_delete_ref(); + free(src_name); + } + } + } if (errs) return -1; return 0; @@ -1424,8 +1433,8 @@ int get_fetch_map(const struct ref *remote_refs, for (rmp = &ref_map; *rmp; ) { if ((*rmp)->peer_ref) { - int st = check_ref_format((*rmp)->peer_ref->name + 5); - if (st && st != CHECK_REF_FORMAT_ONELEVEL) { + if (check_refname_format((*rmp)->peer_ref->name + 5, + REFNAME_ALLOW_ONELEVEL)) { struct ref *ignore = *rmp; error("* Ignoring funny ref '%s' locally", (*rmp)->peer_ref->name); @@ -1529,13 +1538,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs) * nothing to report. */ base = branch->merge[0]->dst; - if (!resolve_ref(base, sha1, 1, NULL)) + if (read_ref(base, sha1)) return 0; theirs = lookup_commit_reference(sha1); if (!theirs) return 0; - if (!resolve_ref(branch->refname, sha1, 1, NULL)) + if (read_ref(branch->refname, sha1)) return 0; ours = lookup_commit_reference(sha1); if (!ours) @@ -1594,19 +1603,29 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) base = branch->merge[0]->dst; base = shorten_unambiguous_ref(base, 0); if (!num_theirs) - strbuf_addf(sb, "Your branch is ahead of '%s' " - "by %d commit%s.\n", - base, num_ours, (num_ours == 1) ? "" : "s"); + strbuf_addf(sb, + Q_("Your branch is ahead of '%s' by %d commit.\n", + "Your branch is ahead of '%s' by %d commits.\n", + num_ours), + base, num_ours); else if (!num_ours) - strbuf_addf(sb, "Your branch is behind '%s' " - "by %d commit%s, " - "and can be fast-forwarded.\n", - base, num_theirs, (num_theirs == 1) ? "" : "s"); + strbuf_addf(sb, + Q_("Your branch is behind '%s' by %d commit, " + "and can be fast-forwarded.\n", + "Your branch is behind '%s' by %d commits, " + "and can be fast-forwarded.\n", + num_theirs), + base, num_theirs); else - strbuf_addf(sb, "Your branch and '%s' have diverged,\n" - "and have %d and %d different commit(s) each, " - "respectively.\n", - base, num_ours, num_theirs); + strbuf_addf(sb, + Q_("Your branch and '%s' have diverged,\n" + "and have %d and %d different commit each, " + "respectively.\n", + "Your branch and '%s' have diverged,\n" + "and have %d and %d different commits each, " + "respectively.\n", + num_theirs), + base, num_ours, num_theirs); return 1; } @@ -1617,7 +1636,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1, int fla int len; /* 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; len = strlen(refname) + 1; |