Merge branch 'ab/t3070-test-dedup'
[gitweb.git] / sha1_name.c
index 776101e8d709b924033a141efa38e3f734837c79..e9ffe685d58366dc3802986519e3e2fd22c151a4 100644 (file)
@@ -7,15 +7,20 @@
 #include "refs.h"
 #include "remote.h"
 #include "dir.h"
+#include "sha1-array.h"
 
 static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
 
-typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
+typedef int (*disambiguate_hint_fn)(const struct object_id *, void *);
 
 struct disambiguate_state {
+       int len; /* length of prefix in hex chars */
+       char hex_pfx[GIT_MAX_HEXSZ + 1];
+       struct object_id bin_pfx;
+
        disambiguate_hint_fn fn;
        void *cb_data;
-       unsigned char candidate[20];
+       struct object_id candidate;
        unsigned candidate_exists:1;
        unsigned candidate_checked:1;
        unsigned candidate_ok:1;
@@ -24,7 +29,7 @@ struct disambiguate_state {
        unsigned always_call_fn:1;
 };
 
-static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
+static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
 {
        if (ds->always_call_fn) {
                ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
@@ -32,10 +37,10 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char
        }
        if (!ds->candidate_exists) {
                /* this is the first candidate */
-               hashcpy(ds->candidate, current);
+               oidcpy(&ds->candidate, current);
                ds->candidate_exists = 1;
                return;
-       } else if (!hashcmp(ds->candidate, current)) {
+       } else if (!oidcmp(&ds->candidate, current)) {
                /* the same as what we already have seen */
                return;
        }
@@ -47,14 +52,14 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char
        }
 
        if (!ds->candidate_checked) {
-               ds->candidate_ok = ds->fn(ds->candidate, ds->cb_data);
+               ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data);
                ds->disambiguate_fn_used = 1;
                ds->candidate_checked = 1;
        }
 
        if (!ds->candidate_ok) {
                /* discard the candidate; we know it does not satisfy fn */
-               hashcpy(ds->candidate, current);
+               oidcpy(&ds->candidate, current);
                ds->candidate_checked = 0;
                return;
        }
@@ -72,10 +77,10 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char
        /* otherwise, current can be discarded and candidate is still good */
 }
 
-static void find_short_object_filename(int len, const char *hex_pfx, struct disambiguate_state *ds)
+static void find_short_object_filename(struct disambiguate_state *ds)
 {
        struct alternate_object_database *alt;
-       char hex[40];
+       char hex[GIT_MAX_HEXSZ];
        static struct alternate_object_database *fakeent;
 
        if (!fakeent) {
@@ -86,38 +91,31 @@ static void find_short_object_filename(int len, const char *hex_pfx, struct disa
                 * alt->name/alt->base while iterating over the
                 * object databases including our own.
                 */
-               const char *objdir = get_object_directory();
-               size_t objdir_len = strlen(objdir);
-               fakeent = xmalloc(st_add3(sizeof(*fakeent), objdir_len, 43));
-               memcpy(fakeent->base, objdir, objdir_len);
-               fakeent->name = fakeent->base + objdir_len + 1;
-               fakeent->name[-1] = '/';
+               fakeent = alloc_alt_odb(get_object_directory());
        }
        fakeent->next = alt_odb_list;
 
-       xsnprintf(hex, sizeof(hex), "%.2s", hex_pfx);
+       xsnprintf(hex, sizeof(hex), "%.2s", ds->hex_pfx);
        for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
+               struct strbuf *buf = alt_scratch_buf(alt);
                struct dirent *de;
                DIR *dir;
-               /*
-                * every alt_odb struct has 42 extra bytes after the base
-                * for exactly this purpose
-                */
-               xsnprintf(alt->name, 42, "%.2s/", hex_pfx);
-               dir = opendir(alt->base);
+
+               strbuf_addf(buf, "%.2s/", ds->hex_pfx);
+               dir = opendir(buf->buf);
                if (!dir)
                        continue;
 
                while (!ds->ambiguous && (de = readdir(dir)) != NULL) {
-                       unsigned char sha1[20];
+                       struct object_id oid;
 
-                       if (strlen(de->d_name) != 38)
+                       if (strlen(de->d_name) != GIT_SHA1_HEXSZ - 2)
                                continue;
-                       if (memcmp(de->d_name, hex_pfx + 2, len - 2))
+                       if (memcmp(de->d_name, ds->hex_pfx + 2, ds->len - 2))
                                continue;
-                       memcpy(hex + 2, de->d_name, 38);
-                       if (!get_sha1_hex(hex, sha1))
-                               update_candidates(ds, sha1);
+                       memcpy(hex + 2, de->d_name, GIT_SHA1_HEXSZ - 2);
+                       if (!get_oid_hex(hex, &oid))
+                               update_candidates(ds, &oid);
                }
                closedir(dir);
        }
@@ -138,13 +136,11 @@ static int match_sha(unsigned len, const unsigned char *a, const unsigned char *
        return 1;
 }
 
-static void unique_in_pack(int len,
-                         const unsigned char *bin_pfx,
-                          struct packed_git *p,
+static void unique_in_pack(struct packed_git *p,
                           struct disambiguate_state *ds)
 {
        uint32_t num, last, i, first = 0;
-       const unsigned char *current = NULL;
+       const struct object_id *current = NULL;
 
        open_pack_index(p);
        num = p->num_objects;
@@ -155,7 +151,7 @@ static void unique_in_pack(int len,
                int cmp;
 
                current = nth_packed_object_sha1(p, mid);
-               cmp = hashcmp(bin_pfx, current);
+               cmp = hashcmp(ds->bin_pfx.hash, current);
                if (!cmp) {
                        first = mid;
                        break;
@@ -173,21 +169,21 @@ static void unique_in_pack(int len,
         * 0, 1 or more objects that actually match(es).
         */
        for (i = first; i < num && !ds->ambiguous; i++) {
-               current = nth_packed_object_sha1(p, i);
-               if (!match_sha(len, bin_pfx, current))
+               struct object_id oid;
+               current = nth_packed_object_oid(&oid, p, i);
+               if (!match_sha(ds->len, ds->bin_pfx.hash, current->hash))
                        break;
                update_candidates(ds, current);
        }
 }
 
-static void find_short_packed_object(int len, const unsigned char *bin_pfx,
-                                    struct disambiguate_state *ds)
+static void find_short_packed_object(struct disambiguate_state *ds)
 {
        struct packed_git *p;
 
        prepare_packed_git();
        for (p = packed_git; p && !ds->ambiguous; p = p->next)
-               unique_in_pack(len, bin_pfx, p, ds);
+               unique_in_pack(p, ds);
 }
 
 #define SHORT_NAME_NOT_FOUND (-1)
@@ -218,77 +214,109 @@ static int finish_object_disambiguation(struct disambiguate_state *ds,
                 * same repository!
                 */
                ds->candidate_ok = (!ds->disambiguate_fn_used ||
-                                   ds->fn(ds->candidate, ds->cb_data));
+                                   ds->fn(&ds->candidate, ds->cb_data));
 
        if (!ds->candidate_ok)
                return SHORT_NAME_AMBIGUOUS;
 
-       hashcpy(sha1, ds->candidate);
+       hashcpy(sha1, ds->candidate.hash);
        return 0;
 }
 
-static int disambiguate_commit_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused)
 {
-       int kind = sha1_object_info(sha1, NULL);
+       int kind = sha1_object_info(oid->hash, NULL);
        return kind == OBJ_COMMIT;
 }
 
-static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused)
 {
        struct object *obj;
        int kind;
 
-       kind = sha1_object_info(sha1, NULL);
+       kind = sha1_object_info(oid->hash, NULL);
        if (kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
 
        /* We need to do this the hard way... */
-       obj = deref_tag(parse_object(sha1), NULL, 0);
+       obj = deref_tag(parse_object(oid), NULL, 0);
        if (obj && obj->type == OBJ_COMMIT)
                return 1;
        return 0;
 }
 
-static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused)
 {
-       int kind = sha1_object_info(sha1, NULL);
+       int kind = sha1_object_info(oid->hash, NULL);
        return kind == OBJ_TREE;
 }
 
-static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused)
 {
        struct object *obj;
        int kind;
 
-       kind = sha1_object_info(sha1, NULL);
+       kind = sha1_object_info(oid->hash, NULL);
        if (kind == OBJ_TREE || kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
 
        /* We need to do this the hard way... */
-       obj = deref_tag(lookup_object(sha1), NULL, 0);
+       obj = deref_tag(parse_object(oid), NULL, 0);
        if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
                return 1;
        return 0;
 }
 
-static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused)
 {
-       int kind = sha1_object_info(sha1, NULL);
+       int kind = sha1_object_info(oid->hash, NULL);
        return kind == OBJ_BLOB;
 }
 
-static int prepare_prefixes(const char *name, int len,
-                           unsigned char *bin_pfx,
-                           char *hex_pfx)
+static disambiguate_hint_fn default_disambiguate_hint;
+
+int set_disambiguate_hint_config(const char *var, const char *value)
 {
+       static const struct {
+               const char *name;
+               disambiguate_hint_fn fn;
+       } hints[] = {
+               { "none", NULL },
+               { "commit", disambiguate_commit_only },
+               { "committish", disambiguate_committish_only },
+               { "tree", disambiguate_tree_only },
+               { "treeish", disambiguate_treeish_only },
+               { "blob", disambiguate_blob_only }
+       };
        int i;
 
-       hashclr(bin_pfx);
-       memset(hex_pfx, 'x', 40);
+       if (!value)
+               return config_error_nonbool(var);
+
+       for (i = 0; i < ARRAY_SIZE(hints); i++) {
+               if (!strcasecmp(value, hints[i].name)) {
+                       default_disambiguate_hint = hints[i].fn;
+                       return 0;
+               }
+       }
+
+       return error("unknown hint type for '%s': %s", var, value);
+}
+
+static int init_object_disambiguation(const char *name, int len,
+                                     struct disambiguate_state *ds)
+{
+       int i;
+
+       if (len < MINIMUM_ABBREV || len > GIT_SHA1_HEXSZ)
+               return -1;
+
+       memset(ds, 0, sizeof(*ds));
+
        for (i = 0; i < len ;i++) {
                unsigned char c = name[i];
                unsigned char val;
@@ -302,11 +330,48 @@ static int prepare_prefixes(const char *name, int len,
                }
                else
                        return -1;
-               hex_pfx[i] = c;
+               ds->hex_pfx[i] = c;
                if (!(i & 1))
                        val <<= 4;
-               bin_pfx[i >> 1] |= val;
+               ds->bin_pfx.hash[i >> 1] |= val;
+       }
+
+       ds->len = len;
+       ds->hex_pfx[len] = '\0';
+       prepare_alt_odb();
+       return 0;
+}
+
+static int show_ambiguous_object(const struct object_id *oid, void *data)
+{
+       const struct disambiguate_state *ds = data;
+       struct strbuf desc = STRBUF_INIT;
+       int type;
+
+
+       if (ds->fn && !ds->fn(oid, ds->cb_data))
+               return 0;
+
+       type = sha1_object_info(oid->hash, NULL);
+       if (type == OBJ_COMMIT) {
+               struct commit *commit = lookup_commit(oid);
+               if (commit) {
+                       struct pretty_print_context pp = {0};
+                       pp.date_mode.type = DATE_SHORT;
+                       format_commit_message(commit, " %ad - %s", &desc, &pp);
+               }
+       } else if (type == OBJ_TAG) {
+               struct tag *tag = lookup_tag(oid);
+               if (!parse_tag(tag) && tag->tag)
+                       strbuf_addf(&desc, " %s", tag->tag);
        }
+
+       advise("  %s %s%s",
+              find_unique_abbrev(oid->hash, DEFAULT_ABBREV),
+              typename(type) ? typename(type) : "unknown type",
+              desc.buf);
+
+       strbuf_release(&desc);
        return 0;
 }
 
@@ -314,19 +379,15 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
                          unsigned flags)
 {
        int status;
-       char hex_pfx[40];
-       unsigned char bin_pfx[20];
        struct disambiguate_state ds;
        int quietly = !!(flags & GET_SHA1_QUIETLY);
 
-       if (len < MINIMUM_ABBREV || len > 40)
-               return -1;
-       if (prepare_prefixes(name, len, bin_pfx, hex_pfx) < 0)
+       if (init_object_disambiguation(name, len, &ds) < 0)
                return -1;
 
-       prepare_alt_odb();
+       if (HAS_MULTI_BITS(flags & GET_SHA1_DISAMBIGUATORS))
+               die("BUG: multiple get_short_sha1 disambiguator flags");
 
-       memset(&ds, 0, sizeof(ds));
        if (flags & GET_SHA1_COMMIT)
                ds.fn = disambiguate_commit_only;
        else if (flags & GET_SHA1_COMMITTISH)
@@ -337,44 +398,98 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
                ds.fn = disambiguate_treeish_only;
        else if (flags & GET_SHA1_BLOB)
                ds.fn = disambiguate_blob_only;
+       else
+               ds.fn = default_disambiguate_hint;
 
-       find_short_object_filename(len, hex_pfx, &ds);
-       find_short_packed_object(len, bin_pfx, &ds);
+       find_short_object_filename(&ds);
+       find_short_packed_object(&ds);
        status = finish_object_disambiguation(&ds, sha1);
 
-       if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
-               return error("short SHA1 %.*s is ambiguous.", len, hex_pfx);
+       if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
+               error(_("short SHA1 %s is ambiguous"), ds.hex_pfx);
+
+               /*
+                * We may still have ambiguity if we simply saw a series of
+                * candidates that did not satisfy our hint function. In
+                * that case, we still want to show them, so disable the hint
+                * function entirely.
+                */
+               if (!ds.ambiguous)
+                       ds.fn = NULL;
+
+               advise(_("The candidates are:"));
+               for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds);
+       }
+
        return status;
 }
 
+static int collect_ambiguous(const struct object_id *oid, void *data)
+{
+       oid_array_append(data, oid);
+       return 0;
+}
+
 int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
 {
-       char hex_pfx[40];
-       unsigned char bin_pfx[20];
+       struct oid_array collect = OID_ARRAY_INIT;
        struct disambiguate_state ds;
-       int len = strlen(prefix);
+       int ret;
 
-       if (len < MINIMUM_ABBREV || len > 40)
+       if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0)
                return -1;
-       if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
-               return -1;
-
-       prepare_alt_odb();
 
-       memset(&ds, 0, sizeof(ds));
        ds.always_call_fn = 1;
-       ds.cb_data = cb_data;
-       ds.fn = fn;
+       ds.fn = collect_ambiguous;
+       ds.cb_data = &collect;
+       find_short_object_filename(&ds);
+       find_short_packed_object(&ds);
 
-       find_short_object_filename(len, hex_pfx, &ds);
-       find_short_packed_object(len, bin_pfx, &ds);
-       return ds.ambiguous;
+       ret = oid_array_for_each_unique(&collect, fn, cb_data);
+       oid_array_clear(&collect);
+       return ret;
+}
+
+/*
+ * Return the slot of the most-significant bit set in "val". There are various
+ * ways to do this quickly with fls() or __builtin_clzl(), but speed is
+ * probably not a big deal here.
+ */
+static unsigned msb(unsigned long val)
+{
+       unsigned r = 0;
+       while (val >>= 1)
+               r++;
+       return r;
 }
 
 int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
 {
        int status, exists;
 
+       if (len < 0) {
+               unsigned long count = approximate_object_count();
+               /*
+                * Add one because the MSB only tells us the highest bit set,
+                * not including the value of all the _other_ bits (so "15"
+                * is only one off of 2^4, but the MSB is the 3rd bit.
+                */
+               len = msb(count) + 1;
+               /*
+                * We now know we have on the order of 2^len objects, which
+                * expects a collision at 2^(len/2). But we also care about hex
+                * chars, not bits, and there are 4 bits per hex. So all
+                * together we need to divide by 2; but we also want to round
+                * odd numbers up, hence adding one before dividing.
+                */
+               len = (len + 1) / 2;
+               /*
+                * For very small repos, we stick with our regular fallback.
+                */
+               if (len < FALLBACK_DEFAULT_ABBREV)
+                       len = FALLBACK_DEFAULT_ABBREV;
+       }
+
        sha1_to_hex_r(hex, sha1);
        if (len == 40 || !len)
                return 40;
@@ -395,7 +510,10 @@ int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
 
 const char *find_unique_abbrev(const unsigned char *sha1, int len)
 {
-       static char hex[GIT_SHA1_HEXSZ + 1];
+       static int bufno;
+       static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
+       char *hex = hexbuffer[bufno];
+       bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
        find_unique_abbrev_r(hex, sha1, len);
        return hex;
 }
@@ -433,7 +551,7 @@ static inline int at_mark(const char *string, int len,
        for (i = 0; i < nr; i++) {
                int suffix_len = strlen(suffix[i]);
                if (suffix_len <= len
-                   && !memcmp(string, suffix[i], suffix_len))
+                   && !strncasecmp(string, suffix[i], suffix_len))
                        return suffix_len;
        }
        return 0;
@@ -542,8 +660,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 
        if (reflog_len) {
                int nth, i;
-               unsigned long at_time;
-               unsigned long co_time;
+               timestamp_t at_time;
+               timestamp_t co_time;
                int co_tz, co_cnt;
 
                /* Is it asking for N-th entry, or approxidate? */
@@ -604,14 +722,14 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 static int get_parent(const char *name, int len,
                      unsigned char *result, int idx)
 {
-       unsigned char sha1[20];
-       int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
+       struct object_id oid;
+       int ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH);
        struct commit *commit;
        struct commit_list *p;
 
        if (ret)
                return ret;
-       commit = lookup_commit_reference(sha1);
+       commit = lookup_commit_reference(&oid);
        if (parse_commit(commit))
                return -1;
        if (!idx) {
@@ -632,14 +750,14 @@ static int get_parent(const char *name, int len,
 static int get_nth_ancestor(const char *name, int len,
                            unsigned char *result, int generation)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        struct commit *commit;
        int ret;
 
-       ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
+       ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH);
        if (ret)
                return ret;
-       commit = lookup_commit_reference(sha1);
+       commit = lookup_commit_reference(&oid);
        if (!commit)
                return -1;
 
@@ -658,7 +776,7 @@ struct object *peel_to_type(const char *name, int namelen,
        if (name && !namelen)
                namelen = strlen(name);
        while (1) {
-               if (!o || (!o->parsed && !parse_object(o->oid.hash)))
+               if (!o || (!o->parsed && !parse_object(&o->oid)))
                        return NULL;
                if (expected_type == OBJ_ANY || o->type == expected_type)
                        return o;
@@ -677,12 +795,12 @@ struct object *peel_to_type(const char *name, int namelen,
        }
 }
 
-static int peel_onion(const char *name, int len, unsigned char *sha1)
+static int peel_onion(const char *name, int len, unsigned char *sha1,
+                     unsigned lookup_flags)
 {
-       unsigned char outer[20];
+       struct object_id outer;
        const char *sp;
        unsigned int expected_type = 0;
-       unsigned lookup_flags = 0;
        struct object *o;
 
        /*
@@ -722,20 +840,21 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
        else
                return -1;
 
+       lookup_flags &= ~GET_SHA1_DISAMBIGUATORS;
        if (expected_type == OBJ_COMMIT)
-               lookup_flags = GET_SHA1_COMMITTISH;
+               lookup_flags |= GET_SHA1_COMMITTISH;
        else if (expected_type == OBJ_TREE)
-               lookup_flags = GET_SHA1_TREEISH;
+               lookup_flags |= GET_SHA1_TREEISH;
 
-       if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
+       if (get_sha1_1(name, sp - name - 2, outer.hash, lookup_flags))
                return -1;
 
-       o = parse_object(outer);
+       o = parse_object(&outer);
        if (!o)
                return -1;
        if (!expected_type) {
                o = deref_tag(o, name, sp - name - 2);
-               if (!o || (!o->parsed && !parse_object(o->oid.hash)))
+               if (!o || (!o->parsed && !parse_object(&o->oid)))
                        return -1;
                hashcpy(sha1, o->oid.hash);
                return 0;
@@ -826,7 +945,7 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l
                return get_nth_ancestor(name, len1, sha1, num);
        }
 
-       ret = peel_onion(name, len, sha1);
+       ret = peel_onion(name, len, sha1, lookup_flags);
        if (!ret)
                return 0;
 
@@ -862,7 +981,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid,
                          int flag, void *cb_data)
 {
        struct commit_list **list = cb_data;
-       struct object *object = parse_object(oid->hash);
+       struct object *object = parse_object(oid);
        if (!object)
                return 0;
        if (object->type == OBJ_TAG) {
@@ -908,7 +1027,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
                int matches;
 
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
-               if (!parse_object(commit->object.oid.hash))
+               if (!parse_object(&commit->object.oid))
                        continue;
                buf = get_commit_buffer(commit, NULL);
                p = strstr(buf, "\n\n");
@@ -934,8 +1053,8 @@ struct grab_nth_branch_switch_cbdata {
        struct strbuf buf;
 };
 
-static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
-                                 const char *email, unsigned long timestamp, int tz,
+static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
+                                 const char *email, timestamp_t timestamp, int tz,
                                  const char *message, void *cb_data)
 {
        struct grab_nth_branch_switch_cbdata *cb = cb_data;
@@ -995,35 +1114,35 @@ static int interpret_nth_prior_checkout(const char *name, int namelen,
        return retval;
 }
 
-int get_sha1_mb(const char *name, unsigned char *sha1)
+int get_oid_mb(const char *name, struct object_id *oid)
 {
        struct commit *one, *two;
        struct commit_list *mbs;
-       unsigned char sha1_tmp[20];
+       struct object_id oid_tmp;
        const char *dots;
        int st;
 
        dots = strstr(name, "...");
        if (!dots)
-               return get_sha1(name, sha1);
+               return get_oid(name, oid);
        if (dots == name)
-               st = get_sha1("HEAD", sha1_tmp);
+               st = get_oid("HEAD", &oid_tmp);
        else {
                struct strbuf sb;
                strbuf_init(&sb, dots - name);
                strbuf_add(&sb, name, dots - name);
-               st = get_sha1_committish(sb.buf, sha1_tmp);
+               st = get_sha1_committish(sb.buf, oid_tmp.hash);
                strbuf_release(&sb);
        }
        if (st)
                return st;
-       one = lookup_commit_reference_gently(sha1_tmp, 0);
+       one = lookup_commit_reference_gently(&oid_tmp, 0);
        if (!one)
                return -1;
 
-       if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
+       if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", oid_tmp.hash))
                return -1;
-       two = lookup_commit_reference_gently(sha1_tmp, 0);
+       two = lookup_commit_reference_gently(&oid_tmp, 0);
        if (!two)
                return -1;
        mbs = get_merge_bases(one, two);
@@ -1031,7 +1150,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
                st = -1;
        else {
                st = 0;
-               hashcpy(sha1, mbs->item->object.oid.hash);
+               oidcpy(oid, &mbs->item->object.oid);
        }
        free_commit_list(mbs);
        return st;
@@ -1059,7 +1178,8 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str
        return 1;
 }
 
-static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
+static int reinterpret(const char *name, int namelen, int len,
+                      struct strbuf *buf, unsigned allowed)
 {
        /* we have extra data, which might need further processing */
        struct strbuf tmp = STRBUF_INIT;
@@ -1067,7 +1187,7 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu
        int ret;
 
        strbuf_add(buf, name + len, namelen - len);
-       ret = interpret_branch_name(buf->buf, buf->len, &tmp);
+       ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed);
        /* that data was not interpreted, remove our cruft */
        if (ret < 0) {
                strbuf_setlen(buf, used);
@@ -1088,11 +1208,27 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref)
        free(s);
 }
 
+static int branch_interpret_allowed(const char *refname, unsigned allowed)
+{
+       if (!allowed)
+               return 1;
+
+       if ((allowed & INTERPRET_BRANCH_LOCAL) &&
+           starts_with(refname, "refs/heads/"))
+               return 1;
+       if ((allowed & INTERPRET_BRANCH_REMOTE) &&
+           starts_with(refname, "refs/remotes/"))
+               return 1;
+
+       return 0;
+}
+
 static int interpret_branch_mark(const char *name, int namelen,
                                 int at, struct strbuf *buf,
                                 int (*get_mark)(const char *, int),
                                 const char *(*get_data)(struct branch *,
-                                                        struct strbuf *))
+                                                        struct strbuf *),
+                                unsigned allowed)
 {
        int len;
        struct branch *branch;
@@ -1117,64 +1253,55 @@ static int interpret_branch_mark(const char *name, int namelen,
        if (!value)
                die("%s", err.buf);
 
+       if (!branch_interpret_allowed(value, allowed))
+               return -1;
+
        set_shortened_ref(buf, value);
        return len + at;
 }
 
-/*
- * This reads short-hand syntax that not only evaluates to a commit
- * object name, but also can act as if the end user spelled the name
- * of the branch from the command line.
- *
- * - "@{-N}" finds the name of the Nth previous branch we were on, and
- *   places the name of the branch in the given buf and returns the
- *   number of characters parsed if successful.
- *
- * - "<branch>@{upstream}" finds the name of the other ref that
- *   <branch> is configured to merge with (missing <branch> defaults
- *   to the current branch), and places the name of the branch in the
- *   given buf and returns the number of characters parsed if
- *   successful.
- *
- * If the input is not of the accepted format, it returns a negative
- * number to signal an error.
- *
- * If the input was ok but there are not N branch switches in the
- * reflog, it returns 0.
- */
-int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
+int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
+                         unsigned allowed)
 {
        char *at;
        const char *start;
-       int len = interpret_nth_prior_checkout(name, namelen, buf);
+       int len;
 
        if (!namelen)
                namelen = strlen(name);
 
-       if (!len) {
-               return len; /* syntax Ok, not enough switches */
-       } else if (len > 0) {
-               if (len == namelen)
-                       return len; /* consumed all */
-               else
-                       return reinterpret(name, namelen, len, buf);
+       if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) {
+               len = interpret_nth_prior_checkout(name, namelen, buf);
+               if (!len) {
+                       return len; /* syntax Ok, not enough switches */
+               } else if (len > 0) {
+                       if (len == namelen)
+                               return len; /* consumed all */
+                       else
+                               return reinterpret(name, namelen, len, buf, allowed);
+               }
        }
 
        for (start = name;
             (at = memchr(start, '@', namelen - (start - name)));
             start = at + 1) {
 
-               len = interpret_empty_at(name, namelen, at - name, buf);
-               if (len > 0)
-                       return reinterpret(name, namelen, len, buf);
+               if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) {
+                       len = interpret_empty_at(name, namelen, at - name, buf);
+                       if (len > 0)
+                               return reinterpret(name, namelen, len, buf,
+                                                  allowed);
+               }
 
                len = interpret_branch_mark(name, namelen, at - name, buf,
-                                           upstream_mark, branch_get_upstream);
+                                           upstream_mark, branch_get_upstream,
+                                           allowed);
                if (len > 0)
                        return len;
 
                len = interpret_branch_mark(name, namelen, at - name, buf,
-                                           push_mark, branch_get_push);
+                                           push_mark, branch_get_push,
+                                           allowed);
                if (len > 0)
                        return len;
        }
@@ -1182,22 +1309,19 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
        return -1;
 }
 
-int strbuf_branchname(struct strbuf *sb, const char *name)
+void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed)
 {
        int len = strlen(name);
-       int used = interpret_branch_name(name, len, sb);
+       int used = interpret_branch_name(name, len, sb, allowed);
 
-       if (used == len)
-               return 0;
        if (used < 0)
                used = 0;
        strbuf_add(sb, name + used, len - used);
-       return len;
 }
 
 int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
 {
-       strbuf_branchname(sb, name);
+       strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
        if (name[0] == '-')
                return -1;
        strbuf_splice(sb, 0, 0, "refs/heads/", 11);
@@ -1214,6 +1338,15 @@ int get_sha1(const char *name, unsigned char *sha1)
        return get_sha1_with_context(name, 0, sha1, &unused);
 }
 
+/*
+ * This is like "get_sha1()", but for struct object_id.
+ */
+int get_oid(const char *name, struct object_id *oid)
+{
+       return get_sha1(name, oid->hash);
+}
+
+
 /*
  * Many callers know that the user meant to name a commit-ish by
  * syntactical positions where the object name appears.  Calling this
@@ -1373,8 +1506,12 @@ static int get_sha1_with_context_1(const char *name,
        const char *cp;
        int only_to_die = flags & GET_SHA1_ONLY_TO_DIE;
 
+       if (only_to_die)
+               flags |= GET_SHA1_QUIETLY;
+
        memset(oc, 0, sizeof(*oc));
        oc->mode = S_IFINVALID;
+       strbuf_init(&oc->symlink_path, 0);
        ret = get_sha1_1(name, namelen, sha1, flags);
        if (!ret)
                return ret;
@@ -1413,7 +1550,8 @@ static int get_sha1_with_context_1(const char *name,
                        namelen = strlen(cp);
                }
 
-               strlcpy(oc->path, cp, sizeof(oc->path));
+               if (flags & GET_SHA1_RECORD_PATH)
+                       oc->path = xstrdup(cp);
 
                if (!active_cache)
                        read_cache();
@@ -1426,7 +1564,7 @@ static int get_sha1_with_context_1(const char *name,
                            memcmp(ce->name, cp, namelen))
                                break;
                        if (ce_stage(ce) == stage) {
-                               hashcpy(sha1, ce->sha1);
+                               hashcpy(sha1, ce->oid.hash);
                                oc->mode = ce->ce_mode;
                                free(new_path);
                                return 0;
@@ -1449,7 +1587,12 @@ static int get_sha1_with_context_1(const char *name,
        if (*cp == ':') {
                unsigned char tree_sha1[20];
                int len = cp - name;
-               if (!get_sha1_1(name, len, tree_sha1, GET_SHA1_TREEISH)) {
+               unsigned sub_flags = flags;
+
+               sub_flags &= ~GET_SHA1_DISAMBIGUATORS;
+               sub_flags |= GET_SHA1_TREEISH;
+
+               if (!get_sha1_1(name, len, tree_sha1, sub_flags)) {
                        const char *filename = cp+1;
                        char *new_filename = NULL;
 
@@ -1471,7 +1614,8 @@ static int get_sha1_with_context_1(const char *name,
                                }
                        }
                        hashcpy(oc->tree, tree_sha1);
-                       strlcpy(oc->path, filename, sizeof(oc->path));
+                       if (flags & GET_SHA1_RECORD_PATH)
+                               oc->path = xstrdup(filename);
 
                        free(new_filename);
                        return ret;
@@ -1497,9 +1641,9 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
        get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
 }
 
-int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
+int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc)
 {
        if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
                die("BUG: incompatible flags for get_sha1_with_context");
-       return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
+       return get_sha1_with_context_1(str, flags, NULL, sha1, oc);
 }