diff options
Diffstat (limited to 'remote.c')
-rw-r--r-- | remote.c | 704 |
1 files changed, 312 insertions, 392 deletions
@@ -2,6 +2,8 @@ #include "config.h" #include "remote.h" #include "refs.h" +#include "refspec.h" +#include "object-store.h" #include "commit.h" #include "diff.h" #include "revision.h" @@ -10,20 +12,11 @@ #include "string-list.h" #include "mergesort.h" #include "argv-array.h" +#include "commit-reach.h" +#include "advice.h" enum map_direction { FROM_SRC, FROM_DST }; -static struct refspec s_tag_refspec = { - 0, - 1, - 0, - 0, - "refs/tags/*", - "refs/tags/*" -}; - -const struct refspec *tag_refspec = &s_tag_refspec; - struct counted_string { size_t len; const char *s; @@ -87,22 +80,6 @@ static const char *alias_url(const char *url, struct rewrites *r) return xstrfmt("%s%s", r->rewrite[longest_i]->base, url + longest->len); } -static void add_push_refspec(struct remote *remote, const char *ref) -{ - ALLOC_GROW(remote->push_refspec, - remote->push_refspec_nr + 1, - remote->push_refspec_alloc); - remote->push_refspec[remote->push_refspec_nr++] = ref; -} - -static void add_fetch_refspec(struct remote *remote, const char *ref) -{ - ALLOC_GROW(remote->fetch_refspec, - remote->fetch_refspec_nr + 1, - remote->fetch_refspec_alloc); - remote->fetch_refspec[remote->fetch_refspec_nr++] = ref; -} - static void add_url(struct remote *remote, const char *url) { ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc); @@ -134,10 +111,14 @@ struct remotes_hash_key { }; static int remotes_hash_cmp(const void *unused_cmp_data, - const struct remote *a, - const struct remote *b, - const struct remotes_hash_key *key) + const void *entry, + const void *entry_or_key, + const void *keydata) { + const struct remote *a = entry; + const struct remote *b = entry_or_key; + const struct remotes_hash_key *key = keydata; + if (key) return strncmp(a->name, key->str, key->len) || a->name[key->len]; else @@ -147,7 +128,7 @@ static int remotes_hash_cmp(const void *unused_cmp_data, static inline void init_remotes_hash(void) { if (!remotes_hash.cmpfn) - hashmap_init(&remotes_hash, (hashmap_cmp_fn)remotes_hash_cmp, NULL, 0); + hashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0); } static struct remote *make_remote(const char *name, int len) @@ -169,9 +150,13 @@ static struct remote *make_remote(const char *name, int len) ret = xcalloc(1, sizeof(struct remote)); ret->prune = -1; /* unspecified */ + ret->prune_tags = -1; /* unspecified */ + ret->name = xstrndup(name, len); + refspec_init(&ret->push, REFSPEC_PUSH); + refspec_init(&ret->fetch, REFSPEC_FETCH); + ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc); remotes[remotes_nr++] = ret; - ret->name = xstrndup(name, len); hashmap_entry_init(ret, lookup_entry.hash); replaced = hashmap_put(&remotes_hash, ret); @@ -269,9 +254,9 @@ static void read_remotes_file(struct remote *remote) if (skip_prefix(buf.buf, "URL:", &v)) add_url_alias(remote, xstrdup(skip_spaces(v))); else if (skip_prefix(buf.buf, "Push:", &v)) - add_push_refspec(remote, xstrdup(skip_spaces(v))); + refspec_append(&remote->push, skip_spaces(v)); else if (skip_prefix(buf.buf, "Pull:", &v)) - add_fetch_refspec(remote, xstrdup(skip_spaces(v))); + refspec_append(&remote->fetch, skip_spaces(v)); } strbuf_release(&buf); fclose(f); @@ -310,15 +295,19 @@ static void read_branches_file(struct remote *remote) frag = "master"; add_url_alias(remote, strbuf_detach(&buf, NULL)); - add_fetch_refspec(remote, xstrfmt("refs/heads/%s:refs/heads/%s", - frag, remote->name)); + strbuf_addf(&buf, "refs/heads/%s:refs/heads/%s", + frag, remote->name); + refspec_append(&remote->fetch, buf.buf); /* * Cogito compatible push: push current HEAD to remote #branch * (master if missing) */ - add_push_refspec(remote, xstrfmt("HEAD:refs/heads/%s", frag)); + strbuf_reset(&buf); + strbuf_addf(&buf, "HEAD:refs/heads/%s", frag); + refspec_append(&remote->push, buf.buf); remote->fetch_tags = 1; /* always auto-follow */ + strbuf_release(&buf); } static int handle_config(const char *key, const char *value, void *cb) @@ -371,7 +360,7 @@ static int handle_config(const char *key, const char *value, void *cb) return 0; /* Handle remote.<name>.* variables */ if (*name == '/') { - warning("Config remote shorthand cannot begin with '/': %s", + warning(_("config remote shorthand cannot begin with '/': %s"), name); return 0; } @@ -387,6 +376,8 @@ static int handle_config(const char *key, const char *value, void *cb) remote->skip_default_update = git_config_bool(key, value); else if (!strcmp(subkey, "prune")) remote->prune = git_config_bool(key, value); + else if (!strcmp(subkey, "prunetags")) + remote->prune_tags = git_config_bool(key, value); else if (!strcmp(subkey, "url")) { const char *v; if (git_config_string(&v, key, value)) @@ -401,12 +392,14 @@ static int handle_config(const char *key, const char *value, void *cb) const char *v; if (git_config_string(&v, key, value)) return -1; - add_push_refspec(remote, v); + refspec_append(&remote->push, v); + free((char *)v); } else if (!strcmp(subkey, "fetch")) { const char *v; if (git_config_string(&v, key, value)) return -1; - add_fetch_refspec(remote, v); + refspec_append(&remote->fetch, v); + free((char *)v); } else if (!strcmp(subkey, "receivepack")) { const char *v; if (git_config_string(&v, key, value)) @@ -414,7 +407,7 @@ static int handle_config(const char *key, const char *value, void *cb) if (!remote->receivepack) remote->receivepack = v; else - error("more than one receivepack given, using the first"); + error(_("more than one receivepack given, using the first")); } else if (!strcmp(subkey, "uploadpack")) { const char *v; if (git_config_string(&v, key, value)) @@ -422,7 +415,7 @@ static int handle_config(const char *key, const char *value, void *cb) if (!remote->uploadpack) remote->uploadpack = v; else - error("more than one uploadpack given, using the first"); + error(_("more than one uploadpack given, using the first")); } else if (!strcmp(subkey, "tagopt")) { if (!strcmp(value, "--no-tags")) remote->fetch_tags = -1; @@ -462,7 +455,6 @@ static void alias_all_urls(void) static void read_config(void) { static int loaded; - struct object_id oid; int flag; if (loaded) @@ -471,7 +463,7 @@ static void read_config(void) current_branch = NULL; if (startup_info->have_repository) { - const char *head_ref = resolve_ref_unsafe("HEAD", 0, oid.hash, &flag); + const char *head_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flag); if (head_ref && (flag & REF_ISSYMREF) && skip_prefix(head_ref, "refs/heads/", &head_ref)) { current_branch = make_branch(head_ref, 0); @@ -481,158 +473,6 @@ static void read_config(void) alias_all_urls(); } -static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) -{ - int i; - struct refspec *rs = xcalloc(nr_refspec, sizeof(*rs)); - - for (i = 0; i < nr_refspec; i++) { - size_t llen; - int is_glob; - const char *lhs, *rhs; - int flags; - - is_glob = 0; - - lhs = refspec[i]; - if (*lhs == '+') { - rs[i].force = 1; - lhs++; - } - - rhs = strrchr(lhs, ':'); - - /* - * Before going on, special case ":" (or "+:") as a refspec - * for pushing matching refs. - */ - if (!fetch && rhs == lhs && rhs[1] == '\0') { - rs[i].matching = 1; - continue; - } - - if (rhs) { - size_t rlen = strlen(++rhs); - is_glob = (1 <= rlen && strchr(rhs, '*')); - rs[i].dst = xstrndup(rhs, rlen); - } - - llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); - if (1 <= llen && memchr(lhs, '*', llen)) { - if ((rhs && !is_glob) || (!rhs && fetch)) - goto invalid; - is_glob = 1; - } else if (rhs && is_glob) { - goto invalid; - } - - rs[i].pattern = is_glob; - rs[i].src = xstrndup(lhs, llen); - flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); - - if (fetch) { - struct object_id unused; - - /* LHS */ - if (!*rs[i].src) - ; /* empty is ok; it means "HEAD" */ - else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused)) - rs[i].exact_sha1 = 1; /* ok */ - else if (!check_refname_format(rs[i].src, flags)) - ; /* valid looking ref is ok */ - else - goto invalid; - /* RHS */ - if (!rs[i].dst) - ; /* missing is ok; it is the same as empty */ - else if (!*rs[i].dst) - ; /* empty is ok; it means "do not store" */ - else if (!check_refname_format(rs[i].dst, flags)) - ; /* valid looking ref is ok */ - else - goto invalid; - } else { - /* - * LHS - * - empty is allowed; it means delete. - * - when wildcarded, it must be a valid looking ref. - * - otherwise, it must be an extended SHA-1, but - * there is no existing way to validate this. - */ - if (!*rs[i].src) - ; /* empty is ok */ - else if (is_glob) { - if (check_refname_format(rs[i].src, flags)) - goto invalid; - } - else - ; /* anything goes, for now */ - /* - * RHS - * - missing is allowed, but LHS then must be a - * valid looking ref. - * - empty is not allowed. - * - otherwise it must be a valid looking ref. - */ - if (!rs[i].dst) { - if (check_refname_format(rs[i].src, flags)) - goto invalid; - } else if (!*rs[i].dst) { - goto invalid; - } else { - if (check_refname_format(rs[i].dst, flags)) - goto invalid; - } - } - } - return rs; - - invalid: - if (verify) { - /* - * nr_refspec must be greater than zero and i must be valid - * since it is only possible to reach this point from within - * the for loop above. - */ - free_refspec(i+1, rs); - return NULL; - } - die("Invalid refspec '%s'", refspec[i]); -} - -int valid_fetch_refspec(const char *fetch_refspec_str) -{ - struct refspec *refspec; - - refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1); - free_refspec(1, refspec); - return !!refspec; -} - -struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec) -{ - return parse_refspec_internal(nr_refspec, refspec, 1, 0); -} - -struct refspec *parse_push_refspec(int nr_refspec, const char **refspec) -{ - return parse_refspec_internal(nr_refspec, refspec, 0, 0); -} - -void free_refspec(int nr_refspec, struct refspec *refspec) -{ - int i; - - if (!refspec) - return; - - for (i = 0; i < nr_refspec; i++) { - free(refspec[i].src); - free(refspec[i].dst); - } - free(refspec); -} - static int valid_remote_nick(const char *name) { if (!name[0] || is_dot_or_dotdot(name)) @@ -672,6 +512,35 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit) return remote_for_branch(branch, explicit); } +const char *remote_ref_for_branch(struct branch *branch, int for_push, + int *explicit) +{ + if (branch) { + if (!for_push) { + if (branch->merge_nr) { + if (explicit) + *explicit = 1; + return branch->merge_name[0]; + } + } else { + const char *dst, *remote_name = + pushremote_for_branch(branch, NULL); + struct remote *remote = remote_get(remote_name); + + if (remote && remote->push.nr && + (dst = apply_refspecs(&remote->push, + branch->refname))) { + if (explicit) + *explicit = 1; + return dst; + } + } + } + if (explicit) + *explicit = 0; + return ""; +} + static struct remote *remote_get_1(const char *name, const char *(*get_default)(struct branch *, int *)) { @@ -696,8 +565,6 @@ static struct remote *remote_get_1(const char *name, add_url_alias(ret, name); if (!valid_remote(ret)) return NULL; - ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec); - ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec); return ret; } @@ -728,12 +595,6 @@ int for_each_remote(each_remote_fn fn, void *priv) struct remote *r = remotes[i]; if (!r) continue; - if (!r->fetch) - r->fetch = parse_fetch_refspec(r->fetch_refspec_nr, - r->fetch_refspec); - if (!r->push) - r->push = parse_push_refspec(r->push_refspec_nr, - r->push_refspec); result = fn(r, priv); } return result; @@ -760,7 +621,7 @@ static void handle_duplicate(struct ref *ref1, struct ref *ref2) * FETCH_HEAD_IGNORE entries always appear at * the end of the list. */ - die(_("Internal error")); + BUG("Internal error"); } } free(ref2->peer_ref); @@ -820,7 +681,7 @@ static int match_name_with_pattern(const char *key, const char *name, size_t namelen; int ret; if (!kstar) - die("Key '%s' of pattern had no '*'", key); + die(_("key '%s' of pattern had no '*'"), key); klen = kstar - key; ksuffixlen = strlen(kstar + 1); namelen = strlen(name); @@ -830,7 +691,7 @@ static int match_name_with_pattern(const char *key, const char *name, struct strbuf sb = STRBUF_INIT; const char *vstar = strchr(value, '*'); if (!vstar) - die("Value '%s' of pattern has no '*'", value); + die(_("value '%s' of pattern has no '*'"), value); strbuf_add(&sb, value, vstar - value); strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen); strbuf_addstr(&sb, vstar + 1); @@ -839,16 +700,18 @@ static int match_name_with_pattern(const char *key, const char *name, return ret; } -static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results) +static void query_refspecs_multiple(struct refspec *rs, + struct refspec_item *query, + struct string_list *results) { int i; int find_src = !query->src; if (find_src && !query->dst) - error("query_refspecs_multiple: need either src or dst"); + BUG("query_refspecs_multiple: need either src or dst"); - for (i = 0; i < ref_count; i++) { - struct refspec *refspec = &refs[i]; + for (i = 0; i < rs->nr; i++) { + struct refspec_item *refspec = &rs->items[i]; const char *key = find_src ? refspec->dst : refspec->src; const char *value = find_src ? refspec->src : refspec->dst; const char *needle = find_src ? query->dst : query->src; @@ -865,7 +728,7 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct } } -int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) +int query_refspecs(struct refspec *rs, struct refspec_item *query) { int i; int find_src = !query->src; @@ -873,10 +736,10 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) char **result = find_src ? &query->src : &query->dst; if (find_src && !query->dst) - return error("query_refspecs: need either src or dst"); + BUG("query_refspecs: need either src or dst"); - for (i = 0; i < ref_count; i++) { - struct refspec *refspec = &refs[i]; + for (i = 0; i < rs->nr; i++) { + struct refspec_item *refspec = &rs->items[i]; const char *key = find_src ? refspec->dst : refspec->src; const char *value = find_src ? refspec->src : refspec->dst; @@ -896,23 +759,22 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) return -1; } -char *apply_refspecs(struct refspec *refspecs, int nr_refspec, - const char *name) +char *apply_refspecs(struct refspec *rs, const char *name) { - struct refspec query; + struct refspec_item query; - memset(&query, 0, sizeof(struct refspec)); + memset(&query, 0, sizeof(struct refspec_item)); query.src = (char *)name; - if (query_refspecs(refspecs, nr_refspec, &query)) + if (query_refspecs(rs, &query)) return NULL; return query.dst; } -int remote_find_tracking(struct remote *remote, struct refspec *refspec) +int remote_find_tracking(struct remote *remote, struct refspec_item *refspec) { - return query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec); + return query_refspecs(&remote->fetch, refspec); } static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen, @@ -1081,7 +943,7 @@ static int try_explicit_object_name(const char *name, return 0; } - if (get_sha1(name, oid.hash)) + if (get_oid(name, &oid)) return -1; if (match) { @@ -1101,26 +963,26 @@ static struct ref *make_linked_ref(const char *name, struct ref ***tail) static char *guess_ref(const char *name, struct ref *peer) { struct strbuf buf = STRBUF_INIT; - struct object_id oid; const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING, - oid.hash, NULL); + NULL, NULL); if (!r) return NULL; - if (starts_with(r, "refs/heads/")) + if (starts_with(r, "refs/heads/")) { strbuf_addstr(&buf, "refs/heads/"); - else if (starts_with(r, "refs/tags/")) + } else if (starts_with(r, "refs/tags/")) { strbuf_addstr(&buf, "refs/tags/"); - else + } else { return NULL; + } strbuf_addstr(&buf, name); return strbuf_detach(&buf, NULL); } static int match_explicit_lhs(struct ref *src, - struct refspec *rs, + struct refspec_item *rs, struct ref **match, int *allocated_match) { @@ -1135,18 +997,74 @@ static int match_explicit_lhs(struct ref *src, * way to delete 'other' ref at the remote end. */ if (try_explicit_object_name(rs->src, match) < 0) - return error("src refspec %s does not match any.", rs->src); + return error(_("src refspec %s does not match any"), rs->src); if (allocated_match) *allocated_match = 1; return 0; default: - return error("src refspec %s matches more than one.", rs->src); + return error(_("src refspec %s matches more than one"), rs->src); + } +} + +static void show_push_unqualified_ref_name_error(const char *dst_value, + const char *matched_src_name) +{ + struct object_id oid; + enum object_type type; + + /* + * TRANSLATORS: "matches '%s'%" is the <dst> part of "git push + * <remote> <src>:<dst>" push, and "being pushed ('%s')" is + * the <src>. + */ + error(_("The destination you provided is not a full refname (i.e.,\n" + "starting with \"refs/\"). We tried to guess what you meant by:\n" + "\n" + "- Looking for a ref that matches '%s' on the remote side.\n" + "- Checking if the <src> being pushed ('%s')\n" + " is a ref in \"refs/{heads,tags}/\". If so we add a corresponding\n" + " refs/{heads,tags}/ prefix on the remote side.\n" + "\n" + "Neither worked, so we gave up. You must fully qualify the ref."), + dst_value, matched_src_name); + + if (!advice_push_unqualified_ref_name) + return; + + if (get_oid(matched_src_name, &oid)) + BUG("'%s' is not a valid object, " + "match_explicit_lhs() should catch this!", + matched_src_name); + type = oid_object_info(the_repository, &oid, NULL); + if (type == OBJ_COMMIT) { + advise(_("The <src> part of the refspec is a commit object.\n" + "Did you mean to create a new branch by pushing to\n" + "'%s:refs/heads/%s'?"), + matched_src_name, dst_value); + } else if (type == OBJ_TAG) { + advise(_("The <src> part of the refspec is a tag object.\n" + "Did you mean to create a new tag by pushing to\n" + "'%s:refs/tags/%s'?"), + matched_src_name, dst_value); + } else if (type == OBJ_TREE) { + advise(_("The <src> part of the refspec is a tree object.\n" + "Did you mean to tag a new tree by pushing to\n" + "'%s:refs/tags/%s'?"), + matched_src_name, dst_value); + } else if (type == OBJ_BLOB) { + advise(_("The <src> part of the refspec is a blob object.\n" + "Did you mean to tag a new blob by pushing to\n" + "'%s:refs/tags/%s'?"), + matched_src_name, dst_value); + } else { + BUG("'%s' should be commit/tag/tree/blob, is '%d'", + matched_src_name, type); } } static int match_explicit(struct ref *src, struct ref *dst, struct ref ***dst_tail, - struct refspec *rs) + struct refspec_item *rs) { struct ref *matched_src, *matched_dst; int allocated_src; @@ -1162,16 +1080,15 @@ static int match_explicit(struct ref *src, struct ref *dst, return -1; if (!dst_value) { - struct object_id oid; int flag; dst_value = resolve_ref_unsafe(matched_src->name, RESOLVE_REF_READING, - oid.hash, &flag); + NULL, &flag); if (!dst_value || ((flag & REF_ISSYMREF) && !starts_with(dst_value, "refs/heads/"))) - die("%s cannot be resolved to branch.", + die(_("%s cannot be resolved to branch"), matched_src->name); } @@ -1179,33 +1096,30 @@ static int match_explicit(struct ref *src, struct ref *dst, case 1: break; case 0: - if (starts_with(dst_value, "refs/")) + if (starts_with(dst_value, "refs/")) { matched_dst = make_linked_ref(dst_value, dst_tail); - else if (is_null_oid(&matched_src->new_oid)) - error("unable to delete '%s': remote ref does not exist", + } else if (is_null_oid(&matched_src->new_oid)) { + error(_("unable to delete '%s': remote ref does not exist"), dst_value); - else if ((dst_guess = guess_ref(dst_value, matched_src))) { + } else if ((dst_guess = guess_ref(dst_value, matched_src))) { matched_dst = make_linked_ref(dst_guess, dst_tail); free(dst_guess); - } else - error("unable to push to unqualified destination: %s\n" - "The destination refspec neither matches an " - "existing ref on the remote nor\n" - "begins with refs/, and we are unable to " - "guess a prefix based on the source ref.", - dst_value); + } else { + show_push_unqualified_ref_name_error(dst_value, + matched_src->name); + } break; default: matched_dst = NULL; - error("dst refspec %s matches more than one.", + error(_("dst refspec %s matches more than one"), dst_value); break; } if (!matched_dst) return -1; if (matched_dst->peer_ref) - return error("dst ref %s receives from more than one src.", - matched_dst->name); + return error(_("dst ref %s receives from more than one src"), + matched_dst->name); else { matched_dst->peer_ref = allocated_src ? matched_src : @@ -1216,36 +1130,37 @@ static int match_explicit(struct ref *src, struct ref *dst, } static int match_explicit_refs(struct ref *src, struct ref *dst, - struct ref ***dst_tail, struct refspec *rs, - int rs_nr) + struct ref ***dst_tail, struct refspec *rs) { int i, errs; - for (i = errs = 0; i < rs_nr; i++) - errs += match_explicit(src, dst, dst_tail, &rs[i]); + for (i = errs = 0; i < rs->nr; i++) + errs += match_explicit(src, dst, dst_tail, &rs->items[i]); return errs; } -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) +static char *get_ref_match(const struct refspec *rs, const struct ref *ref, + int send_mirror, int direction, + const struct refspec_item **ret_pat) { - const struct refspec *pat; + const struct refspec_item *pat; char *name; int i; int matching_refs = -1; - for (i = 0; i < rs_nr; i++) { - if (rs[i].matching && - (matching_refs == -1 || rs[i].force)) { + for (i = 0; i < rs->nr; i++) { + const struct refspec_item *item = &rs->items[i]; + if (item->matching && + (matching_refs == -1 || item->force)) { matching_refs = i; continue; } - if (rs[i].pattern) { - const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src; + if (item->pattern) { + const char *dst_side = item->dst ? item->dst : item->src; int match; if (direction == FROM_SRC) - match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name); + match = match_name_with_pattern(item->src, ref->name, dst_side, &name); else - match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name); + match = match_name_with_pattern(dst_side, ref->name, item->src, &name); if (match) { matching_refs = i; break; @@ -1255,7 +1170,7 @@ static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref if (matching_refs == -1) return NULL; - pat = rs + matching_refs; + pat = &rs->items[matching_refs]; if (pat->matching) { /* * "matching refs"; traditionally we pushed everything @@ -1290,7 +1205,7 @@ static void add_to_tips(struct tips *tips, const struct object_id *oid) if (is_null_oid(oid)) return; - commit = lookup_commit_reference_gently(oid, 1); + commit = lookup_commit_reference_gently(the_repository, oid, 1); if (!commit || (commit->object.flags & TMP_MARK)) return; commit->object.flags |= TMP_MARK; @@ -1330,7 +1245,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds continue; /* not a tag */ if (string_list_has_string(&dst_tag, ref->name)) continue; /* they already have it */ - if (sha1_object_info(ref->new_oid.hash, NULL) != OBJ_TAG) + if (oid_object_info(the_repository, &ref->new_oid, NULL) != OBJ_TAG) continue; /* be conservative */ item = string_list_append(&src_tag, ref->name); item->util = ref; @@ -1345,14 +1260,42 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds * sent to the other side. */ if (sent_tips.nr) { + const int reachable_flag = 1; + struct commit_list *found_commits; + struct commit **src_commits; + int nr_src_commits = 0, alloc_src_commits = 16; + ALLOC_ARRAY(src_commits, alloc_src_commits); + for_each_string_list_item(item, &src_tag) { struct ref *ref = item->util; + struct commit *commit; + + if (is_null_oid(&ref->new_oid)) + continue; + commit = lookup_commit_reference_gently(the_repository, + &ref->new_oid, + 1); + if (!commit) + /* not pushing a commit, which is not an error */ + continue; + + ALLOC_GROW(src_commits, nr_src_commits + 1, alloc_src_commits); + src_commits[nr_src_commits++] = commit; + } + + found_commits = get_reachable_subset(sent_tips.tip, sent_tips.nr, + src_commits, nr_src_commits, + reachable_flag); + + for_each_string_list_item(item, &src_tag) { struct ref *dst_ref; + struct ref *ref = item->util; struct commit *commit; if (is_null_oid(&ref->new_oid)) continue; - commit = lookup_commit_reference_gently(&ref->new_oid, + commit = lookup_commit_reference_gently(the_repository, + &ref->new_oid, 1); if (!commit) /* not pushing a commit, which is not an error */ @@ -1362,7 +1305,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds * Is this tag, which they do not have, reachable from * any of the commits we are sending? */ - if (!in_merge_bases_many(commit, sent_tips.nr, sent_tips.tip)) + if (!(commit->object.flags & reachable_flag)) continue; /* Add it in */ @@ -1370,7 +1313,12 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds oidcpy(&dst_ref->new_oid, &ref->new_oid); dst_ref->peer_ref = copy_ref(ref); } + + clear_commit_marks_many(nr_src_commits, src_commits, reachable_flag); + free(src_commits); + free_commit_list(found_commits); } + string_list_clear(&src_tag, 0); free(sent_tips.tip); } @@ -1397,22 +1345,20 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref) * but we can catch some errors early before even talking to the * remote side. */ -int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names) +int check_push_refs(struct ref *src, struct refspec *rs) { - struct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names); int ret = 0; int i; - for (i = 0; i < nr_refspec; i++) { - struct refspec *rs = refspec + i; + for (i = 0; i < rs->nr; i++) { + struct refspec_item *item = &rs->items[i]; - if (rs->pattern || rs->matching) + if (item->pattern || item->matching) continue; - ret |= match_explicit_lhs(src, rs, NULL, NULL); + ret |= match_explicit_lhs(src, item, NULL, NULL); } - free_refspec(nr_refspec, refspec); return ret; } @@ -1425,32 +1371,29 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names) * dst (e.g. pushing to a new branch, done in match_explicit_refs). */ int match_push_refs(struct ref *src, struct ref **dst, - int nr_refspec, const char **refspec, int flags) + struct refspec *rs, 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 *ref, **dst_tail = tail_ref(dst); struct string_list dst_ref_index = STRING_LIST_INIT_NODUP; - if (!nr_refspec) { - nr_refspec = 1; - refspec = default_refspec; - } - rs = parse_push_refspec(nr_refspec, (const char **) refspec); - errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec); + /* If no refspec is provided, use the default ":" */ + if (!rs->nr) + refspec_append(rs, ":"); + + errs = match_explicit_refs(src, *dst, &dst_tail, rs); /* pick the remainder */ for (ref = src; ref; ref = ref->next) { struct string_list_item *dst_item; struct ref *dst_peer; - const struct refspec *pat = NULL; + const struct refspec_item *pat = NULL; char *dst_name; - dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat); + dst_name = get_ref_match(rs, ref, send_mirror, FROM_SRC, &pat); if (!dst_name) continue; @@ -1499,7 +1442,7 @@ int match_push_refs(struct ref *src, struct ref **dst, /* We're already sending something to this ref. */ continue; - src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL); + src_name = get_ref_match(rs, ref, send_mirror, FROM_DST, NULL); if (src_name) { if (!src_ref_index.nr) prepare_ref_index(&src_ref_index, src); @@ -1511,6 +1454,7 @@ int match_push_refs(struct ref *src, struct ref **dst, } string_list_clear(&src_ref_index, 0); } + if (errs) return -1; return 0; @@ -1532,7 +1476,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, ref->deletion = is_null_oid(&ref->new_oid); if (!ref->deletion && - !oidcmp(&ref->old_oid, &ref->new_oid)) { + oideq(&ref->old_oid, &ref->new_oid)) { ref->status = REF_STATUS_UPTODATE; continue; } @@ -1547,7 +1491,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, * branch. */ if (ref->expect_old_sha1) { - if (oidcmp(&ref->old_oid, &ref->old_oid_expect)) + if (!oideq(&ref->old_oid, &ref->old_oid_expect)) reject_reason = REF_STATUS_REJECT_STALE; else /* If the ref isn't stale then force the update. */ @@ -1580,8 +1524,8 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS; else if (!has_object_file(&ref->old_oid)) reject_reason = REF_STATUS_REJECT_FETCH_FIRST; - else if (!lookup_commit_reference_gently(&ref->old_oid, 1) || - !lookup_commit_reference_gently(&ref->new_oid, 1)) + else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1) || + !lookup_commit_reference_gently(the_repository, &ref->new_oid, 1)) reject_reason = REF_STATUS_REJECT_NEEDS_FORCE; else if (!ref_newer(&ref->new_oid, &ref->old_oid)) reject_reason = REF_STATUS_REJECT_NONFASTFORWARD; @@ -1628,7 +1572,7 @@ static void set_merge(struct branch *ret) strcmp(ret->remote_name, ".")) continue; if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]), - oid.hash, &ref) == 1) + &oid, &ref) == 1) ret->merge[i]->dst = ref; else ret->merge[i]->dst = xstrdup(ret->merge_name[i]); @@ -1707,7 +1651,7 @@ static const char *tracking_for_push_dest(struct remote *remote, { char *ret; - ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname); + ret = apply_refspecs(&remote->fetch, refname); if (!ret) return error_buf(err, _("push destination '%s' on remote '%s' has no local tracking branch"), @@ -1725,12 +1669,11 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err) _("branch '%s' has no remote for pushing"), branch->name); - if (remote->push_refspec_nr) { + if (remote->push.nr) { char *dst; const char *ret; - dst = apply_refspecs(remote->push, remote->push_refspec_nr, - branch->refname); + dst = apply_refspecs(&remote->push, branch->refname); if (!dst) return error_buf(err, _("push refspecs for '%s' do not include '%s'"), @@ -1773,7 +1716,7 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err) } } - die("BUG: unhandled push situation"); + BUG("unhandled push situation"); } const char *branch_get_push(struct branch *branch, struct strbuf *err) @@ -1788,10 +1731,9 @@ const char *branch_get_push(struct branch *branch, struct strbuf *err) static int ignore_symref_update(const char *refname) { - struct object_id oid; int flag; - if (!resolve_ref_unsafe(refname, 0, oid.hash, &flag)) + if (!resolve_ref_unsafe(refname, 0, NULL, &flag)) return 0; /* non-existing refs are OK */ return (flag & REF_ISSYMREF); } @@ -1804,7 +1746,7 @@ static int ignore_symref_update(const char *refname) * local symbolic ref. */ static struct ref *get_expanded_map(const struct ref *remote_refs, - const struct refspec *refspec) + const struct refspec_item *refspec) { const struct ref *ref; struct ref *ret = NULL; @@ -1835,11 +1777,18 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name) { const struct ref *ref; + const struct ref *best_match = NULL; + int best_score = 0; + for (ref = refs; ref; ref = ref->next) { - if (refname_match(name, ref->name)) - return ref; + int score = refname_match(name, ref->name); + + if (best_score < score) { + best_match = ref; + best_score = score; + } } - return NULL; + return best_match; } struct ref *get_remote_ref(const struct ref *remote_refs, const char *name) @@ -1869,7 +1818,7 @@ static struct ref *get_local_ref(const char *name) } int get_fetch_map(const struct ref *remote_refs, - const struct refspec *refspec, + const struct refspec_item *refspec, struct ref ***tail, int missing_ok) { @@ -1883,11 +1832,12 @@ int get_fetch_map(const struct ref *remote_refs, if (refspec->exact_sha1) { ref_map = alloc_ref(name); get_oid_hex(name, &ref_map->old_oid); + ref_map->exact_oid = 1; } else { ref_map = get_remote_ref(remote_refs, name); } if (!missing_ok && !ref_map) - die("Couldn't find remote ref %s", name); + die(_("couldn't find remote ref %s"), name); if (ref_map) { ref_map->peer_ref = get_local_ref(refspec->dst); if (ref_map->peer_ref && refspec->force) @@ -1900,7 +1850,7 @@ int get_fetch_map(const struct ref *remote_refs, if (!starts_with((*rmp)->peer_ref->name, "refs/") || check_refname_format((*rmp)->peer_ref->name, 0)) { struct ref *ignore = *rmp; - error("* Ignoring funny ref '%s' locally", + error(_("* Ignoring funny ref '%s' locally"), (*rmp)->peer_ref->name); *rmp = (*rmp)->next; free(ignore->peer_ref); @@ -1929,64 +1879,24 @@ int resolve_remote_symref(struct ref *ref, struct ref *list) return 1; } -static void unmark_and_free(struct commit_list *list, unsigned int mark) -{ - while (list) { - struct commit *commit = pop_commit(&list); - commit->object.flags &= ~mark; - } -} - -int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid) -{ - struct object *o; - struct commit *old, *new; - struct commit_list *list, *used; - int found = 0; - - /* - * Both new and old must be commit-ish and new is descendant of - * old. Otherwise we require --force. - */ - o = deref_tag(parse_object(old_oid), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - old = (struct commit *) o; - - o = deref_tag(parse_object(new_oid), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - new = (struct commit *) o; - - if (parse_commit(new) < 0) - return 0; - - used = list = NULL; - commit_list_insert(new, &list); - while (list) { - new = pop_most_recent_commit(&list, TMP_MARK); - commit_list_insert(new, &used); - if (new == old) { - found = 1; - break; - } - } - unmark_and_free(list, TMP_MARK); - unmark_and_free(used, TMP_MARK); - return found; -} - /* - * Compare a branch with its upstream, and save their differences (number - * of commits) in *num_ours and *num_theirs. The name of the upstream branch - * (or NULL if no upstream is defined) is returned via *upstream_name, if it - * is not itself NULL. + * Lookup the upstream branch for the given branch and if present, optionally + * compute the commit ahead/behind values for the pair. + * + * If abf is AHEAD_BEHIND_FULL, compute the full ahead/behind and return the + * counts in *num_ours and *num_theirs. If abf is AHEAD_BEHIND_QUICK, skip + * the (potentially expensive) a/b computation (*num_ours and *num_theirs are + * set to zero). + * + * The name of the upstream branch (or NULL if no upstream is defined) is + * returned via *upstream_name, if it is not itself NULL. * * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no - * upstream defined, or ref does not exist), 0 otherwise. + * upstream defined, or ref does not exist). Returns 0 if the commits are + * identical. Returns 1 if commits are different. */ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, - const char **upstream_name) + const char **upstream_name, enum ahead_behind_flags abf) { struct object_id oid; struct commit *ours, *theirs; @@ -2002,23 +1912,27 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, return -1; /* Cannot stat if what we used to build on no longer exists */ - if (read_ref(base, oid.hash)) + if (read_ref(base, &oid)) return -1; - theirs = lookup_commit_reference(&oid); + theirs = lookup_commit_reference(the_repository, &oid); if (!theirs) return -1; - if (read_ref(branch->refname, oid.hash)) + if (read_ref(branch->refname, &oid)) return -1; - ours = lookup_commit_reference(&oid); + ours = lookup_commit_reference(the_repository, &oid); if (!ours) return -1; + *num_theirs = *num_ours = 0; + /* are we the same? */ - if (theirs == ours) { - *num_theirs = *num_ours = 0; + if (theirs == ours) return 0; - } + if (abf == AHEAD_BEHIND_QUICK) + return 1; + if (abf != AHEAD_BEHIND_FULL) + BUG("stat_tracking_info: invalid abf '%d'", abf); /* Run "rev-list --left-right ours...theirs" internally... */ argv_array_push(&argv, ""); /* ignored */ @@ -2028,14 +1942,12 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, oid_to_hex(&theirs->object.oid)); argv_array_push(&argv, "--"); - init_revisions(&revs, NULL); + repo_init_revisions(the_repository, &revs, NULL); setup_revisions(argv.argc, argv.argv, &revs, NULL); if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); /* ... and count the commits on each side. */ - *num_ours = 0; - *num_theirs = 0; while (1) { struct commit *c = get_revision(&revs); if (!c) @@ -2051,20 +1963,22 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, clear_commit_marks(theirs, ALL_REV_FLAGS); argv_array_clear(&argv); - return 0; + return 1; } /* * Return true when there is anything to report, otherwise false. */ -int format_tracking_info(struct branch *branch, struct strbuf *sb) +int format_tracking_info(struct branch *branch, struct strbuf *sb, + enum ahead_behind_flags abf) { - int ours, theirs; + int ours, theirs, sti; const char *full_base; char *base; int upstream_is_gone = 0; - if (stat_tracking_info(branch, &ours, &theirs, &full_base) < 0) { + sti = stat_tracking_info(branch, &ours, &theirs, &full_base, abf); + if (sti < 0) { if (!full_base) return 0; upstream_is_gone = 1; @@ -2078,10 +1992,17 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) if (advice_status_hints) strbuf_addstr(sb, _(" (use \"git branch --unset-upstream\" to fixup)\n")); - } else if (!ours && !theirs) { + } else if (!sti) { strbuf_addf(sb, - _("Your branch is up-to-date with '%s'.\n"), + _("Your branch is up to date with '%s'.\n"), base); + } else if (abf == AHEAD_BEHIND_QUICK) { + strbuf_addf(sb, + _("Your branch and '%s' refer to different commits.\n"), + base); + if (advice_status_hints) + strbuf_addf(sb, _(" (use \"%s\" for details)\n"), + "git status --ahead-behind"); } else if (!theirs) { strbuf_addf(sb, Q_("Your branch is ahead of '%s' by %d commit.\n", @@ -2167,7 +2088,7 @@ struct ref *guess_remote_head(const struct ref *head, /* If refs/heads/master could be right, it is. */ if (!all) { r = find_ref_by_name(refs, "refs/heads/master"); - if (r && !oidcmp(&r->old_oid, &head->old_oid)) + if (r && oideq(&r->old_oid, &head->old_oid)) return copy_ref(r); } @@ -2175,7 +2096,7 @@ struct ref *guess_remote_head(const struct ref *head, for (r = refs; r; r = r->next) { if (r != head && starts_with(r->name, "refs/heads/") && - !oidcmp(&r->old_oid, &head->old_oid)) { + oideq(&r->old_oid, &head->old_oid)) { *tail = copy_ref(r); tail = &((*tail)->next); if (!all) @@ -2189,8 +2110,7 @@ struct ref *guess_remote_head(const struct ref *head, struct stale_heads_info { struct string_list *ref_names; struct ref **stale_refs_tail; - struct refspec *refs; - int ref_count; + struct refspec *rs; }; static int get_stale_heads_cb(const char *refname, const struct object_id *oid, @@ -2198,12 +2118,12 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid, { struct stale_heads_info *info = cb_data; struct string_list matches = STRING_LIST_INIT_DUP; - struct refspec query; + struct refspec_item query; int i, stale = 1; - memset(&query, 0, sizeof(struct refspec)); + memset(&query, 0, sizeof(struct refspec_item)); query.dst = (char *)refname; - query_refspecs_multiple(info->refs, info->ref_count, &query, &matches); + query_refspecs_multiple(info->rs, &query, &matches); if (matches.nr == 0) goto clean_exit; /* No matches */ @@ -2231,7 +2151,7 @@ clean_exit: return 0; } -struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map) +struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map) { struct ref *ref, *stale_refs = NULL; struct string_list ref_names = STRING_LIST_INIT_NODUP; @@ -2239,8 +2159,7 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet info.ref_names = &ref_names; info.stale_refs_tail = &stale_refs; - info.refs = refs; - info.ref_count = ref_count; + info.rs = rs; for (ref = fetch_map; ref; ref = ref->next) string_list_append(&ref_names, ref->name); string_list_sort(&ref_names); @@ -2297,9 +2216,10 @@ static int parse_push_cas_option(struct push_cas_option *cas, const char *arg, i if (!*colon) entry->use_tracking = 1; else if (!colon[1]) - hashclr(entry->expect); - else if (get_sha1(colon + 1, entry->expect)) - return error("cannot parse expected object name '%s'", colon + 1); + oidclr(&entry->expect); + else if (get_oid(colon + 1, &entry->expect)) + return error(_("cannot parse expected object name '%s'"), + colon + 1); return 0; } @@ -2324,10 +2244,10 @@ static int remote_tracking(struct remote *remote, const char *refname, { char *dst; - dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname); + dst = apply_refspecs(&remote->fetch, refname); if (!dst) return -1; /* no tracking ref for refname at remote */ - if (read_ref(dst, oid->hash)) + if (read_ref(dst, oid)) return -1; /* we know what the tracking ref is but we cannot read it */ return 0; } @@ -2345,7 +2265,7 @@ static void apply_cas(struct push_cas_option *cas, continue; ref->expect_old_sha1 = 1; if (!entry->use_tracking) - hashcpy(ref->old_oid_expect.hash, cas->entry[i].expect); + oidcpy(&ref->old_oid_expect, &entry->expect); else if (remote_tracking(remote, ref->name, &ref->old_oid_expect)) oidclr(&ref->old_oid_expect); return; |