]> git.ipfire.org Git - thirdparty/git.git/blobdiff - remote.c
Fix some printf format warnings
[thirdparty/git.git] / remote.c
index 570e11286ea295e825a23b1d5ed40c9fbd02be57..73d33f2584b061085fcaa0958c26b1c3d4665904 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -4,13 +4,15 @@
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
+#include "dir.h"
+#include "tag.h"
 
 static struct refspec s_tag_refspec = {
        0,
        1,
        0,
-       "refs/tags/",
-       "refs/tags/"
+       "refs/tags/*",
+       "refs/tags/*"
 };
 
 const struct refspec *tag_refspec = &s_tag_refspec;
@@ -26,6 +28,11 @@ struct rewrite {
        int instead_of_nr;
        int instead_of_alloc;
 };
+struct rewrites {
+       struct rewrite **rewrite;
+       int rewrite_alloc;
+       int rewrite_nr;
+};
 
 static struct remote **remotes;
 static int remotes_alloc;
@@ -37,15 +44,15 @@ static int branches_nr;
 
 static struct branch *current_branch;
 static const char *default_remote_name;
+static int explicit_default_remote_name;
 
-static struct rewrite **rewrite;
-static int rewrite_alloc;
-static int rewrite_nr;
+static struct rewrites rewrites;
+static struct rewrites rewrites_push;
 
 #define BUF_SIZE (2048)
 static char buffer[BUF_SIZE];
 
-static const char *alias_url(const char *url)
+static const char *alias_url(const char *url, struct rewrites *r)
 {
        int i, j;
        char *ret;
@@ -54,14 +61,14 @@ static const char *alias_url(const char *url)
 
        longest = NULL;
        longest_i = -1;
-       for (i = 0; i < rewrite_nr; i++) {
-               if (!rewrite[i])
+       for (i = 0; i < r->rewrite_nr; i++) {
+               if (!r->rewrite[i])
                        continue;
-               for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
-                       if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
+               for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
+                       if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
                            (!longest ||
-                            longest->len < rewrite[i]->instead_of[j].len)) {
-                               longest = &(rewrite[i]->instead_of[j]);
+                            longest->len < r->rewrite[i]->instead_of[j].len)) {
+                               longest = &(r->rewrite[i]->instead_of[j]);
                                longest_i = i;
                        }
                }
@@ -69,10 +76,10 @@ static const char *alias_url(const char *url)
        if (!longest)
                return url;
 
-       ret = xmalloc(rewrite[longest_i]->baselen +
+       ret = xmalloc(r->rewrite[longest_i]->baselen +
                     (strlen(url) - longest->len) + 1);
-       strcpy(ret, rewrite[longest_i]->base);
-       strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
+       strcpy(ret, r->rewrite[longest_i]->base);
+       strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
        return ret;
 }
 
@@ -98,9 +105,23 @@ static void add_url(struct remote *remote, const char *url)
        remote->url[remote->url_nr++] = url;
 }
 
+static void add_pushurl(struct remote *remote, const char *pushurl)
+{
+       ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
+       remote->pushurl[remote->pushurl_nr++] = pushurl;
+}
+
+static void add_pushurl_alias(struct remote *remote, const char *url)
+{
+       const char *pushurl = alias_url(url, &rewrites_push);
+       if (pushurl != url)
+               add_pushurl(remote, pushurl);
+}
+
 static void add_url_alias(struct remote *remote, const char *url)
 {
-       add_url(remote, alias_url(url));
+       add_url(remote, alias_url(url, &rewrites));
+       add_pushurl_alias(remote, url);
 }
 
 static struct remote *make_remote(const char *name, int len)
@@ -160,22 +181,22 @@ static struct branch *make_branch(const char *name, int len)
        return ret;
 }
 
-static struct rewrite *make_rewrite(const char *base, int len)
+static struct rewrite *make_rewrite(struct rewrites *r, const char *base, int len)
 {
        struct rewrite *ret;
        int i;
 
-       for (i = 0; i < rewrite_nr; i++) {
+       for (i = 0; i < r->rewrite_nr; i++) {
                if (len
-                   ? (len == rewrite[i]->baselen &&
-                      !strncmp(base, rewrite[i]->base, len))
-                   : !strcmp(base, rewrite[i]->base))
-                       return rewrite[i];
+                   ? (len == r->rewrite[i]->baselen &&
+                      !strncmp(base, r->rewrite[i]->base, len))
+                   : !strcmp(base, r->rewrite[i]->base))
+                       return r->rewrite[i];
        }
 
-       ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
+       ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc);
        ret = xcalloc(1, sizeof(struct rewrite));
-       rewrite[rewrite_nr++] = ret;
+       r->rewrite[r->rewrite_nr++] = ret;
        if (len) {
                ret->base = xstrndup(base, len);
                ret->baselen = len;
@@ -298,7 +319,7 @@ static void read_branches_file(struct remote *remote)
                strbuf_addstr(&branch, "HEAD:");
        }
        add_url_alias(remote, p);
-       add_fetch_refspec(remote, strbuf_detach(&branch, 0));
+       add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
        /*
         * Cogito compatible push: push current HEAD to remote #branch
         * (master if missing)
@@ -309,7 +330,7 @@ static void read_branches_file(struct remote *remote)
                strbuf_addf(&branch, ":refs/heads/%s", frag);
        else
                strbuf_addstr(&branch, ":refs/heads/master");
-       add_push_refspec(remote, strbuf_detach(&branch, 0));
+       add_push_refspec(remote, strbuf_detach(&branch, NULL));
        remote->fetch_tags = 1; /* always auto-follow */
 }
 
@@ -329,8 +350,10 @@ static int handle_config(const char *key, const char *value, void *cb)
                        if (!value)
                                return config_error_nonbool(key);
                        branch->remote_name = xstrdup(value);
-                       if (branch == current_branch)
+                       if (branch == current_branch) {
                                default_remote_name = branch->remote_name;
+                               explicit_default_remote_name = 1;
+                       }
                } else if (!strcmp(subkey, ".merge")) {
                        if (!value)
                                return config_error_nonbool(key);
@@ -344,8 +367,13 @@ static int handle_config(const char *key, const char *value, void *cb)
                subkey = strrchr(name, '.');
                if (!subkey)
                        return 0;
-               rewrite = make_rewrite(name, subkey - name);
                if (!strcmp(subkey, ".insteadof")) {
+                       rewrite = make_rewrite(&rewrites, name, subkey - name);
+                       if (!value)
+                               return config_error_nonbool(key);
+                       add_instead_of(rewrite, xstrdup(value));
+               } else if (!strcmp(subkey, ".pushinsteadof")) {
+                       rewrite = make_rewrite(&rewrites_push, name, subkey - name);
                        if (!value)
                                return config_error_nonbool(key);
                        add_instead_of(rewrite, xstrdup(value));
@@ -361,7 +389,7 @@ static int handle_config(const char *key, const char *value, void *cb)
        }
        subkey = strrchr(name, '.');
        if (!subkey)
-               return error("Config with no key for remote %s", name);
+               return 0;
        remote = make_remote(name, subkey - name);
        remote->origin = REMOTE_CONFIG;
        if (!strcmp(subkey, ".mirror"))
@@ -374,6 +402,11 @@ static int handle_config(const char *key, const char *value, void *cb)
                if (git_config_string(&v, key, value))
                        return -1;
                add_url(remote, v);
+       } else if (!strcmp(subkey, ".pushurl")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_pushurl(remote, v);
        } else if (!strcmp(subkey, ".push")) {
                const char *v;
                if (git_config_string(&v, key, value))
@@ -414,10 +447,17 @@ static void alias_all_urls(void)
 {
        int i, j;
        for (i = 0; i < remotes_nr; i++) {
+               int add_pushurl_aliases;
                if (!remotes[i])
                        continue;
+               for (j = 0; j < remotes[i]->pushurl_nr; j++) {
+                       remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
+               }
+               add_pushurl_aliases = remotes[i]->pushurl_nr == 0;
                for (j = 0; j < remotes[i]->url_nr; j++) {
-                       remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
+                       if (add_pushurl_aliases)
+                               add_pushurl_alias(remotes[i], remotes[i]->url[j]);
+                       remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
                }
        }
 }
@@ -450,16 +490,11 @@ static void read_config(void)
  */
 static int verify_refname(char *name, int is_glob)
 {
-       int result, len = -1;
+       int result;
 
-       if (is_glob) {
-               len = strlen(name);
-               assert(name[len - 1] == '/');
-               name[len - 1] = '\0';
-       }
        result = check_ref_format(name);
-       if (is_glob)
-               name[len - 1] = '/';
+       if (is_glob && result == CHECK_REF_FORMAT_WILDCARD)
+               result = CHECK_REF_FORMAT_OK;
        return result;
 }
 
@@ -494,7 +529,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
                int is_glob;
                const char *lhs, *rhs;
 
-               llen = is_glob = 0;
+               is_glob = 0;
 
                lhs = refspec[i];
                if (*lhs == '+') {
@@ -515,16 +550,15 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
 
                if (rhs) {
                        size_t rlen = strlen(++rhs);
-                       is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*"));
-                       rs[i].dst = xstrndup(rhs, rlen - is_glob);
+                       is_glob = (1 <= rlen && strchr(rhs, '*'));
+                       rs[i].dst = xstrndup(rhs, rlen);
                }
 
                llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
-               if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) {
+               if (1 <= llen && memchr(lhs, '*', llen)) {
                        if ((rhs && !is_glob) || (!rhs && fetch))
                                goto invalid;
                        is_glob = 1;
-                       llen--;
                } else if (rhs && is_glob) {
                        goto invalid;
                }
@@ -634,10 +668,7 @@ static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
 
 static int valid_remote_nick(const char *name)
 {
-       if (!name[0] || /* not empty */
-           (name[0] == '.' && /* not "." */
-            (!name[1] || /* not ".." */
-             (name[1] == '.' && !name[2]))))
+       if (!name[0] || is_dot_or_dotdot(name))
                return 0;
        return !strchr(name, '/'); /* no slash */
 }
@@ -645,10 +676,16 @@ static int valid_remote_nick(const char *name)
 struct remote *remote_get(const char *name)
 {
        struct remote *ret;
+       int name_given = 0;
 
        read_config();
-       if (!name)
+       if (name)
+               name_given = 1;
+       else {
                name = default_remote_name;
+               name_given = explicit_default_remote_name;
+       }
+
        ret = make_remote(name, 0);
        if (valid_remote_nick(name)) {
                if (!ret->url)
@@ -656,7 +693,7 @@ struct remote *remote_get(const char *name)
                if (!ret->url)
                        read_branches_file(ret);
        }
-       if (!ret->url)
+       if (name_given && !ret->url)
                add_url_alias(ret, name);
        if (!ret->url)
                return NULL;
@@ -665,6 +702,17 @@ struct remote *remote_get(const char *name)
        return ret;
 }
 
+int remote_is_configured(const char *name)
+{
+       int i;
+       read_config();
+
+       for (i = 0; i < remotes_nr; i++)
+               if (!strcmp(name, remotes[i]->name))
+                       return 1;
+       return 0;
+}
+
 int for_each_remote(each_remote_fn fn, void *priv)
 {
        int i, result = 0;
@@ -721,6 +769,41 @@ int remote_has_url(struct remote *remote, const char *url)
        return 0;
 }
 
+static int match_name_with_pattern(const char *key, const char *name,
+                                  const char *value, char **result)
+{
+       const char *kstar = strchr(key, '*');
+       size_t klen;
+       size_t ksuffixlen;
+       size_t namelen;
+       int ret;
+       if (!kstar)
+               die("Key '%s' of pattern had no '*'", key);
+       klen = kstar - key;
+       ksuffixlen = strlen(kstar + 1);
+       namelen = strlen(name);
+       ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
+               !memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
+       if (ret && value) {
+               const char *vstar = strchr(value, '*');
+               size_t vlen;
+               size_t vsuffixlen;
+               if (!vstar)
+                       die("Value '%s' of pattern has no '*'", value);
+               vlen = vstar - value;
+               vsuffixlen = strlen(vstar + 1);
+               *result = xmalloc(vlen + vsuffixlen +
+                                 strlen(name) -
+                                 klen - ksuffixlen + 1);
+               strncpy(*result, value, vlen);
+               strncpy(*result + vlen,
+                       name + klen, namelen - klen - ksuffixlen);
+               strcpy(*result + vlen + namelen - klen - ksuffixlen,
+                      vstar + 1);
+       }
+       return ret;
+}
+
 int remote_find_tracking(struct remote *remote, struct refspec *refspec)
 {
        int find_src = refspec->src == NULL;
@@ -744,13 +827,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec)
                if (!fetch->dst)
                        continue;
                if (fetch->pattern) {
-                       if (!prefixcmp(needle, key)) {
-                               *result = xmalloc(strlen(value) +
-                                                 strlen(needle) -
-                                                 strlen(key) + 1);
-                               strcpy(*result, value);
-                               strcpy(*result + strlen(value),
-                                      needle + strlen(key));
+                       if (match_name_with_pattern(key, needle, value, result)) {
                                refspec->force = fetch->force;
                                return 0;
                        }
@@ -780,10 +857,18 @@ struct ref *alloc_ref(const char *name)
 
 static struct ref *copy_ref(const struct ref *ref)
 {
-       struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
-       memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1);
-       ret->next = NULL;
-       return ret;
+       struct ref *cpy;
+       size_t len;
+       if (!ref)
+               return NULL;
+       len = strlen(ref->name);
+       cpy = xmalloc(sizeof(struct ref) + len + 1);
+       memcpy(cpy, ref, sizeof(struct ref) + len + 1);
+       cpy->next = NULL;
+       cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL;
+       cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL;
+       cpy->peer_ref = copy_ref(ref->peer_ref);
+       return cpy;
 }
 
 struct ref *copy_ref_list(const struct ref *ref)
@@ -802,6 +887,7 @@ static void free_ref(struct ref *ref)
 {
        if (!ref)
                return;
+       free_ref(ref->peer_ref);
        free(ref->remote_status);
        free(ref->symref);
        free(ref);
@@ -812,7 +898,6 @@ void free_refs(struct ref *ref)
        struct ref *next;
        while (ref) {
                next = ref->next;
-               free(ref->peer_ref);
                free_ref(ref);
                ref = next;
        }
@@ -929,6 +1014,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
                          struct refspec *rs)
 {
        struct ref *matched_src, *matched_dst;
+       int copy_src;
 
        const char *dst_value = rs->dst;
        char *dst_guess;
@@ -939,6 +1025,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
        matched_src = matched_dst = NULL;
        switch (count_refspec_match(rs->src, src, &matched_src)) {
        case 1:
+               copy_src = 1;
                break;
        case 0:
                /* The source could be in the get_sha1() format
@@ -948,6 +1035,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
                matched_src = try_explicit_object_name(rs->src);
                if (!matched_src)
                        return error("src refspec %s does not match any.", rs->src);
+               copy_src = 0;
                break;
        default:
                return error("src refspec %s matches more than one.", rs->src);
@@ -971,7 +1059,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
        case 0:
                if (!memcmp(dst_value, "refs/", 5))
                        matched_dst = make_linked_ref(dst_value, dst_tail);
-               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);
                else
                        error("unable to push to unqualified destination: %s\n"
@@ -993,7 +1081,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
                return error("dst ref %s receives from more than one src.",
                      matched_dst->name);
        else {
-               matched_dst->peer_ref = matched_src;
+               matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src;
                matched_dst->force = rs->force;
        }
        return 0;
@@ -1022,7 +1110,8 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
                        continue;
                }
 
-               if (rs[i].pattern && !prefixcmp(src->name, rs[i].src))
+               if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name,
+                                                            NULL, NULL))
                        return rs + i;
        }
        if (matching_refs != -1)
@@ -1031,26 +1120,35 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
                return NULL;
 }
 
+static struct ref **tail_ref(struct ref **head)
+{
+       struct ref **tail = head;
+       while (*tail)
+               tail = &((*tail)->next);
+       return tail;
+}
+
 /*
  * 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.
  */
-int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
+int match_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;
-       static const char *default_refspec[] = { ":", 0 };
+       int errs;
+       static const char *default_refspec[] = { ":", NULL };
+       struct ref **dst_tail = tail_ref(dst);
 
        if (!nr_refspec) {
                nr_refspec = 1;
                refspec = default_refspec;
        }
        rs = parse_push_refspec(nr_refspec, (const char **) refspec);
-       if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
-               return -1;
+       errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);
 
        /* pick the remainder */
        for ( ; src; src = src->next) {
@@ -1076,13 +1174,11 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 
                } else {
                        const char *dst_side = pat->dst ? pat->dst : pat->src;
-                       dst_name = xmalloc(strlen(dst_side) +
-                                          strlen(src->name) -
-                                          strlen(pat->src) + 2);
-                       strcpy(dst_name, dst_side);
-                       strcat(dst_name, src->name + strlen(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);
+               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. */
@@ -1098,14 +1194,16 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                                goto free_name;
 
                        /* Create a new one and link it */
-                       dst_peer = make_linked_ref(dst_name, dst_tail);
+                       dst_peer = make_linked_ref(dst_name, &dst_tail);
                        hashcpy(dst_peer->new_sha1, src->new_sha1);
                }
-               dst_peer->peer_ref = src;
+               dst_peer->peer_ref = copy_ref(src);
                dst_peer->force = pat->force;
        free_name:
                free(dst_name);
        }
+       if (errs)
+               return -1;
        return 0;
 }
 
@@ -1127,8 +1225,9 @@ struct branch *branch_get(const char *name)
                        for (i = 0; i < ret->merge_nr; i++) {
                                ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
                                ret->merge[i]->src = xstrdup(ret->merge_name[i]);
-                               remote_find_tracking(ret->remote,
-                                                    ret->merge[i]);
+                               if (remote_find_tracking(ret->remote, ret->merge[i])
+                                   && !strcmp(ret->remote_name, "."))
+                                       ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
                        }
                }
        }
@@ -1156,19 +1255,17 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
        struct ref *ret = NULL;
        struct ref **tail = &ret;
 
-       int remote_prefix_len = strlen(refspec->src);
-       int local_prefix_len = strlen(refspec->dst);
+       char *expn_name;
 
        for (ref = remote_refs; ref; ref = ref->next) {
                if (strchr(ref->name, '^'))
                        continue; /* a dereference item */
-               if (!prefixcmp(ref->name, refspec->src)) {
-                       const char *match;
+               if (match_name_with_pattern(refspec->src, ref->name,
+                                           refspec->dst, &expn_name)) {
                        struct ref *cpy = copy_ref(ref);
-                       match = ref->name + remote_prefix_len;
 
-                       cpy->peer_ref = alloc_ref_with_prefix(refspec->dst,
-                                       local_prefix_len, match);
+                       cpy->peer_ref = alloc_ref(expn_name);
+                       free(expn_name);
                        if (refspec->force)
                                cpy->peer_ref->force = 1;
                        *tail = cpy;
@@ -1201,7 +1298,7 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
 
 static struct ref *get_local_ref(const char *name)
 {
-       if (!name)
+       if (!name || name[0] == '\0')
                return NULL;
 
        if (!prefixcmp(name, "refs/"))
@@ -1271,6 +1368,54 @@ 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_list *temp = list;
+               temp->item->object.flags &= ~mark;
+               list = temp->next;
+               free(temp);
+       }
+}
+
+int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
+{
+       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_sha1), NULL, 0);
+       if (!o || o->type != OBJ_COMMIT)
+               return 0;
+       old = (struct commit *) o;
+
+       o = deref_tag(parse_object(new_sha1), 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;
+}
+
 /*
  * Return true if there is anything to report, otherwise false.
  */
@@ -1298,13 +1443,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
        base = branch->merge[0]->dst;
        if (!resolve_ref(base, sha1, 1, NULL))
                return 0;
-       theirs = lookup_commit(sha1);
+       theirs = lookup_commit_reference(sha1);
        if (!theirs)
                return 0;
 
        if (!resolve_ref(branch->refname, sha1, 1, NULL))
                return 0;
-       ours = lookup_commit(sha1);
+       ours = lookup_commit_reference(sha1);
        if (!ours)
                return 0;
 
@@ -1359,9 +1504,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                return 0;
 
        base = branch->merge[0]->dst;
-       if (!prefixcmp(base, "refs/remotes/")) {
-               base += strlen("refs/remotes/");
-       }
+       base = shorten_unambiguous_ref(base, 0);
        if (!num_theirs)
                strbuf_addf(sb, "Your branch is ahead of '%s' "
                            "by %d commit%s.\n",
@@ -1378,3 +1521,68 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                            base, num_ours, num_theirs);
        return 1;
 }
+
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+       struct ref ***local_tail = cb_data;
+       struct ref *ref;
+       int len;
+
+       /* we already know it starts with refs/ to get here */
+       if (check_ref_format(refname + 5))
+               return 0;
+
+       len = strlen(refname) + 1;
+       ref = xcalloc(1, sizeof(*ref) + len);
+       hashcpy(ref->new_sha1, sha1);
+       memcpy(ref->name, refname, len);
+       **local_tail = ref;
+       *local_tail = &ref->next;
+       return 0;
+}
+
+struct ref *get_local_heads(void)
+{
+       struct ref *local_refs = NULL, **local_tail = &local_refs;
+       for_each_ref(one_local_ref, &local_tail);
+       return local_refs;
+}
+
+struct ref *guess_remote_head(const struct ref *head,
+                             const struct ref *refs,
+                             int all)
+{
+       const struct ref *r;
+       struct ref *list = NULL;
+       struct ref **tail = &list;
+
+       if (!head)
+               return NULL;
+
+       /*
+        * Some transports support directly peeking at
+        * where HEAD points; if that is the case, then
+        * we don't have to guess.
+        */
+       if (head->symref)
+               return copy_ref(find_ref_by_name(refs, head->symref));
+
+       /* If refs/heads/master could be right, it is. */
+       if (!all) {
+               r = find_ref_by_name(refs, "refs/heads/master");
+               if (r && !hashcmp(r->old_sha1, head->old_sha1))
+                       return copy_ref(r);
+       }
+
+       /* Look for another ref that points there */
+       for (r = refs; r; r = r->next) {
+               if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) {
+                       *tail = copy_ref(r);
+                       tail = &((*tail)->next);
+                       if (!all)
+                               break;
+               }
+       }
+
+       return list;
+}