Merge branch 'bc/hash-transition-part-15'
authorJunio C Hamano <gitster@pobox.com>
Tue, 30 Oct 2018 06:43:42 +0000 (15:43 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 30 Oct 2018 06:43:42 +0000 (15:43 +0900)
More codepaths are moving away from hardcoded hash sizes.

* bc/hash-transition-part-15:
rerere: convert to use the_hash_algo
submodule: make zero-oid comparison hash function agnostic
apply: rename new_sha1_prefix and old_sha1_prefix
apply: replace hard-coded constants
tag: express constant in terms of the_hash_algo
transport: use parse_oid_hex instead of a constant
upload-pack: express constants in terms of the_hash_algo
refs/packed-backend: express constants using the_hash_algo
packfile: express constants in terms of the_hash_algo
pack-revindex: express constants in terms of the_hash_algo
builtin/fetch-pack: remove constants with parse_oid_hex
builtin/mktree: remove hard-coded constant
builtin/repack: replace hard-coded constants
pack-bitmap-write: use GIT_MAX_RAWSZ for allocation
object_id.cocci: match only expressions of type 'struct object_id'

1  2 
apply.c
pack-bitmap-write.c
packfile.c
rerere.c
transport.c
upload-pack.c
diff --combined apply.c
index fdae1d423b2e637a0f6b145c848585fff285e414,b9eb02ec12e452397f6a10503fbda836fd3a6777..073d5f04512ac6febc2949040f53ef273433f3c0
+++ b/apply.c
@@@ -223,8 -223,8 +223,8 @@@ struct patch 
        struct fragment *fragments;
        char *result;
        size_t resultsize;
-       char old_sha1_prefix[41];
-       char new_sha1_prefix[41];
+       char old_oid_prefix[GIT_MAX_HEXSZ + 1];
+       char new_oid_prefix[GIT_MAX_HEXSZ + 1];
        struct patch *next;
  
        /* three-way fallback result */
@@@ -1093,13 -1093,14 +1093,14 @@@ static int gitdiff_index(struct apply_s
         */
        const char *ptr, *eol;
        int len;
+       const unsigned hexsz = the_hash_algo->hexsz;
  
        ptr = strchr(line, '.');
-       if (!ptr || ptr[1] != '.' || 40 < ptr - line)
+       if (!ptr || ptr[1] != '.' || hexsz < ptr - line)
                return 0;
        len = ptr - line;
-       memcpy(patch->old_sha1_prefix, line, len);
-       patch->old_sha1_prefix[len] = 0;
+       memcpy(patch->old_oid_prefix, line, len);
+       patch->old_oid_prefix[len] = 0;
  
        line = ptr + 2;
        ptr = strchr(line, ' ');
                ptr = eol;
        len = ptr - line;
  
-       if (40 < len)
+       if (hexsz < len)
                return 0;
-       memcpy(patch->new_sha1_prefix, line, len);
-       patch->new_sha1_prefix[len] = 0;
+       memcpy(patch->new_oid_prefix, line, len);
+       patch->new_oid_prefix[len] = 0;
        if (*ptr == ' ')
                return gitdiff_oldmode(state, ptr + 1, patch);
        return 0;
@@@ -2131,12 -2132,10 +2132,12 @@@ static int parse_chunk(struct apply_sta
  
        if (!use_patch(state, patch))
                patch->ws_rule = 0;
 +      else if (patch->new_name)
 +              patch->ws_rule = whitespace_rule(state->repo->index,
 +                                               patch->new_name);
        else
 -              patch->ws_rule = whitespace_rule(patch->new_name
 -                                               ? patch->new_name
 -                                               : patch->old_name);
 +              patch->ws_rule = whitespace_rule(state->repo->index,
 +                                               patch->old_name);
  
        patchsize = parse_single_patch(state,
                                       buffer + offset + hdrsize,
@@@ -2206,7 -2205,7 +2207,7 @@@ static void reverse_patches(struct patc
                SWAP(p->new_mode, p->old_mode);
                SWAP(p->is_new, p->is_delete);
                SWAP(p->lines_added, p->lines_deleted);
-               SWAP(p->old_sha1_prefix, p->new_sha1_prefix);
+               SWAP(p->old_oid_prefix, p->new_oid_prefix);
  
                for (; frag; frag = frag->next) {
                        SWAP(frag->newpos, frag->oldpos);
@@@ -3144,15 -3143,16 +3145,16 @@@ static int apply_binary(struct apply_st
  {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        struct object_id oid;
+       const unsigned hexsz = the_hash_algo->hexsz;
  
        /*
         * For safety, we require patch index line to contain
-        * full 40-byte textual SHA1 for old and new, at least for now.
+        * full hex textual object ID for old and new, at least for now.
         */
-       if (strlen(patch->old_sha1_prefix) != 40 ||
-           strlen(patch->new_sha1_prefix) != 40 ||
-           get_oid_hex(patch->old_sha1_prefix, &oid) ||
-           get_oid_hex(patch->new_sha1_prefix, &oid))
+       if (strlen(patch->old_oid_prefix) != hexsz ||
+           strlen(patch->new_oid_prefix) != hexsz ||
+           get_oid_hex(patch->old_oid_prefix, &oid) ||
+           get_oid_hex(patch->new_oid_prefix, &oid))
                return error(_("cannot apply binary patch to '%s' "
                               "without full index line"), name);
  
                 * applies to.
                 */
                hash_object_file(img->buf, img->len, blob_type, &oid);
-               if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
+               if (strcmp(oid_to_hex(&oid), patch->old_oid_prefix))
                        return error(_("the patch applies to '%s' (%s), "
                                       "which does not match the "
                                       "current contents."),
                                       "'%s' but it is not empty"), name);
        }
  
-       get_oid_hex(patch->new_sha1_prefix, &oid);
+       get_oid_hex(patch->new_oid_prefix, &oid);
        if (is_null_oid(&oid)) {
                clear_image(img);
                return 0; /* deletion patch */
                if (!result)
                        return error(_("the necessary postimage %s for "
                                       "'%s' cannot be read"),
-                                    patch->new_sha1_prefix, name);
+                                    patch->new_oid_prefix, name);
                clear_image(img);
                img->buf = result;
                img->len = size;
  
                /* verify that the result matches */
                hash_object_file(img->buf, img->len, blob_type, &oid);
-               if (strcmp(oid_to_hex(&oid), patch->new_sha1_prefix))
+               if (strcmp(oid_to_hex(&oid), patch->new_oid_prefix))
                        return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
-                               name, patch->new_sha1_prefix, oid_to_hex(&oid));
+                               name, patch->new_oid_prefix, oid_to_hex(&oid));
        }
  
        return 0;
@@@ -3469,8 -3469,7 +3471,8 @@@ static int load_preimage(struct apply_s
        return 0;
  }
  
 -static int three_way_merge(struct image *image,
 +static int three_way_merge(struct apply_state *state,
 +                         struct image *image,
                           char *path,
                           const struct object_id *base,
                           const struct object_id *ours,
        status = ll_merge(&result, path,
                          &base_file, "base",
                          &our_file, "ours",
 -                        &their_file, "theirs", NULL);
 +                        &their_file, "theirs",
 +                        state->repo->index,
 +                        NULL);
        free(base_file.ptr);
        free(our_file.ptr);
        free(their_file.ptr);
@@@ -3568,7 -3565,7 +3570,7 @@@ static int try_threeway(struct apply_st
        /* Preimage the patch was prepared for */
        if (patch->is_new)
                write_object_file("", 0, blob_type, &pre_oid);
-       else if (get_oid(patch->old_sha1_prefix, &pre_oid) ||
+       else if (get_oid(patch->old_oid_prefix, &pre_oid) ||
                 read_blob_object(&buf, &pre_oid, patch->old_mode))
                return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
  
        clear_image(&tmp_image);
  
        /* in-core three-way merge between post and our using pre as base */
 -      status = three_way_merge(image, patch->new_name,
 +      status = three_way_merge(state, image, patch->new_name,
                                 &pre_oid, &our_oid, &post_oid);
        if (status < 0) {
                if (state->apply_verbosity > verbosity_silent)
@@@ -4060,13 -4057,13 +4062,13 @@@ static int preimage_oid_in_gitlink_patc
            starts_with(++preimage, heading) &&
            /* does it record full SHA-1? */
            !get_oid_hex(preimage + sizeof(heading) - 1, oid) &&
-           preimage[sizeof(heading) + GIT_SHA1_HEXSZ - 1] == '\n' &&
+           preimage[sizeof(heading) + the_hash_algo->hexsz - 1] == '\n' &&
            /* does the abbreviated name on the index line agree with it? */
-           starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
+           starts_with(preimage + sizeof(heading) - 1, p->old_oid_prefix))
                return 0; /* it all looks fine */
  
        /* we may have full object name on the index line */
-       return get_oid_hex(p->old_sha1_prefix, oid);
+       return get_oid_hex(p->old_oid_prefix, oid);
  }
  
  /* Build an index that contains just the files needed for a 3way merge */
@@@ -4095,7 -4092,7 +4097,7 @@@ static int build_fake_ancestor(struct a
                        else
                                return error(_("sha1 information is lacking or "
                                               "useless for submodule %s"), name);
-               } else if (!get_oid_blob(patch->old_sha1_prefix, &oid)) {
+               } else if (!get_oid_blob(patch->old_oid_prefix, &oid)) {
                        ; /* ok */
                } else if (!patch->lines_added && !patch->lines_deleted) {
                        /* mode-only change: update the current */
@@@ -4632,7 -4629,7 +4634,7 @@@ static int write_out_results(struct app
                }
                string_list_clear(&cpath, 0);
  
 -              rerere(0);
 +              repo_rerere(state->repo, 0);
        }
  
        return errs;
diff --combined pack-bitmap-write.c
index f2fd9d81ef6a95612f86a05ce4eb183436d7bab8,6f0c78d6aaefcc678713eab95bb127cb7b76dff3..9d1b951697e9552b80a502fd587e43b615369635
@@@ -37,7 -37,7 +37,7 @@@ struct bitmap_writer 
  
        struct progress *progress;
        int show_progress;
-       unsigned char pack_checksum[20];
+       unsigned char pack_checksum[GIT_MAX_RAWSZ];
  };
  
  static struct bitmap_writer writer;
@@@ -262,7 -262,7 +262,7 @@@ void bitmap_writer_build(struct packing
        if (writer.show_progress)
                writer.progress = start_progress("Building bitmaps", writer.selected_nr);
  
 -      init_revisions(&revs, NULL);
 +      repo_init_revisions(the_repository, &revs, NULL);
        revs.tag_objects = 1;
        revs.tree_objects = 1;
        revs.blob_objects = 1;
diff --combined packfile.c
index 86074a76e965087ed826e3170b867e995fd9e9fe,17f993b5bfec64b2c747bd5f8daa7f8628cfa374..f2850a00b58cccfec57335fb8e0500b7b1cba093
@@@ -80,8 -80,10 +80,8 @@@ void pack_report(void
  static int check_packed_git_idx(const char *path, struct packed_git *p)
  {
        void *idx_map;
 -      struct pack_idx_header *hdr;
        size_t idx_size;
 -      uint32_t version, nr, i, *index;
 -      int fd = git_open(path);
 +      int fd = git_open(path), ret;
        struct stat st;
        const unsigned int hashsz = the_hash_algo->rawsz;
  
        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
  
 -      hdr = idx_map;
 +      ret = load_idx(path, hashsz, idx_map, idx_size, p);
 +
 +      if (ret)
 +              munmap(idx_map, idx_size);
 +
 +      return ret;
 +}
 +
 +int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
 +           size_t idx_size, struct packed_git *p)
 +{
 +      struct pack_idx_header *hdr = idx_map;
 +      uint32_t version, nr, i, *index;
 +
 +      if (idx_size < 4 * 256 + hashsz + hashsz)
 +              return error("index file %s is too small", path);
 +      if (idx_map == NULL)
 +              return error("empty data");
 +
        if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
                version = ntohl(hdr->idx_version);
 -              if (version < 2 || version > 2) {
 -                      munmap(idx_map, idx_size);
 +              if (version < 2 || version > 2)
                        return error("index file %s is version %"PRIu32
                                     " and is not supported by this binary"
                                     " (try upgrading GIT to a newer version)",
                                     path, version);
 -              }
        } else
                version = 1;
  
                index += 2;  /* skip index header */
        for (i = 0; i < 256; i++) {
                uint32_t n = ntohl(index[i]);
 -              if (n < nr) {
 -                      munmap(idx_map, idx_size);
 +              if (n < nr)
                        return error("non-monotonic index %s", path);
 -              }
                nr = n;
        }
  
                 *  - hash of the packfile
                 *  - file checksum
                 */
 -              if (idx_size != 4*256 + nr * (hashsz + 4) + hashsz + hashsz) {
 -                      munmap(idx_map, idx_size);
 +              if (idx_size != 4 * 256 + nr * (hashsz + 4) + hashsz + hashsz)
                        return error("wrong index v1 file size in %s", path);
 -              }
        } else if (version == 2) {
                /*
                 * Minimum size:
                unsigned long max_size = min_size;
                if (nr)
                        max_size += (nr - 1)*8;
 -              if (idx_size < min_size || idx_size > max_size) {
 -                      munmap(idx_map, idx_size);
 +              if (idx_size < min_size || idx_size > max_size)
                        return error("wrong index v2 file size in %s", path);
 -              }
                if (idx_size != min_size &&
                    /*
                     * make sure we can deal with large pack offsets.
                     * 31-bit signed offset won't be enough, neither
                     * 32-bit unsigned one will be.
                     */
 -                  (sizeof(off_t) <= 4)) {
 -                      munmap(idx_map, idx_size);
 +                  (sizeof(off_t) <= 4))
                        return error("pack too large for current definition of off_t in %s", path);
 -              }
        }
  
        p->index_version = version;
@@@ -1127,13 -1121,14 +1127,14 @@@ int unpack_object_header(struct packed_
  void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1)
  {
        unsigned i;
+       const unsigned hashsz = the_hash_algo->rawsz;
        for (i = 0; i < p->num_bad_objects; i++)
-               if (hasheq(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
+               if (hasheq(sha1, p->bad_object_sha1 + hashsz * i))
                        return;
        p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
                                      st_mult(GIT_MAX_RAWSZ,
                                              st_add(p->num_bad_objects, 1)));
-       hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
+       hashcpy(p->bad_object_sha1 + hashsz * p->num_bad_objects, sha1);
        p->num_bad_objects++;
  }
  
diff --combined rerere.c
index 887e26d45bfa1071736ef016d82c69de90b14f8d,ceb98015ffa0617f11894dc00c62b174f45c5e4c..b5b2357411f145867eb1918cbc79f77c6192db1a
+++ b/rerere.c
@@@ -29,7 -29,7 +29,7 @@@ static int rerere_dir_alloc
  #define RR_HAS_POSTIMAGE 1
  #define RR_HAS_PREIMAGE 2
  static struct rerere_dir {
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_HEXSZ];
        int status_alloc, status_nr;
        unsigned char *status;
  } **rerere_dir;
@@@ -52,7 -52,7 +52,7 @@@ static void free_rerere_id(struct strin
  
  static const char *rerere_id_hex(const struct rerere_id *id)
  {
-       return sha1_to_hex(id->collection->sha1);
+       return sha1_to_hex(id->collection->hash);
  }
  
  static void fit_variant(struct rerere_dir *rr_dir, int variant)
@@@ -115,7 -115,7 +115,7 @@@ static int is_rr_file(const char *name
  static void scan_rerere_dir(struct rerere_dir *rr_dir)
  {
        struct dirent *de;
-       DIR *dir = opendir(git_path("rr-cache/%s", sha1_to_hex(rr_dir->sha1)));
+       DIR *dir = opendir(git_path("rr-cache/%s", sha1_to_hex(rr_dir->hash)));
  
        if (!dir)
                return;
        closedir(dir);
  }
  
- static const unsigned char *rerere_dir_sha1(size_t i, void *table)
+ static const unsigned char *rerere_dir_hash(size_t i, void *table)
  {
        struct rerere_dir **rr_dir = table;
-       return rr_dir[i]->sha1;
+       return rr_dir[i]->hash;
  }
  
  static struct rerere_dir *find_rerere_dir(const char *hex)
  {
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_RAWSZ];
        struct rerere_dir *rr_dir;
        int pos;
  
-       if (get_sha1_hex(hex, sha1))
+       if (get_sha1_hex(hex, hash))
                return NULL; /* BUG */
-       pos = sha1_pos(sha1, rerere_dir, rerere_dir_nr, rerere_dir_sha1);
+       pos = sha1_pos(hash, rerere_dir, rerere_dir_nr, rerere_dir_hash);
        if (pos < 0) {
                rr_dir = xmalloc(sizeof(*rr_dir));
-               hashcpy(rr_dir->sha1, sha1);
+               hashcpy(rr_dir->hash, hash);
                rr_dir->status = NULL;
                rr_dir->status_nr = 0;
                rr_dir->status_alloc = 0;
@@@ -207,26 -207,27 +207,27 @@@ static void read_rr(struct string_list 
                return;
        while (!strbuf_getwholeline(&buf, in, '\0')) {
                char *path;
-               unsigned char sha1[20];
+               unsigned char hash[GIT_MAX_RAWSZ];
                struct rerere_id *id;
                int variant;
+               const unsigned hexsz = the_hash_algo->hexsz;
  
                /* There has to be the hash, tab, path and then NUL */
-               if (buf.len < 42 || get_sha1_hex(buf.buf, sha1))
+               if (buf.len < hexsz + 2 || get_sha1_hex(buf.buf, hash))
                        die(_("corrupt MERGE_RR"));
  
-               if (buf.buf[40] != '.') {
+               if (buf.buf[hexsz] != '.') {
                        variant = 0;
-                       path = buf.buf + 40;
+                       path = buf.buf + hexsz;
                } else {
                        errno = 0;
-                       variant = strtol(buf.buf + 41, &path, 10);
+                       variant = strtol(buf.buf + hexsz + 1, &path, 10);
                        if (errno)
                                die(_("corrupt MERGE_RR"));
                }
                if (*(path++) != '\t')
                        die(_("corrupt MERGE_RR"));
-               buf.buf[40] = '\0';
+               buf.buf[hexsz] = '\0';
                id = new_rerere_id_hex(buf.buf);
                id->variant = variant;
                string_list_insert(rr, path)->util = id;
@@@ -360,7 -361,7 +361,7 @@@ static void rerere_strbuf_putconflict(s
  }
  
  static int handle_conflict(struct strbuf *out, struct rerere_io *io,
-                          int marker_size, git_SHA_CTX *ctx)
+                          int marker_size, git_hash_ctx *ctx)
  {
        enum {
                RR_SIDE_1 = 0, RR_SIDE_2, RR_ORIGINAL
                        strbuf_addbuf(out, &two);
                        rerere_strbuf_putconflict(out, '>', marker_size);
                        if (ctx) {
-                               git_SHA1_Update(ctx, one.buf ? one.buf : "",
-                                           one.len + 1);
-                               git_SHA1_Update(ctx, two.buf ? two.buf : "",
-                                           two.len + 1);
+                               the_hash_algo->update_fn(ctx, one.buf ?
+                                                        one.buf : "",
+                                                        one.len + 1);
+                               the_hash_algo->update_fn(ctx, two.buf ?
+                                                        two.buf : "",
+                                                        two.len + 1);
                        }
                        break;
                } else if (hunk == RR_SIDE_1)
   * Return 1 if conflict hunks are found, 0 if there are no conflict
   * hunks and -1 if an error occured.
   */
- static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size)
+ static int handle_path(unsigned char *hash, struct rerere_io *io, int marker_size)
  {
-       git_SHA_CTX ctx;
+       git_hash_ctx ctx;
        struct strbuf buf = STRBUF_INIT, out = STRBUF_INIT;
        int has_conflicts = 0;
-       if (sha1)
-               git_SHA1_Init(&ctx);
+       if (hash)
+               the_hash_algo->init_fn(&ctx);
  
        while (!io->getline(&buf, io)) {
                if (is_cmarker(buf.buf, '<', marker_size)) {
                        has_conflicts = handle_conflict(&out, io, marker_size,
-                                                       sha1 ? &ctx : NULL);
+                                                       hash ? &ctx : NULL);
                        if (has_conflicts < 0)
                                break;
                        rerere_io_putmem(out.buf, out.len, io);
        strbuf_release(&buf);
        strbuf_release(&out);
  
-       if (sha1)
-               git_SHA1_Final(sha1, &ctx);
+       if (hash)
+               the_hash_algo->final_fn(hash, &ctx);
  
        return has_conflicts;
  }
   * Scan the path for conflicts, do the "handle_path()" thing above, and
   * return the number of conflict hunks found.
   */
- static int handle_file(struct index_state *istate, const char *path,
-                      unsigned char *sha1, const char *output)
 -static int handle_file(const char *path, unsigned char *hash, const char *output)
++static int handle_file(struct index_state *istate,
++                     const char *path, unsigned char *hash, const char *output)
  {
        int has_conflicts = 0;
        struct rerere_io_file io;
 -      int marker_size = ll_merge_marker_size(path);
 +      int marker_size = ll_merge_marker_size(istate, path);
  
        memset(&io, 0, sizeof(io));
        io.io.getline = rerere_file_getline;
                }
        }
  
-       has_conflicts = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+       has_conflicts = handle_path(hash, (struct rerere_io *)&io, marker_size);
  
        fclose(io.input);
        if (io.io.wrerror)
   * stages we have already looked at in this invocation of this
   * function.
   */
 -static int check_one_conflict(int i, int *type)
 +static int check_one_conflict(struct index_state *istate, int i, int *type)
  {
 -      const struct cache_entry *e = active_cache[i];
 +      const struct cache_entry *e = istate->cache[i];
  
        if (!ce_stage(e)) {
                *type = RESOLVED;
        }
  
        *type = PUNTED;
 -      while (i < active_nr && ce_stage(active_cache[i]) == 1)
 +      while (i < istate->cache_nr && ce_stage(istate->cache[i]) == 1)
                i++;
  
        /* Only handle regular files with both stages #2 and #3 */
 -      if (i + 1 < active_nr) {
 -              const struct cache_entry *e2 = active_cache[i];
 -              const struct cache_entry *e3 = active_cache[i + 1];
 +      if (i + 1 < istate->cache_nr) {
 +              const struct cache_entry *e2 = istate->cache[i];
 +              const struct cache_entry *e3 = istate->cache[i + 1];
                if (ce_stage(e2) == 2 &&
                    ce_stage(e3) == 3 &&
                    ce_same_name(e, e3) &&
        }
  
        /* Skip the entries with the same name */
 -      while (i < active_nr && ce_same_name(e, active_cache[i]))
 +      while (i < istate->cache_nr && ce_same_name(e, istate->cache[i]))
                i++;
        return i;
  }
   * are identical to the previous round, might want to be handled,
   * though.
   */
 -static int find_conflict(struct string_list *conflict)
 +static int find_conflict(struct repository *r, struct string_list *conflict)
  {
        int i;
 -      if (read_cache() < 0)
 +
 +      if (read_index(r->index) < 0)
                return error(_("index file corrupt"));
  
 -      for (i = 0; i < active_nr;) {
 +      for (i = 0; i < r->index->cache_nr;) {
                int conflict_type;
 -              const struct cache_entry *e = active_cache[i];
 -              i = check_one_conflict(i, &conflict_type);
 +              const struct cache_entry *e = r->index->cache[i];
 +              i = check_one_conflict(r->index, i, &conflict_type);
                if (conflict_type == THREE_STAGED)
                        string_list_insert(conflict, (const char *)e->name);
        }
   * NEEDSWORK: we may want to fix the caller that implements "rerere
   * remaining" to do this without abusing merge_rr.
   */
 -int rerere_remaining(struct string_list *merge_rr)
 +int rerere_remaining(struct repository *r, struct string_list *merge_rr)
  {
        int i;
 +
        if (setup_rerere(merge_rr, RERERE_READONLY))
                return 0;
 -      if (read_cache() < 0)
 +      if (read_index(r->index) < 0)
                return error(_("index file corrupt"));
  
 -      for (i = 0; i < active_nr;) {
 +      for (i = 0; i < r->index->cache_nr;) {
                int conflict_type;
 -              const struct cache_entry *e = active_cache[i];
 -              i = check_one_conflict(i, &conflict_type);
 +              const struct cache_entry *e = r->index->cache[i];
 +              i = check_one_conflict(r->index, i, &conflict_type);
                if (conflict_type == PUNTED)
                        string_list_insert(merge_rr, (const char *)e->name);
                else if (conflict_type == RESOLVED) {
   * if that recorded conflict resolves cleanly what we
   * got in the "cur".
   */
 -static int try_merge(const struct rerere_id *id, const char *path,
 +static int try_merge(struct index_state *istate,
 +                   const struct rerere_id *id, const char *path,
                     mmfile_t *cur, mmbuffer_t *result)
  {
        int ret;
                 * A three-way merge. Note that this honors user-customizable
                 * low-level merge driver settings.
                 */
 -              ret = ll_merge(result, path, &base, NULL, cur, "", &other, "", NULL);
 +              ret = ll_merge(result, path, &base, NULL, cur, "", &other, "",
 +                             istate, NULL);
  
        free(base.ptr);
        free(other.ptr);
   * Returns 0 for successful replay of recorded resolution, or non-zero
   * for failure.
   */
 -static int merge(const struct rerere_id *id, const char *path)
 +static int merge(struct index_state *istate, const struct rerere_id *id, const char *path)
  {
        FILE *f;
        int ret;
         * Normalize the conflicts in path and write it out to
         * "thisimage" temporary file.
         */
 -      if ((handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) ||
 +      if ((handle_file(istate, path, NULL, rerere_path(id, "thisimage")) < 0) ||
            read_mmfile(&cur, rerere_path(id, "thisimage"))) {
                ret = 1;
                goto out;
        }
  
 -      ret = try_merge(id, path, &cur, &result);
 +      ret = try_merge(istate, id, path, &cur, &result);
        if (ret)
                goto out;
  
@@@ -697,7 -695,7 +700,7 @@@ out
        return ret;
  }
  
 -static void update_paths(struct string_list *update)
 +static void update_paths(struct repository *r, struct string_list *update)
  {
        struct lock_file index_lock = LOCK_INIT;
        int i;
  
        for (i = 0; i < update->nr; i++) {
                struct string_list_item *item = &update->items[i];
 -              if (add_file_to_cache(item->string, 0))
 +              if (add_file_to_index(r->index, item->string, 0))
                        exit(128);
                fprintf_ln(stderr, _("Staged '%s' using previous resolution."),
                        item->string);
        }
  
 -      if (write_locked_index(&the_index, &index_lock,
 +      if (write_locked_index(r->index, &index_lock,
                               COMMIT_LOCK | SKIP_IF_UNCHANGED))
                die(_("unable to write new index file"));
  }
@@@ -731,8 -729,7 +734,8 @@@ static void remove_variant(struct rerer
   * only have the preimage for that conflict, in which case the result
   * needs to be recorded as a resolution in a postimage file.
   */
 -static void do_rerere_one_path(struct string_list_item *rr_item,
 +static void do_rerere_one_path(struct index_state *istate,
 +                             struct string_list_item *rr_item,
                               struct string_list *update)
  {
        const char *path = rr_item->string;
  
        /* Has the user resolved it already? */
        if (variant >= 0) {
 -              if (!handle_file(path, NULL, NULL)) {
 +              if (!handle_file(istate, path, NULL, NULL)) {
                        copy_file(rerere_path(id, "postimage"), path, 0666);
                        id->collection->status[variant] |= RR_HAS_POSTIMAGE;
                        fprintf_ln(stderr, _("Recorded resolution for '%s'."), path);
                        continue;
  
                vid.variant = variant;
 -              if (merge(&vid, path))
 +              if (merge(istate, &vid, path))
                        continue; /* failed to replay */
  
                /*
        assign_variant(id);
  
        variant = id->variant;
 -      handle_file(path, NULL, rerere_path(id, "preimage"));
 +      handle_file(istate, path, NULL, rerere_path(id, "preimage"));
        if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
                const char *path = rerere_path(id, "postimage");
                if (unlink(path))
        fprintf_ln(stderr, _("Recorded preimage for '%s'"), path);
  }
  
 -static int do_plain_rerere(struct string_list *rr, int fd)
 +static int do_plain_rerere(struct repository *r,
 +                         struct string_list *rr, int fd)
  {
        struct string_list conflict = STRING_LIST_INIT_DUP;
        struct string_list update = STRING_LIST_INIT_DUP;
        int i;
  
 -      find_conflict(&conflict);
 +      find_conflict(r, &conflict);
  
        /*
         * MERGE_RR records paths with conflicts immediately after
         */
        for (i = 0; i < conflict.nr; i++) {
                struct rerere_id *id;
-               unsigned char sha1[20];
+               unsigned char hash[GIT_MAX_RAWSZ];
                const char *path = conflict.items[i].string;
                int ret;
  
                 * conflict ID.  No need to write anything out
                 * yet.
                 */
-               ret = handle_file(r->index, path, sha1, NULL);
 -              ret = handle_file(path, hash, NULL);
++              ret = handle_file(r->index, path, hash, NULL);
                if (ret != 0 && string_list_has_string(rr, path)) {
                        remove_variant(string_list_lookup(rr, path)->util);
                        string_list_remove(rr, path, 1);
                if (ret < 1)
                        continue;
  
-               id = new_rerere_id(sha1);
+               id = new_rerere_id(hash);
                string_list_insert(rr, path)->util = id;
  
                /* Ensure that the directory exists. */
        }
  
        for (i = 0; i < rr->nr; i++)
 -              do_rerere_one_path(&rr->items[i], &update);
 +              do_rerere_one_path(r->index, &rr->items[i], &update);
  
        if (update.nr)
 -              update_paths(&update);
 +              update_paths(r, &update);
  
        return write_rr(rr, fd);
  }
@@@ -904,7 -900,7 +907,7 @@@ int setup_rerere(struct string_list *me
   * perform mergy operations, possibly leaving conflicted index entries
   * and working tree files.
   */
 -int rerere(int flags)
 +int repo_rerere(struct repository *r, int flags)
  {
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
        int fd, status;
        fd = setup_rerere(&merge_rr, flags);
        if (fd < 0)
                return 0;
 -      status = do_plain_rerere(&merge_rr, fd);
 +      status = do_plain_rerere(r, &merge_rr, fd);
        free_rerere_dirs();
        return status;
  }
@@@ -949,30 -945,29 +952,30 @@@ static int rerere_mem_getline(struct st
        return 0;
  }
  
- static int handle_cache(struct index_state *istate, const char *path,
-                       unsigned char *sha1, const char *output)
 -static int handle_cache(const char *path, unsigned char *hash, const char *output)
++static int handle_cache(struct index_state *istate,
++                      const char *path, unsigned char *hash, const char *output)
  {
        mmfile_t mmfile[3] = {{NULL}};
        mmbuffer_t result = {NULL, 0};
        const struct cache_entry *ce;
        int pos, len, i, has_conflicts;
        struct rerere_io_mem io;
 -      int marker_size = ll_merge_marker_size(path);
 +      int marker_size = ll_merge_marker_size(istate, path);
  
        /*
         * Reproduce the conflicted merge in-core
         */
        len = strlen(path);
 -      pos = cache_name_pos(path, len);
 +      pos = index_name_pos(istate, path, len);
        if (0 <= pos)
                return -1;
        pos = -pos - 1;
  
 -      while (pos < active_nr) {
 +      while (pos < istate->cache_nr) {
                enum object_type type;
                unsigned long size;
  
 -              ce = active_cache[pos++];
 +              ce = istate->cache[pos++];
                if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
                        break;
                i = ce_stage(ce) - 1;
         */
        ll_merge(&result, path, &mmfile[0], NULL,
                 &mmfile[1], "ours",
 -               &mmfile[2], "theirs", NULL);
 +               &mmfile[2], "theirs",
 +               istate, NULL);
        for (i = 0; i < 3; i++)
                free(mmfile[i].ptr);
  
         * Grab the conflict ID and optionally write the original
         * contents with conflict markers out.
         */
-       has_conflicts = handle_path(sha1, (struct rerere_io *)&io, marker_size);
+       has_conflicts = handle_path(hash, (struct rerere_io *)&io, marker_size);
        strbuf_release(&io.input);
        if (io.io.output)
                fclose(io.io.output);
        return has_conflicts;
  }
  
 -static int rerere_forget_one_path(const char *path, struct string_list *rr)
 +static int rerere_forget_one_path(struct index_state *istate,
 +                                const char *path,
 +                                struct string_list *rr)
  {
        const char *filename;
        struct rerere_id *id;
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_RAWSZ];
        int ret;
        struct string_list_item *item;
  
         * Recreate the original conflict from the stages in the
         * index and compute the conflict ID
         */
-       ret = handle_cache(istate, path, sha1, NULL);
 -      ret = handle_cache(path, hash, NULL);
++      ret = handle_cache(istate, path, hash, NULL);
        if (ret < 1)
                return error(_("could not parse conflict hunks in '%s'"), path);
  
        /* Nuke the recorded resolution for the conflict */
-       id = new_rerere_id(sha1);
+       id = new_rerere_id(hash);
  
        for (id->variant = 0;
             id->variant < id->collection->status_nr;
                if (!has_rerere_resolution(id))
                        continue;
  
-               handle_cache(istate, path, sha1, rerere_path(id, "thisimage"));
 -              handle_cache(path, hash, rerere_path(id, "thisimage"));
++              handle_cache(istate, path, hash, rerere_path(id, "thisimage"));
                if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
                        free(cur.ptr);
                        error(_("failed to update conflicted state in '%s'"), path);
                        goto fail_exit;
                }
 -              cleanly_resolved = !try_merge(id, path, &cur, &result);
 +              cleanly_resolved = !try_merge(istate, id, path, &cur, &result);
                free(result.ptr);
                free(cur.ptr);
                if (cleanly_resolved)
         * conflict in the working tree, run us again to record
         * the postimage.
         */
-       handle_cache(istate, path, sha1, rerere_path(id, "preimage"));
 -      handle_cache(path, hash, rerere_path(id, "preimage"));
++      handle_cache(istate, path, hash, rerere_path(id, "preimage"));
        fprintf_ln(stderr, _("Updated preimage for '%s'"), path);
  
        /*
@@@ -1098,13 -1090,13 +1101,13 @@@ fail_exit
        return -1;
  }
  
 -int rerere_forget(struct pathspec *pathspec)
 +int rerere_forget(struct repository *r, struct pathspec *pathspec)
  {
        int i, fd;
        struct string_list conflict = STRING_LIST_INIT_DUP;
        struct string_list merge_rr = STRING_LIST_INIT_DUP;
  
 -      if (read_cache() < 0)
 +      if (read_index(r->index) < 0)
                return error(_("index file corrupt"));
  
        fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
         * recover the original conflicted state and then
         * find the conflicted paths.
         */
 -      unmerge_cache(pathspec);
 -      find_conflict(&conflict);
 +      unmerge_index(r->index, pathspec);
 +      find_conflict(r, &conflict);
        for (i = 0; i < conflict.nr; i++) {
                struct string_list_item *it = &conflict.items[i];
 -              if (!match_pathspec(&the_index, pathspec, it->string,
 +              if (!match_pathspec(r->index, pathspec, it->string,
                                    strlen(it->string), 0, NULL, 0))
                        continue;
 -              rerere_forget_one_path(it->string, &merge_rr);
 +              rerere_forget_one_path(r->index, it->string, &merge_rr);
        }
        return write_rr(&merge_rr, fd);
  }
diff --combined transport.c
index f4ffbd96cb65fdbac3c74d3d60c253bc3a8884f6,44b9ddf670531d8f27f8aaca9bee8ebadc2df6e7..01ce11a32575fc8ca889ba99f610983ef150688d
@@@ -252,18 -252,8 +252,18 @@@ static int connect_setup(struct transpo
        return 0;
  }
  
 -static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
 -                                      const struct argv_array *ref_prefixes)
 +/*
 + * Obtains the protocol version from the transport and writes it to
 + * transport->data->version, first connecting if not already connected.
 + *
 + * If the protocol version is one that allows skipping the listing of remote
 + * refs, and must_list_refs is 0, the listing of remote refs is skipped and
 + * this function returns NULL. Otherwise, this function returns the list of
 + * remote refs.
 + */
 +static struct ref *handshake(struct transport *transport, int for_push,
 +                           const struct argv_array *ref_prefixes,
 +                           int must_list_refs)
  {
        struct git_transport_data *data = transport->data;
        struct ref *refs = NULL;
        data->version = discover_version(&reader);
        switch (data->version) {
        case protocol_v2:
 -              get_remote_refs(data->fd[1], &reader, &refs, for_push,
 -                              ref_prefixes, transport->server_options);
 +              if (must_list_refs)
 +                      get_remote_refs(data->fd[1], &reader, &refs, for_push,
 +                                      ref_prefixes,
 +                                      transport->server_options);
                break;
        case protocol_v1:
        case protocol_v0:
        }
        data->got_remote_heads = 1;
  
 +      if (reader.line_peeked)
 +              BUG("buffer must be empty at the end of handshake()");
 +
        return refs;
  }
  
 +static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
 +                                      const struct argv_array *ref_prefixes)
 +{
 +      return handshake(transport, for_push, ref_prefixes, 1);
 +}
 +
  static int fetch_refs_via_pack(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
  {
        args.server_options = transport->server_options;
        args.negotiation_tips = data->options.negotiation_tips;
  
 -      if (!data->got_remote_heads)
 -              refs_tmp = get_refs_via_connect(transport, 0, NULL);
 +      if (!data->got_remote_heads) {
 +              int i;
 +              int must_list_refs = 0;
 +              for (i = 0; i < nr_heads; i++) {
 +                      if (!to_fetch[i]->exact_oid) {
 +                              must_list_refs = 1;
 +                              break;
 +                      }
 +              }
 +              refs_tmp = handshake(transport, 0, NULL, must_list_refs);
 +      }
  
        switch (data->version) {
        case protocol_v2:
@@@ -733,7 -703,6 +733,7 @@@ static int disconnect_git(struct transp
  }
  
  static struct transport_vtable taken_over_vtable = {
 +      1,
        NULL,
        get_refs_via_connect,
        fetch_refs_via_pack,
@@@ -883,7 -852,6 +883,7 @@@ void transport_check_allowed(const cha
  }
  
  static struct transport_vtable bundle_vtable = {
 +      0,
        NULL,
        get_refs_from_bundle,
        fetch_refs_from_bundle,
  };
  
  static struct transport_vtable builtin_smart_vtable = {
 +      1,
        NULL,
        get_refs_via_connect,
        fetch_refs_via_pack,
@@@ -1172,8 -1139,7 +1172,8 @@@ int transport_push(struct transport *tr
                                        oid_array_append(&commits,
                                                          &ref->new_oid);
  
 -                      if (!push_unpushed_submodules(&commits,
 +                      if (!push_unpushed_submodules(&the_index,
 +                                                    &commits,
                                                      transport->remote,
                                                      rs,
                                                      transport->push_options,
                                        oid_array_append(&commits,
                                                          &ref->new_oid);
  
 -                      if (find_unpushed_submodules(&commits, transport->remote->name,
 -                                              &needs_pushing)) {
 +                      if (find_unpushed_submodules(&the_index,
 +                                                   &commits,
 +                                                   transport->remote->name,
 +                                                   &needs_pushing)) {
                                oid_array_clear(&commits);
                                die_with_unpushed_submodules(&needs_pushing);
                        }
@@@ -1260,15 -1224,6 +1260,15 @@@ int transport_fetch_refs(struct transpo
        struct ref **heads = NULL;
        struct ref *rm;
  
 +      if (!transport->vtable->fetch_without_list)
 +              /*
 +               * Some transports (e.g. the built-in bundle transport and the
 +               * transport helper interface) do not work when fetching is
 +               * done immediately after transport creation. List the remote
 +               * refs anyway (if not already listed) as a workaround.
 +               */
 +              transport_get_remote_refs(transport, NULL);
 +
        for (rm = refs; rm; rm = rm->next) {
                nr_refs++;
                if (rm->peer_ref &&
@@@ -1370,33 -1325,6 +1370,33 @@@ literal_copy
        return xstrdup(url);
  }
  
 +static void fill_alternate_refs_command(struct child_process *cmd,
 +                                      const char *repo_path)
 +{
 +      const char *value;
 +
 +      if (!git_config_get_value("core.alternateRefsCommand", &value)) {
 +              cmd->use_shell = 1;
 +
 +              argv_array_push(&cmd->args, value);
 +              argv_array_push(&cmd->args, repo_path);
 +      } else {
 +              cmd->git_cmd = 1;
 +
 +              argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
 +              argv_array_push(&cmd->args, "for-each-ref");
 +              argv_array_push(&cmd->args, "--format=%(objectname)");
 +
 +              if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
 +                      argv_array_push(&cmd->args, "--");
 +                      argv_array_split(&cmd->args, value);
 +              }
 +      }
 +
 +      cmd->env = local_repo_env;
 +      cmd->out = -1;
 +}
 +
  static void read_alternate_refs(const char *path,
                                alternate_ref_fn *cb,
                                void *data)
        struct strbuf line = STRBUF_INIT;
        FILE *fh;
  
 -      cmd.git_cmd = 1;
 -      argv_array_pushf(&cmd.args, "--git-dir=%s", path);
 -      argv_array_push(&cmd.args, "for-each-ref");
 -      argv_array_push(&cmd.args, "--format=%(objectname) %(refname)");
 -      cmd.env = local_repo_env;
 -      cmd.out = -1;
 +      fill_alternate_refs_command(&cmd, path);
  
        if (start_command(&cmd))
                return;
        fh = xfdopen(cmd.out, "r");
        while (strbuf_getline_lf(&line, fh) != EOF) {
                struct object_id oid;
+               const char *p;
  
-               if (get_oid_hex(line.buf, &oid) ||
-                   line.buf[GIT_SHA1_HEXSZ]) {
 -              if (parse_oid_hex(line.buf, &oid, &p) ||
 -                  *p != ' ') {
++              if (parse_oid_hex(line.buf, &oid, &p) || *p) {
                        warning(_("invalid line while parsing alternate refs: %s"),
                                line.buf);
                        break;
                }
  
 -              cb(p + 1, &oid, data);
 +              cb(&oid, data);
        }
  
        fclose(fh);
diff --combined upload-pack.c
index 540778d1ddb85b494a92bce75993c4d13d47ef82,1aae5dd8288f4b5afc142998a007ecee68b74db6..5dc317c3b437952963f974b0faaaffdb0176fbca
@@@ -24,7 -24,6 +24,7 @@@
  #include "quote.h"
  #include "upload-pack.h"
  #include "serve.h"
 +#include "commit-graph.h"
  #include "commit-reach.h"
  
  /* Remember to update object flag allocation in object.h */
@@@ -444,6 -443,7 +444,7 @@@ static int do_reachable_revlist(struct 
        struct object *o;
        char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */
        int i;
+       const unsigned hexsz = the_hash_algo->hexsz;
  
        cmd->argv = argv;
        cmd->git_cmd = 1;
                goto error;
  
        namebuf[0] = '^';
-       namebuf[GIT_SHA1_HEXSZ + 1] = '\n';
+       namebuf[hexsz + 1] = '\n';
        for (i = get_max_object_index(); 0 < i; ) {
                o = get_indexed_object(--i);
                if (!o)
                        o->flags &= ~TMP_MARK;
                if (!is_our_ref(o))
                        continue;
-               memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
-               if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 2) < 0)
+               memcpy(namebuf + 1, oid_to_hex(&o->oid), hexsz);
+               if (write_in_full(cmd->in, namebuf, hexsz + 2) < 0)
                        goto error;
        }
-       namebuf[GIT_SHA1_HEXSZ] = '\n';
+       namebuf[hexsz] = '\n';
        for (i = 0; i < src->nr; i++) {
                o = src->objects[i].item;
                if (is_our_ref(o)) {
                }
                if (reachable && o->type == OBJ_COMMIT)
                        o->flags |= TMP_MARK;
-               memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
-               if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 1) < 0)
+               memcpy(namebuf, oid_to_hex(&o->oid), hexsz);
+               if (write_in_full(cmd->in, namebuf, hexsz + 1) < 0)
                        goto error;
        }
        close(cmd->in);
@@@ -693,7 -693,6 +694,7 @@@ static void deepen_by_rev_list(int ac, 
  {
        struct commit_list *result;
  
 +      close_commit_graph(the_repository);
        result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
        send_shallow(result);
        free_commit_list(result);