Merge branch 'jk/alt-odb-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:19 +0000 (13:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:20 +0000 (13:25 -0700)
Codepaths involved in interacting alternate object store have
been cleaned up.

* jk/alt-odb-cleanup:
alternates: use fspathcmp to detect duplicates
sha1_file: always allow relative paths to alternates
count-objects: report alternates via verbose mode
fill_sha1_file: write into a strbuf
alternates: store scratch buffer as strbuf
fill_sha1_file: write "boring" characters
alternates: use a separate scratch space
alternates: encapsulate alt->base munging
alternates: provide helper for allocating alternate
alternates: provide helper for adding to alternates list
link_alt_odb_entry: refactor string handling
link_alt_odb_entry: handle normalize_path errors
t5613: clarify "too deep" recursion tests
t5613: do not chdir in main process
t5613: whitespace/style cleanups
t5613: use test_must_fail
t5613: drop test_valid_repo function
t5613: drop reachable_via function

1  2 
builtin/submodule--helper.c
cache.h
sha1_file.c
sha1_name.c
submodule.c
transport.c
index 444ec06c2a31d1ec4981ba7475147c3154cd1b18,fd72c90442dca5f1be7bb822ed0579813557c739..6182eb3197848dc613da1d02280fce0de003addc
@@@ -492,20 -492,16 +492,16 @@@ static int add_possible_reference_from_
  {
        struct submodule_alternate_setup *sas = sas_cb;
  
-       /* directory name, minus trailing slash */
-       size_t namelen = alt->name - alt->base - 1;
-       struct strbuf name = STRBUF_INIT;
-       strbuf_add(&name, alt->base, namelen);
        /*
         * If the alternate object store is another repository, try the
         * standard layout with .git/modules/<name>/objects
         */
-       if (ends_with(name.buf, ".git/objects")) {
+       if (ends_with(alt->path, ".git/objects")) {
                char *sm_alternate;
                struct strbuf sb = STRBUF_INIT;
                struct strbuf err = STRBUF_INIT;
-               strbuf_add(&sb, name.buf, name.len - strlen("objects"));
+               strbuf_add(&sb, alt->path, strlen(alt->path) - strlen("objects"));
                /*
                 * We need to end the new path with '/' to mark it as a dir,
                 * otherwise a submodule name containing '/' will be broken
                strbuf_release(&sb);
        }
  
-       strbuf_release(&name);
        return 0;
  }
  
@@@ -753,7 -748,7 +748,7 @@@ static int prepare_to_clone_next_submod
                if (suc->recursive_prefix)
                        strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name);
                else
 -                      strbuf_addf(&sb, "%s", ce->name);
 +                      strbuf_addstr(&sb, ce->name);
                strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf);
                strbuf_addch(out, '\n');
                goto cleanup;
diff --combined cache.h
index 2cfb1cab6673ef8f26b8f59b72902c7083a4f101,5d36ffa1f253d1c007bfa41be270593612d44a6e..0dc39a998cb246b8eb22bc6def8a0900a14e2ee2
+++ b/cache.h
@@@ -1206,11 -1206,6 +1206,11 @@@ struct object_context 
  #define GET_SHA1_FOLLOW_SYMLINKS 0100
  #define GET_SHA1_ONLY_TO_DIE    04000
  
 +#define GET_SHA1_DISAMBIGUATORS \
 +      (GET_SHA1_COMMIT | GET_SHA1_COMMITTISH | \
 +      GET_SHA1_TREE | GET_SHA1_TREEISH | \
 +      GET_SHA1_BLOB)
 +
  extern int get_sha1(const char *str, unsigned char *sha1);
  extern int get_sha1_commit(const char *str, unsigned char *sha1);
  extern int get_sha1_committish(const char *str, unsigned char *sha1);
@@@ -1225,8 -1220,6 +1225,8 @@@ extern int get_oid(const char *str, str
  typedef int each_abbrev_fn(const unsigned char *sha1, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
 +extern int set_disambiguate_hint_config(const char *var, const char *value);
 +
  /*
   * Try to read a SHA1 in hexadecimal format from the 40 characters
   * starting at hex.  Write the 20-byte result to sha1 in binary form.
@@@ -1390,16 -1383,46 +1390,46 @@@ extern void remove_scheduled_dirs(void)
  
  extern struct alternate_object_database {
        struct alternate_object_database *next;
-       char *name;
-       char base[FLEX_ARRAY]; /* more */
+       /* see alt_scratch_buf() */
+       struct strbuf scratch;
+       size_t base_len;
+       char path[FLEX_ARRAY];
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
  extern void read_info_alternates(const char * relative_base, int depth);
  extern char *compute_alternate_path(const char *path, struct strbuf *err);
- extern void add_to_alternates_file(const char *reference);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
  extern int foreach_alt_odb(alt_odb_fn, void*);
  
+ /*
+  * Allocate a "struct alternate_object_database" but do _not_ actually
+  * add it to the list of alternates.
+  */
+ struct alternate_object_database *alloc_alt_odb(const char *dir);
+ /*
+  * Add the directory to the on-disk alternates file; the new entry will also
+  * take effect in the current process.
+  */
+ extern void add_to_alternates_file(const char *dir);
+ /*
+  * Add the directory to the in-memory list of alternates (along with any
+  * recursive alternates it points to), but do not modify the on-disk alternates
+  * file.
+  */
+ extern void add_to_alternates_memory(const char *dir);
+ /*
+  * Returns a scratch strbuf pre-filled with the alternate object directory,
+  * including a trailing slash, which can be used to access paths in the
+  * alternate. Always use this over direct access to alt->scratch, as it
+  * cleans up any previous use of the scratch buffer.
+  */
+ extern struct strbuf *alt_scratch_buf(struct alternate_object_database *alt);
  struct pack_window {
        struct pack_window *next;
        unsigned char *base;
@@@ -1602,15 -1625,7 +1632,15 @@@ struct object_info 
                } packed;
        } u;
  };
 +
 +/*
 + * Initializer for a "struct object_info" that wants no items. You may
 + * also memset() the memory to all-zeroes.
 + */
 +#define OBJECT_INFO_INIT {NULL}
 +
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 +extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
diff --combined sha1_file.c
index 309e87d987e2e458d120a8ae587c5f99e7732865,064651947dcab37b014309796aa32df0ed944750..266152de36a099bfa75bfaa8429f0234c52a80b1
@@@ -172,36 -172,42 +172,42 @@@ enum scld_error safe_create_leading_dir
        return result;
  }
  
- static void fill_sha1_path(char *pathbuf, const unsigned char *sha1)
+ static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1)
  {
        int i;
        for (i = 0; i < 20; i++) {
                static char hex[] = "0123456789abcdef";
                unsigned int val = sha1[i];
-               char *pos = pathbuf + i*2 + (i > 0);
-               *pos++ = hex[val >> 4];
-               *pos = hex[val & 0xf];
+               strbuf_addch(buf, hex[val >> 4]);
+               strbuf_addch(buf, hex[val & 0xf]);
+               if (!i)
+                       strbuf_addch(buf, '/');
        }
  }
  
  const char *sha1_file_name(const unsigned char *sha1)
  {
-       static char buf[PATH_MAX];
-       const char *objdir;
-       int len;
+       static struct strbuf buf = STRBUF_INIT;
  
-       objdir = get_object_directory();
-       len = strlen(objdir);
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "%s/", get_object_directory());
  
-       /* '/' + sha1(2) + '/' + sha1(38) + '\0' */
-       if (len + 43 > PATH_MAX)
-               die("insanely long object directory %s", objdir);
-       memcpy(buf, objdir, len);
-       buf[len] = '/';
-       buf[len+3] = '/';
-       buf[len+42] = '\0';
-       fill_sha1_path(buf + len + 1, sha1);
-       return buf;
+       fill_sha1_path(&buf, sha1);
+       return buf.buf;
+ }
+ struct strbuf *alt_scratch_buf(struct alternate_object_database *alt)
+ {
+       strbuf_setlen(&alt->scratch, alt->base_len);
+       return &alt->scratch;
+ }
+ static const char *alt_sha1_path(struct alternate_object_database *alt,
+                                const unsigned char *sha1)
+ {
+       struct strbuf *buf = alt_scratch_buf(alt);
+       fill_sha1_path(buf, sha1);
+       return buf->buf;
  }
  
  /*
@@@ -234,6 -240,35 +240,35 @@@ char *sha1_pack_index_name(const unsign
  struct alternate_object_database *alt_odb_list;
  static struct alternate_object_database **alt_odb_tail;
  
+ /*
+  * Return non-zero iff the path is usable as an alternate object database.
+  */
+ static int alt_odb_usable(struct strbuf *path, const char *normalized_objdir)
+ {
+       struct alternate_object_database *alt;
+       /* Detect cases where alternate disappeared */
+       if (!is_directory(path->buf)) {
+               error("object directory %s does not exist; "
+                     "check .git/objects/info/alternates.",
+                     path->buf);
+               return 0;
+       }
+       /*
+        * Prevent the common mistake of listing the same
+        * thing twice, or object directory itself.
+        */
+       for (alt = alt_odb_list; alt; alt = alt->next) {
+               if (!fspathcmp(path->buf, alt->path))
+                       return 0;
+       }
+       if (!fspathcmp(path->buf, normalized_objdir))
+               return 0;
+       return 1;
+ }
  /*
   * Prepare alternate object database registry.
   *
@@@ -253,8 -288,6 +288,6 @@@ static int link_alt_odb_entry(const cha
        int depth, const char *normalized_objdir)
  {
        struct alternate_object_database *ent;
-       struct alternate_object_database *alt;
-       size_t pfxlen, entlen;
        struct strbuf pathbuf = STRBUF_INIT;
  
        if (!is_absolute_path(entry) && relative_base) {
        }
        strbuf_addstr(&pathbuf, entry);
  
-       normalize_path_copy(pathbuf.buf, pathbuf.buf);
-       pfxlen = strlen(pathbuf.buf);
+       if (strbuf_normalize_path(&pathbuf) < 0) {
+               error("unable to normalize alternate object path: %s",
+                     pathbuf.buf);
+               strbuf_release(&pathbuf);
+               return -1;
+       }
  
        /*
         * The trailing slash after the directory name is given by
         * this function at the end. Remove duplicates.
         */
-       while (pfxlen && pathbuf.buf[pfxlen-1] == '/')
-               pfxlen -= 1;
+       while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
+               strbuf_setlen(&pathbuf, pathbuf.len - 1);
  
-       entlen = st_add(pfxlen, 43); /* '/' + 2 hex + '/' + 38 hex + NUL */
-       ent = xmalloc(st_add(sizeof(*ent), entlen));
-       memcpy(ent->base, pathbuf.buf, pfxlen);
-       strbuf_release(&pathbuf);
-       ent->name = ent->base + pfxlen + 1;
-       ent->base[pfxlen + 3] = '/';
-       ent->base[pfxlen] = ent->base[entlen-1] = 0;
-       /* Detect cases where alternate disappeared */
-       if (!is_directory(ent->base)) {
-               error("object directory %s does not exist; "
-                     "check .git/objects/info/alternates.",
-                     ent->base);
-               free(ent);
+       if (!alt_odb_usable(&pathbuf, normalized_objdir)) {
+               strbuf_release(&pathbuf);
                return -1;
        }
  
-       /* Prevent the common mistake of listing the same
-        * thing twice, or object directory itself.
-        */
-       for (alt = alt_odb_list; alt; alt = alt->next) {
-               if (pfxlen == alt->name - alt->base - 1 &&
-                   !memcmp(ent->base, alt->base, pfxlen)) {
-                       free(ent);
-                       return -1;
-               }
-       }
-       if (!fspathcmp(ent->base, normalized_objdir)) {
-               free(ent);
-               return -1;
-       }
+       ent = alloc_alt_odb(pathbuf.buf);
  
        /* add the alternate entry */
        *alt_odb_tail = ent;
        ent->next = NULL;
  
        /* recursively add alternates */
-       read_info_alternates(ent->base, depth + 1);
-       ent->base[pfxlen] = '/';
+       read_info_alternates(pathbuf.buf, depth + 1);
  
+       strbuf_release(&pathbuf);
        return 0;
  }
  
@@@ -335,7 -344,9 +344,9 @@@ static void link_alt_odb_entries(const 
        }
  
        strbuf_add_absolute_path(&objdirbuf, get_object_directory());
-       normalize_path_copy(objdirbuf.buf, objdirbuf.buf);
+       if (strbuf_normalize_path(&objdirbuf) < 0)
+               die("unable to normalize object directory: %s",
+                   objdirbuf.buf);
  
        alt_copy = xmemdupz(alt, len);
        string_list_split_in_place(&entries, alt_copy, sep, -1);
                const char *entry = entries.items[i].string;
                if (entry[0] == '\0' || entry[0] == '#')
                        continue;
-               if (!is_absolute_path(entry) && depth) {
-                       error("%s: ignoring relative alternate object store %s",
-                                       relative_base, entry);
-               } else {
-                       link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
-               }
+               link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
        }
        string_list_clear(&entries, 0);
        free(alt_copy);
@@@ -381,6 -387,18 +387,18 @@@ void read_info_alternates(const char * 
        munmap(map, mapsz);
  }
  
+ struct alternate_object_database *alloc_alt_odb(const char *dir)
+ {
+       struct alternate_object_database *ent;
+       FLEX_ALLOC_STR(ent, path, dir);
+       strbuf_init(&ent->scratch, 0);
+       strbuf_addf(&ent->scratch, "%s/", dir);
+       ent->base_len = ent->scratch.len;
+       return ent;
+ }
  void add_to_alternates_file(const char *reference)
  {
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
        free(alts);
  }
  
+ void add_to_alternates_memory(const char *reference)
+ {
+       /*
+        * Make sure alternates are initialized, or else our entry may be
+        * overwritten when they are.
+        */
+       prepare_alt_odb();
+       link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
+ }
  /*
   * Compute the exact path an alternate is at and returns it. In case of
   * error NULL is returned and the human readable error is added to `err`
@@@ -566,8 -595,8 +595,8 @@@ static int check_and_freshen_nonlocal(c
        struct alternate_object_database *alt;
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
-               fill_sha1_path(alt->name, sha1);
-               if (check_and_freshen_file(alt->base, freshen))
+               const char *path = alt_sha1_path(alt, sha1);
+               if (check_and_freshen_file(path, freshen))
                        return 1;
        }
        return 0;
@@@ -1443,11 -1472,8 +1472,8 @@@ void prepare_packed_git(void
                return;
        prepare_packed_git_one(get_object_directory(), 1);
        prepare_alt_odb();
-       for (alt = alt_odb_list; alt; alt = alt->next) {
-               alt->name[-1] = 0;
-               prepare_packed_git_one(alt->base, 0);
-               alt->name[-1] = '/';
-       }
+       for (alt = alt_odb_list; alt; alt = alt->next)
+               prepare_packed_git_one(alt->path, 0);
        rearrange_packed_git();
        prepare_packed_git_mru();
        prepare_packed_git_run_once = 1;
@@@ -1565,8 -1591,8 +1591,8 @@@ static int stat_sha1_file(const unsigne
        prepare_alt_odb();
        errno = ENOENT;
        for (alt = alt_odb_list; alt; alt = alt->next) {
-               fill_sha1_path(alt->name, sha1);
-               if (!lstat(alt->base, st))
+               const char *path = alt_sha1_path(alt, sha1);
+               if (!lstat(path, st))
                        return 0;
        }
  
@@@ -1586,8 -1612,8 +1612,8 @@@ static int open_sha1_file(const unsigne
  
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
-               fill_sha1_path(alt->name, sha1);
-               fd = git_open_noatime(alt->base);
+               const char *path = alt_sha1_path(alt, sha1);
+               fd = git_open_noatime(path);
                if (fd >= 0)
                        return fd;
                if (most_interesting_errno == ENOENT)
@@@ -1826,9 -1852,11 +1852,9 @@@ static int parse_sha1_header_extended(c
  
  int parse_sha1_header(const char *hdr, unsigned long *sizep)
  {
 -      struct object_info oi;
 +      struct object_info oi = OBJECT_INFO_INIT;
  
        oi.sizep = sizep;
 -      oi.typename = NULL;
 -      oi.typep = NULL;
        return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
  }
  
@@@ -2066,8 -2094,8 +2092,8 @@@ unwind
        goto out;
  }
  
 -static int packed_object_info(struct packed_git *p, off_t obj_offset,
 -                            struct object_info *oi)
 +int packed_object_info(struct packed_git *p, off_t obj_offset,
 +                     struct object_info *oi)
  {
        struct pack_window *w_curs = NULL;
        unsigned long size;
@@@ -2838,7 -2866,7 +2864,7 @@@ int sha1_object_info_extended(const uns
  int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
  {
        enum object_type type;
 -      struct object_info oi = {NULL};
 +      struct object_info oi = OBJECT_INFO_INIT;
  
        oi.typep = &type;
        oi.sizep = sizep;
@@@ -3648,8 -3676,7 +3674,7 @@@ static int loose_from_alt_odb(struct al
        struct strbuf buf = STRBUF_INIT;
        int r;
  
-       /* copy base not including trailing '/' */
-       strbuf_add(&buf, alt->base, alt->name - alt->base - 1);
+       strbuf_addstr(&buf, alt->path);
        r = for_each_loose_file_in_objdir_buf(&buf,
                                              data->cb, NULL, NULL,
                                              data->data);
diff --combined sha1_name.c
index 3b647fd7cf79127cfa6f684836e387568a87c310,defbb3eb05aa3d82c86549337f9cc56e1f6076bd..409283614679b625f1cb6d191f1a2a32423bbcda
@@@ -7,20 -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 *);
  
  struct disambiguate_state {
 +      int len; /* length of prefix in hex chars */
 +      char hex_pfx[GIT_SHA1_HEXSZ + 1];
 +      unsigned char bin_pfx[GIT_SHA1_RAWSZ];
 +
        disambiguate_hint_fn fn;
        void *cb_data;
 -      unsigned char candidate[20];
 +      unsigned char candidate[GIT_SHA1_RAWSZ];
        unsigned candidate_exists:1;
        unsigned candidate_checked:1;
        unsigned candidate_ok:1;
@@@ -77,10 -72,10 +77,10 @@@ static void update_candidates(struct di
        /* 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_SHA1_HEXSZ];
        static struct alternate_object_database *fakeent;
  
        if (!fakeent) {
                 * 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/", ds->hex_pfx);
-               dir = opendir(alt->base);
 -              strbuf_addf(buf, "%.2s/", hex_pfx);
++              strbuf_addf(buf, "%.2s/", ds->hex_pfx);
+               dir = opendir(buf->buf);
                if (!dir)
                        continue;
  
  
                        if (strlen(de->d_name) != 38)
                                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))
@@@ -143,7 -131,9 +136,7 @@@ static int match_sha(unsigned len, cons
        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;
                int cmp;
  
                current = nth_packed_object_sha1(p, mid);
 -              cmp = hashcmp(bin_pfx, current);
 +              cmp = hashcmp(ds->bin_pfx, current);
                if (!cmp) {
                        first = mid;
                        break;
         */
        for (i = first; i < num && !ds->ambiguous; i++) {
                current = nth_packed_object_sha1(p, i);
 -              if (!match_sha(len, bin_pfx, current))
 +              if (!match_sha(ds->len, ds->bin_pfx, current))
                        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)
@@@ -271,7 -262,7 +264,7 @@@ static int disambiguate_treeish_only(co
                return 0;
  
        /* We need to do this the hard way... */
 -      obj = deref_tag(lookup_object(sha1), NULL, 0);
 +      obj = deref_tag(parse_object(sha1), NULL, 0);
        if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
                return 1;
        return 0;
@@@ -283,46 -274,14 +276,46 @@@ static int disambiguate_blob_only(cons
        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;
 +
 +      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;
  
 -      hashclr(bin_pfx);
 -      memset(hex_pfx, 'x', 40);
 +      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;
                }
                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[i >> 1] |= val;
        }
 +
 +      ds->len = len;
 +      ds->hex_pfx[len] = '\0';
 +      prepare_alt_odb();
 +      return 0;
 +}
 +
 +static int show_ambiguous_object(const unsigned char *sha1, void *data)
 +{
 +      const struct disambiguate_state *ds = data;
 +      struct strbuf desc = STRBUF_INIT;
 +      int type;
 +
 +      if (ds->fn && !ds->fn(sha1, ds->cb_data))
 +              return 0;
 +
 +      type = sha1_object_info(sha1, NULL);
 +      if (type == OBJ_COMMIT) {
 +              struct commit *commit = lookup_commit(sha1);
 +              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(sha1);
 +              if (!parse_tag(tag) && tag->tag)
 +                      strbuf_addf(&desc, " %s", tag->tag);
 +      }
 +
 +      advise("  %s %s%s",
 +             find_unique_abbrev(sha1, DEFAULT_ABBREV),
 +             typename(type) ? typename(type) : "unknown type",
 +             desc.buf);
 +
 +      strbuf_release(&desc);
        return 0;
  }
  
@@@ -384,15 -307,19 +377,15 @@@ static int get_short_sha1(const char *n
                          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)
                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 unsigned char *sha1, void *data)
 +{
 +      sha1_array_append(data, sha1);
 +      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 sha1_array collect = SHA1_ARRAY_INIT;
        struct disambiguate_state ds;
 -      int len = strlen(prefix);
 +      int ret;
  
 -      if (len < MINIMUM_ABBREV || len > 40)
 -              return -1;
 -      if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
 +      if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0)
                return -1;
  
 -      prepare_alt_odb();
 -
 -      memset(&ds, 0, sizeof(ds));
        ds.always_call_fn = 1;
 -      ds.cb_data = cb_data;
 -      ds.fn = fn;
 -
 -      find_short_object_filename(len, hex_pfx, &ds);
 -      find_short_packed_object(len, bin_pfx, &ds);
 -      return ds.ambiguous;
 +      ds.fn = collect_ambiguous;
 +      ds.cb_data = &collect;
 +      find_short_object_filename(&ds);
 +      find_short_packed_object(&ds);
 +
 +      ret = sha1_array_for_each_unique(&collect, fn, cb_data);
 +      sha1_array_clear(&collect);
 +      return ret;
  }
  
  int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
@@@ -761,12 -670,12 +754,12 @@@ struct object *peel_to_type(const char 
        }
  }
  
 -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];
        const char *sp;
        unsigned int expected_type = 0;
 -      unsigned lookup_flags = 0;
        struct object *o;
  
        /*
        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))
                return -1;
@@@ -911,7 -819,7 +904,7 @@@ static int get_sha1_1(const char *name
                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;
  
@@@ -1467,9 -1375,6 +1460,9 @@@ static int get_sha1_with_context_1(cons
        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;
        ret = get_sha1_1(name, namelen, sha1, flags);
        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;
  
diff --combined submodule.c
index 2de06a33516d40ab169a9a031df4fb547f9094fd,8b3274a9dcf0854b79475b7a69c35b2a7c2b3bc5..733332035b3c19279885175e0689665a34dc13ef
@@@ -123,9 -123,7 +123,7 @@@ void stage_updated_gitmodules(void
  static int add_submodule_odb(const char *path)
  {
        struct strbuf objects_directory = STRBUF_INIT;
-       struct alternate_object_database *alt_odb;
        int ret = 0;
-       size_t alloc;
  
        ret = strbuf_git_path_submodule(&objects_directory, path, "objects/");
        if (ret)
                ret = -1;
                goto done;
        }
-       /* avoid adding it twice */
-       prepare_alt_odb();
-       for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next)
-               if (alt_odb->name - alt_odb->base == objects_directory.len &&
-                               !strncmp(alt_odb->base, objects_directory.buf,
-                                       objects_directory.len))
-                       goto done;
-       alloc = st_add(objects_directory.len, 42); /* for "12/345..." sha1 */
-       alt_odb = xmalloc(st_add(sizeof(*alt_odb), alloc));
-       alt_odb->next = alt_odb_list;
-       xsnprintf(alt_odb->base, alloc, "%s", objects_directory.buf);
-       alt_odb->name = alt_odb->base + objects_directory.len;
-       alt_odb->name[2] = '/';
-       alt_odb->name[40] = '\0';
-       alt_odb->name[41] = '\0';
-       alt_odb_list = alt_odb;
-       /* add possible alternates from the submodule */
-       read_info_alternates(objects_directory.buf, 0);
+       add_to_alternates_memory(objects_directory.buf);
  done:
        strbuf_release(&objects_directory);
        return ret;
@@@ -396,7 -375,7 +375,7 @@@ output_header
                        find_unique_abbrev(one->hash, DEFAULT_ABBREV));
        if (!fast_backward && !fast_forward)
                strbuf_addch(&sb, '.');
 -      strbuf_addf(&sb, "%s", find_unique_abbrev(two->hash, DEFAULT_ABBREV));
 +      strbuf_add_unique_abbrev(&sb, two->hash, DEFAULT_ABBREV);
        if (message)
                strbuf_addf(&sb, " %s%s\n", message, reset);
        else
@@@ -728,10 -707,9 +707,10 @@@ void check_for_new_submodule_commits(un
        sha1_array_append(&ref_tips_after_fetch, new_sha1);
  }
  
 -static void add_sha1_to_argv(const unsigned char sha1[20], void *data)
 +static int add_sha1_to_argv(const unsigned char sha1[20], void *data)
  {
        argv_array_push(data, sha1_to_hex(sha1));
 +      return 0;
  }
  
  static void calculate_changed_submodule_paths(void)
diff --combined transport.c
index a85801042b012c2e2684cbb4e5e3dc1925e43582,4bc4eeae54ea96bbe48658eb3e6718590fee4350..079499dbafd749aaaebcac96d717893c8919ef23
@@@ -151,15 -151,6 +151,15 @@@ static int set_git_option(struct git_tr
                                die(_("transport: invalid depth option '%s'"), value);
                }
                return 0;
 +      } else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) {
 +              opts->deepen_since = value;
 +              return 0;
 +      } else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {
 +              opts->deepen_not = (const struct string_list *)value;
 +              return 0;
 +      } else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {
 +              opts->deepen_relative = !!value;
 +              return 0;
        }
        return 1;
  }
@@@ -220,9 -211,6 +220,9 @@@ static int fetch_refs_via_pack(struct t
        args.quiet = (transport->verbose < 0);
        args.no_progress = !transport->progress;
        args.depth = data->options.depth;
 +      args.deepen_since = data->options.deepen_since;
 +      args.deepen_not = data->options.deepen_not;
 +      args.deepen_relative = data->options.deepen_relative;
        args.check_self_contained_and_connected =
                data->options.check_self_contained_and_connected;
        args.cloning = transport->cloning;
@@@ -1096,9 -1084,7 +1096,7 @@@ static int refs_from_alternate_cb(struc
        const struct ref *extra;
        struct alternate_refs_data *cb = data;
  
-       e->name[-1] = '\0';
-       other = xstrdup(real_path(e->base));
-       e->name[-1] = '/';
+       other = xstrdup(real_path(e->path));
        len = strlen(other);
  
        while (other[len-1] == '/')