status: don't require the repository to be writable
[gitweb.git] / remote.c
index c7a8c2b1fbdea9aa03d8a5ccd8cbfb39eed742df..b979a9642b81b456ab92cdf57e8eda944f01f92e 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -5,13 +5,15 @@
 #include "diff.h"
 #include "revision.h"
 #include "dir.h"
+#include "tag.h"
+#include "string-list.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;
@@ -27,6 +29,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;
@@ -40,14 +47,13 @@ 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;
@@ -56,14 +62,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;
                        }
                }
@@ -71,10 +77,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;
 }
 
@@ -100,9 +106,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)
@@ -162,22 +182,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;
@@ -300,7 +320,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)
@@ -311,7 +331,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 */
 }
 
@@ -348,8 +368,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));
@@ -365,19 +390,25 @@ 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"))
                remote->mirror = git_config_bool(key, value);
        else if (!strcmp(subkey, ".skipdefaultupdate"))
                remote->skip_default_update = git_config_bool(key, value);
-
+       else if (!strcmp(subkey, ".skipfetchall"))
+               remote->skip_default_update = git_config_bool(key, value);
        else if (!strcmp(subkey, ".url")) {
                const char *v;
                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))
@@ -418,10 +449,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);
                }
        }
 }
@@ -454,16 +492,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;
 }
 
@@ -498,7 +531,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 == '+') {
@@ -519,16 +552,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;
                }
@@ -672,6 +704,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;
@@ -693,29 +736,33 @@ int for_each_remote(each_remote_fn fn, void *priv)
 
 void ref_remove_duplicates(struct ref *ref_map)
 {
-       struct ref **posn;
-       struct ref *next;
-       for (; ref_map; ref_map = ref_map->next) {
+       struct string_list refs = { NULL, 0, 0, 0 };
+       struct string_list_item *item = NULL;
+       struct ref *prev = NULL, *next = NULL;
+       for (; ref_map; prev = ref_map, ref_map = next) {
+               next = ref_map->next;
                if (!ref_map->peer_ref)
                        continue;
-               posn = &ref_map->next;
-               while (*posn) {
-                       if ((*posn)->peer_ref &&
-                           !strcmp((*posn)->peer_ref->name,
-                                   ref_map->peer_ref->name)) {
-                               if (strcmp((*posn)->name, ref_map->name))
-                                       die("%s tracks both %s and %s",
-                                           ref_map->peer_ref->name,
-                                           (*posn)->name, ref_map->name);
-                               next = (*posn)->next;
-                               free((*posn)->peer_ref);
-                               free(*posn);
-                               *posn = next;
-                       } else {
-                               posn = &(*posn)->next;
-                       }
+
+               item = string_list_lookup(ref_map->peer_ref->name, &refs);
+               if (item) {
+                       if (strcmp(((struct ref *)item->util)->name,
+                                  ref_map->name))
+                               die("%s tracks both %s and %s",
+                                   ref_map->peer_ref->name,
+                                   ((struct ref *)item->util)->name,
+                                   ref_map->name);
+                       prev->next = ref_map->next;
+                       free(ref_map->peer_ref);
+                       free(ref_map);
+                       ref_map = prev; /* skip this; we freed it */
+                       continue;
                }
+
+               item = string_list_insert(ref_map->peer_ref->name, &refs);
+               item->util = ref_map;
        }
+       string_list_clear(&refs, 0);
 }
 
 int remote_has_url(struct remote *remote, const char *url)
@@ -728,6 +775,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;
@@ -751,13 +833,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;
                        }
@@ -787,10 +863,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)
@@ -809,6 +893,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);
@@ -819,7 +904,6 @@ void free_refs(struct ref *ref)
        struct ref *next;
        while (ref) {
                next = ref->next;
-               free(ref->peer_ref);
                free_ref(ref);
                ref = next;
        }
@@ -936,6 +1020,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;
@@ -946,6 +1031,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
@@ -955,6 +1041,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);
@@ -978,7 +1065,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"
@@ -1000,7 +1087,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;
@@ -1029,7 +1116,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)
@@ -1038,26 +1126,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) {
@@ -1083,13 +1180,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. */
@@ -1105,14 +1200,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;
 }
 
@@ -1134,8 +1231,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]);
                        }
                }
        }
@@ -1163,19 +1261,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;
@@ -1208,7 +1304,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/"))
@@ -1278,6 +1374,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.
  */
@@ -1305,13 +1449,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;
 
@@ -1366,9 +1510,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",
@@ -1385,3 +1527,107 @@ 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;
+}
+
+struct stale_heads_info {
+       struct remote *remote;
+       struct string_list *ref_names;
+       struct ref **stale_refs_tail;
+};
+
+static int get_stale_heads_cb(const char *refname,
+       const unsigned char *sha1, int flags, void *cb_data)
+{
+       struct stale_heads_info *info = cb_data;
+       struct refspec refspec;
+       memset(&refspec, 0, sizeof(refspec));
+       refspec.dst = (char *)refname;
+       if (!remote_find_tracking(info->remote, &refspec)) {
+               if (!((flags & REF_ISSYMREF) ||
+                   string_list_has_string(info->ref_names, refspec.src))) {
+                       struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
+                       hashcpy(ref->new_sha1, sha1);
+               }
+       }
+       return 0;
+}
+
+struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
+{
+       struct ref *ref, *stale_refs = NULL;
+       struct string_list ref_names = { NULL, 0, 0, 0 };
+       struct stale_heads_info info;
+       info.remote = remote;
+       info.ref_names = &ref_names;
+       info.stale_refs_tail = &stale_refs;
+       for (ref = fetch_map; ref; ref = ref->next)
+               string_list_append(ref->name, &ref_names);
+       sort_string_list(&ref_names);
+       for_each_ref(get_stale_heads_cb, &info);
+       string_list_clear(&ref_names, 0);
+       return stale_refs;
+}