Merge branch 'ot/mru-on-list'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Feb 2018 21:39:05 +0000 (13:39 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Feb 2018 21:39:05 +0000 (13:39 -0800)
The first step to getting rid of mru API and using the
doubly-linked list API directly instead.

* ot/mru-on-list:
mru: use double-linked list from list.h

1  2 
builtin/pack-objects.c
packfile.c
diff --combined builtin/pack-objects.c
index 6c71552cdf994c4e65cb5495d133494371529f80,ba812349e0aab35c2a009fe3c7da407ec8b7f48c..0c3d03de487e1645a11d379dc5966cd9b2be81dd
@@@ -15,8 -15,6 +15,8 @@@
  #include "diff.h"
  #include "revision.h"
  #include "list-objects.h"
 +#include "list-objects-filter.h"
 +#include "list-objects-filter-options.h"
  #include "pack-objects.h"
  #include "progress.h"
  #include "refs.h"
@@@ -75,24 -73,12 +75,24 @@@ static int use_bitmap_index = -1
  static int write_bitmap_index;
  static uint16_t write_bitmap_options;
  
 +static int exclude_promisor_objects;
 +
  static unsigned long delta_cache_size = 0;
  static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
  static unsigned long cache_max_small_delta_size = 1000;
  
  static unsigned long window_memory_limit = 0;
  
 +static struct list_objects_filter_options filter_options;
 +
 +enum missing_action {
 +      MA_ERROR = 0,      /* fail if any missing objects are encountered */
 +      MA_ALLOW_ANY,      /* silently allow ALL missing objects */
 +      MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
 +};
 +static enum missing_action arg_missing_action;
 +static show_object_fn fn_show_object;
 +
  /*
   * stats
   */
@@@ -165,7 -151,7 +165,7 @@@ static unsigned long do_compress(void *
  }
  
  static unsigned long write_large_blob_data(struct git_istream *st, struct sha1file *f,
 -                                         const unsigned char *sha1)
 +                                         const struct object_id *oid)
  {
        git_zstream stream;
        unsigned char ibuf[1024 * 16];
                int zret = Z_OK;
                readlen = read_istream(st, ibuf, sizeof(ibuf));
                if (readlen == -1)
 -                      die(_("unable to read %s"), sha1_to_hex(sha1));
 +                      die(_("unable to read %s"), oid_to_hex(oid));
  
                stream.next_in = ibuf;
                stream.avail_in = readlen;
@@@ -353,7 -339,7 +353,7 @@@ static unsigned long write_no_reuse_obj
                sha1write(f, header, hdrlen);
        }
        if (st) {
 -              datalen = write_large_blob_data(st, f, entry->idx.oid.hash);
 +              datalen = write_large_blob_data(st, f, &entry->idx.oid);
                close_istream(st);
        } else {
                sha1write(f, buf, datalen);
@@@ -571,13 -557,13 +571,13 @@@ static enum write_one_status write_one(
  static int mark_tagged(const char *path, const struct object_id *oid, int flag,
                       void *cb_data)
  {
 -      unsigned char peeled[20];
 +      struct object_id peeled;
        struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL);
  
        if (entry)
                entry->tagged = 1;
 -      if (!peel_ref(path, peeled)) {
 -              entry = packlist_find(&to_pack, peeled, NULL);
 +      if (!peel_ref(path, &peeled)) {
 +              entry = packlist_find(&to_pack, peeled.hash, NULL);
                if (entry)
                        entry->tagged = 1;
        }
@@@ -806,7 -792,7 +806,7 @@@ static void write_pack_file(void
        write_order = compute_write_order();
  
        do {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                char *pack_tmp_name = NULL;
  
                if (pack_to_stdout)
                 * If so, rewrite it like in fast-import
                 */
                if (pack_to_stdout) {
 -                      sha1close(f, sha1, CSUM_CLOSE);
 +                      sha1close(f, oid.hash, CSUM_CLOSE);
                } else if (nr_written == nr_remaining) {
 -                      sha1close(f, sha1, CSUM_FSYNC);
 +                      sha1close(f, oid.hash, CSUM_FSYNC);
                } else {
 -                      int fd = sha1close(f, sha1, 0);
 -                      fixup_pack_header_footer(fd, sha1, pack_tmp_name,
 -                                               nr_written, sha1, offset);
 +                      int fd = sha1close(f, oid.hash, 0);
 +                      fixup_pack_header_footer(fd, oid.hash, pack_tmp_name,
 +                                               nr_written, oid.hash, offset);
                        close(fd);
                        if (write_bitmap_index) {
                                warning(_(no_split_warning));
                        strbuf_addf(&tmpname, "%s-", base_name);
  
                        if (write_bitmap_index) {
 -                              bitmap_writer_set_checksum(sha1);
 +                              bitmap_writer_set_checksum(oid.hash);
                                bitmap_writer_build_type_index(written_list, nr_written);
                        }
  
                        finish_tmp_packfile(&tmpname, pack_tmp_name,
                                            written_list, nr_written,
 -                                          &pack_idx_opts, sha1);
 +                                          &pack_idx_opts, oid.hash);
  
                        if (write_bitmap_index) {
 -                              strbuf_addf(&tmpname, "%s.bitmap", sha1_to_hex(sha1));
 +                              strbuf_addf(&tmpname, "%s.bitmap", oid_to_hex(&oid));
  
                                stop_progress(&progress_state);
  
  
                        strbuf_release(&tmpname);
                        free(pack_tmp_name);
 -                      puts(sha1_to_hex(sha1));
 +                      puts(oid_to_hex(&oid));
                }
  
                /* mark written objects as written to previous pack */
@@@ -942,13 -928,13 +942,13 @@@ static int no_try_delta(const char *pat
   * found the item, since that saves us from having to look it up again a
   * few lines later when we want to add the new entry.
   */
 -static int have_duplicate_entry(const unsigned char *sha1,
 +static int have_duplicate_entry(const struct object_id *oid,
                                int exclude,
                                uint32_t *index_pos)
  {
        struct object_entry *entry;
  
 -      entry = packlist_find(&to_pack, sha1, index_pos);
 +      entry = packlist_find(&to_pack, oid->hash, index_pos);
        if (!entry)
                return 0;
  
@@@ -1004,15 -990,15 +1004,15 @@@ static int want_found_object(int exclud
   * function finds if there is any pack that has the object and returns the pack
   * and its offset in these variables.
   */
 -static int want_object_in_pack(const unsigned char *sha1,
 +static int want_object_in_pack(const struct object_id *oid,
                               int exclude,
                               struct packed_git **found_pack,
                               off_t *found_offset)
  {
-       struct mru_entry *entry;
        int want;
+       struct list_head *pos;
  
 -      if (!exclude && local && has_loose_object_nonlocal(sha1))
 +      if (!exclude && local && has_loose_object_nonlocal(oid->hash))
                return 0;
  
        /*
                        return want;
        }
  
-       for (entry = packed_git_mru.head; entry; entry = entry->next) {
+       list_for_each(pos, &packed_git_mru.list) {
+               struct mru *entry = list_entry(pos, struct mru, list);
                struct packed_git *p = entry->item;
                off_t offset;
  
                if (p == *found_pack)
                        offset = *found_offset;
                else
 -                      offset = find_pack_entry_one(sha1, p);
 +                      offset = find_pack_entry_one(oid->hash, p);
  
                if (offset) {
                        if (!*found_pack) {
        return 1;
  }
  
 -static void create_object_entry(const unsigned char *sha1,
 +static void create_object_entry(const struct object_id *oid,
                                enum object_type type,
                                uint32_t hash,
                                int exclude,
  {
        struct object_entry *entry;
  
 -      entry = packlist_alloc(&to_pack, sha1, index_pos);
 +      entry = packlist_alloc(&to_pack, oid->hash, index_pos);
        entry->hash = hash;
        if (type)
                entry->type = type;
@@@ -1084,17 -1071,17 +1085,17 @@@ static const char no_closure_warning[] 
  "disabling bitmap writing, as some objects are not being packed"
  );
  
 -static int add_object_entry(const unsigned char *sha1, enum object_type type,
 +static int add_object_entry(const struct object_id *oid, enum object_type type,
                            const char *name, int exclude)
  {
        struct packed_git *found_pack = NULL;
        off_t found_offset = 0;
        uint32_t index_pos;
  
 -      if (have_duplicate_entry(sha1, exclude, &index_pos))
 +      if (have_duplicate_entry(oid, exclude, &index_pos))
                return 0;
  
 -      if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset)) {
 +      if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) {
                /* The pack is missing an object, so it will not have closure */
                if (write_bitmap_index) {
                        warning(_(no_closure_warning));
                return 0;
        }
  
 -      create_object_entry(sha1, type, pack_name_hash(name),
 +      create_object_entry(oid, type, pack_name_hash(name),
                            exclude, name && no_try_delta(name),
                            index_pos, found_pack, found_offset);
  
        return 1;
  }
  
 -static int add_object_entry_from_bitmap(const unsigned char *sha1,
 +static int add_object_entry_from_bitmap(const struct object_id *oid,
                                        enum object_type type,
                                        int flags, uint32_t name_hash,
                                        struct packed_git *pack, off_t offset)
  {
        uint32_t index_pos;
  
 -      if (have_duplicate_entry(sha1, 0, &index_pos))
 +      if (have_duplicate_entry(oid, 0, &index_pos))
                return 0;
  
 -      if (!want_object_in_pack(sha1, 0, &pack, &offset))
 +      if (!want_object_in_pack(oid, 0, &pack, &offset))
                return 0;
  
 -      create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
 +      create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset);
  
        display_progress(progress_state, nr_result);
        return 1;
  }
  
  struct pbase_tree_cache {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int ref;
        int temporary;
        void *tree_data;
  };
  
  static struct pbase_tree_cache *(pbase_tree_cache[256]);
 -static int pbase_tree_cache_ix(const unsigned char *sha1)
 +static int pbase_tree_cache_ix(const struct object_id *oid)
  {
 -      return sha1[0] % ARRAY_SIZE(pbase_tree_cache);
 +      return oid->hash[0] % ARRAY_SIZE(pbase_tree_cache);
  }
  static int pbase_tree_cache_ix_incr(int ix)
  {
@@@ -1158,14 -1145,14 +1159,14 @@@ static struct pbase_tree 
        struct pbase_tree_cache pcache;
  } *pbase_tree;
  
 -static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
 +static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid)
  {
        struct pbase_tree_cache *ent, *nent;
        void *data;
        unsigned long size;
        enum object_type type;
        int neigh;
 -      int my_ix = pbase_tree_cache_ix(sha1);
 +      int my_ix = pbase_tree_cache_ix(oid);
        int available_ix = -1;
  
        /* pbase-tree-cache acts as a limited hashtable.
         */
        for (neigh = 0; neigh < 8; neigh++) {
                ent = pbase_tree_cache[my_ix];
 -              if (ent && !hashcmp(ent->sha1, sha1)) {
 +              if (ent && !oidcmp(&ent->oid, oid)) {
                        ent->ref++;
                        return ent;
                }
        /* Did not find one.  Either we got a bogus request or
         * we need to read and perhaps cache.
         */
 -      data = read_sha1_file(sha1, &type, &size);
 +      data = read_sha1_file(oid->hash, &type, &size);
        if (!data)
                return NULL;
        if (type != OBJ_TREE) {
                free(ent->tree_data);
                nent = ent;
        }
 -      hashcpy(nent->sha1, sha1);
 +      oidcpy(&nent->oid, oid);
        nent->tree_data = data;
        nent->tree_size = size;
        nent->ref = 1;
@@@ -1261,7 -1248,7 +1262,7 @@@ static void add_pbase_object(struct tre
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
 -                      add_object_entry(entry.oid->hash,
 +                      add_object_entry(entry.oid,
                                         object_type(entry.mode),
                                         fullname, 1);
                        return;
                        const char *down = name+cmplen+1;
                        int downlen = name_cmp_len(down);
  
 -                      tree = pbase_tree_get(entry.oid->hash);
 +                      tree = pbase_tree_get(entry.oid);
                        if (!tree)
                                return;
                        init_tree_desc(&sub, tree->tree_data, tree->tree_size);
@@@ -1291,7 -1278,7 +1292,7 @@@ static int done_pbase_path_pos(unsigne
        int lo = 0;
        int hi = done_pbase_paths_num;
        while (lo < hi) {
 -              int mi = (hi + lo) / 2;
 +              int mi = lo + (hi - lo) / 2;
                if (done_pbase_paths[mi] == hash)
                        return mi;
                if (done_pbase_paths[mi] < hash)
@@@ -1331,7 -1318,7 +1332,7 @@@ static void add_preferred_base_object(c
        cmplen = name_cmp_len(name);
        for (it = pbase_tree; it; it = it->next) {
                if (cmplen == 0) {
 -                      add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1);
 +                      add_object_entry(&it->pcache.oid, OBJ_TREE, NULL, 1);
                }
                else {
                        struct tree_desc tree;
        }
  }
  
 -static void add_preferred_base(unsigned char *sha1)
 +static void add_preferred_base(struct object_id *oid)
  {
        struct pbase_tree *it;
        void *data;
        unsigned long size;
 -      unsigned char tree_sha1[20];
 +      struct object_id tree_oid;
  
        if (window <= num_preferred_base++)
                return;
  
 -      data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
 +      data = read_object_with_reference(oid->hash, tree_type, &size, tree_oid.hash);
        if (!data)
                return;
  
        for (it = pbase_tree; it; it = it->next) {
 -              if (!hashcmp(it->pcache.sha1, tree_sha1)) {
 +              if (!oidcmp(&it->pcache.oid, &tree_oid)) {
                        free(data);
                        return;
                }
        it->next = pbase_tree;
        pbase_tree = it;
  
 -      hashcpy(it->pcache.sha1, tree_sha1);
 +      oidcpy(&it->pcache.oid, &tree_oid);
        it->pcache.tree_data = data;
        it->pcache.tree_size = size;
  }
@@@ -2371,7 -2358,7 +2372,7 @@@ static void add_tag_chain(const struct 
                        die("unable to pack objects reachable from tag %s",
                            oid_to_hex(oid));
  
 -              add_object_entry(tag->object.oid.hash, OBJ_TAG, NULL, 0);
 +              add_object_entry(&tag->object.oid, OBJ_TAG, NULL, 0);
  
                if (tag->tagged->type != OBJ_TAG)
                        return;
@@@ -2385,7 -2372,7 +2386,7 @@@ static int add_ref_tag(const char *path
        struct object_id peeled;
  
        if (starts_with(path, "refs/tags/") && /* is a tag? */
 -          !peel_ref(path, peeled.hash)    && /* peelable? */
 +          !peel_ref(path, &peeled)    && /* peelable? */
            packlist_find(&to_pack, peeled.hash, NULL))      /* object packed? */
                add_tag_chain(oid);
        return 0;
@@@ -2519,9 -2506,8 +2520,9 @@@ static int git_pack_config(const char *
  
  static void read_object_list_from_stdin(void)
  {
 -      char line[40 + 1 + PATH_MAX + 2];
 -      unsigned char sha1[20];
 +      char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2];
 +      struct object_id oid;
 +      const char *p;
  
        for (;;) {
                if (!fgets(line, sizeof(line), stdin)) {
                        continue;
                }
                if (line[0] == '-') {
 -                      if (get_sha1_hex(line+1, sha1))
 -                              die("expected edge sha1, got garbage:\n %s",
 +                      if (get_oid_hex(line+1, &oid))
 +                              die("expected edge object ID, got garbage:\n %s",
                                    line);
 -                      add_preferred_base(sha1);
 +                      add_preferred_base(&oid);
                        continue;
                }
 -              if (get_sha1_hex(line, sha1))
 -                      die("expected sha1, got garbage:\n %s", line);
 +              if (parse_oid_hex(line, &oid, &p))
 +                      die("expected object ID, got garbage:\n %s", line);
  
 -              add_preferred_base_object(line+41);
 -              add_object_entry(sha1, 0, line+41, 0);
 +              add_preferred_base_object(p + 1);
 +              add_object_entry(&oid, 0, p + 1, 0);
        }
  }
  
  
  static void show_commit(struct commit *commit, void *data)
  {
 -      add_object_entry(commit->object.oid.hash, OBJ_COMMIT, NULL, 0);
 +      add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0);
        commit->object.flags |= OBJECT_ADDED;
  
        if (write_bitmap_index)
  static void show_object(struct object *obj, const char *name, void *data)
  {
        add_preferred_base_object(name);
 -      add_object_entry(obj->oid.hash, obj->type, name, 0);
 +      add_object_entry(&obj->oid, obj->type, name, 0);
        obj->flags |= OBJECT_ADDED;
  }
  
 +static void show_object__ma_allow_any(struct object *obj, const char *name, void *data)
 +{
 +      assert(arg_missing_action == MA_ALLOW_ANY);
 +
 +      /*
 +       * Quietly ignore ALL missing objects.  This avoids problems with
 +       * staging them now and getting an odd error later.
 +       */
 +      if (!has_object_file(&obj->oid))
 +              return;
 +
 +      show_object(obj, name, data);
 +}
 +
 +static void show_object__ma_allow_promisor(struct object *obj, const char *name, void *data)
 +{
 +      assert(arg_missing_action == MA_ALLOW_PROMISOR);
 +
 +      /*
 +       * Quietly ignore EXPECTED missing objects.  This avoids problems with
 +       * staging them now and getting an odd error later.
 +       */
 +      if (!has_object_file(&obj->oid) && is_promisor_object(&obj->oid))
 +              return;
 +
 +      show_object(obj, name, data);
 +}
 +
 +static int option_parse_missing_action(const struct option *opt,
 +                                     const char *arg, int unset)
 +{
 +      assert(arg);
 +      assert(!unset);
 +
 +      if (!strcmp(arg, "error")) {
 +              arg_missing_action = MA_ERROR;
 +              fn_show_object = show_object;
 +              return 0;
 +      }
 +
 +      if (!strcmp(arg, "allow-any")) {
 +              arg_missing_action = MA_ALLOW_ANY;
 +              fetch_if_missing = 0;
 +              fn_show_object = show_object__ma_allow_any;
 +              return 0;
 +      }
 +
 +      if (!strcmp(arg, "allow-promisor")) {
 +              arg_missing_action = MA_ALLOW_PROMISOR;
 +              fetch_if_missing = 0;
 +              fn_show_object = show_object__ma_allow_promisor;
 +              return 0;
 +      }
 +
 +      die(_("invalid value for --missing"));
 +      return 0;
 +}
 +
  static void show_edge(struct commit *commit)
  {
 -      add_preferred_base(commit->object.oid.hash);
 +      add_preferred_base(&commit->object.oid);
  }
  
  struct in_pack_object {
  };
  
  struct in_pack {
 -      int alloc;
 -      int nr;
 +      unsigned int alloc;
 +      unsigned int nr;
        struct in_pack_object *array;
  };
  
@@@ -2674,7 -2602,7 +2675,7 @@@ static void add_objects_in_unpacked_pac
        memset(&in_pack, 0, sizeof(in_pack));
  
        for (p = packed_git; p; p = p->next) {
 -              const unsigned char *sha1;
 +              struct object_id oid;
                struct object *o;
  
                if (!p->pack_local || p->pack_keep)
                           in_pack.alloc);
  
                for (i = 0; i < p->num_objects; i++) {
 -                      sha1 = nth_packed_object_sha1(p, i);
 -                      o = lookup_unknown_object(sha1);
 +                      nth_packed_object_oid(&oid, p, i);
 +                      o = lookup_unknown_object(oid.hash);
                        if (!(o->flags & OBJECT_ADDED))
                                mark_in_pack_object(o, p, &in_pack);
                        o->flags |= OBJECT_ADDED;
                QSORT(in_pack.array, in_pack.nr, ofscmp);
                for (i = 0; i < in_pack.nr; i++) {
                        struct object *o = in_pack.array[i].object;
 -                      add_object_entry(o->oid.hash, o->type, "", 0);
 +                      add_object_entry(&o->oid, o->type, "", 0);
                }
        }
        free(in_pack.array);
@@@ -2715,7 -2643,7 +2716,7 @@@ static int add_loose_object(const struc
                return 0;
        }
  
 -      add_object_entry(oid->hash, type, "", 0);
 +      add_object_entry(oid, type, "", 0);
        return 0;
  }
  
@@@ -2731,7 -2659,7 +2732,7 @@@ static void add_unreachable_loose_objec
                                      NULL, NULL, NULL);
  }
  
 -static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 +static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid)
  {
        static struct packed_git *last_found = (void *)1;
        struct packed_git *p;
  
        while (p) {
                if ((!p->pack_local || p->pack_keep) &&
 -                      find_pack_entry_one(sha1, p)) {
 +                      find_pack_entry_one(oid->hash, p)) {
                        last_found = p;
                        return 1;
                }
@@@ -2791,7 -2719,7 +2792,7 @@@ static void loosen_unused_packed_object
                for (i = 0; i < p->num_objects; i++) {
                        nth_packed_object_oid(&oid, p, i);
                        if (!packlist_find(&to_pack, oid.hash, NULL) &&
 -                          !has_sha1_pack_kept_or_nonlocal(oid.hash) &&
 +                          !has_sha1_pack_kept_or_nonlocal(&oid) &&
                            !loosened_object_can_be_discarded(&oid, p->mtime))
                                if (force_object_loose(oid.hash, p->mtime))
                                        die("unable to force loose object");
@@@ -2889,12 -2817,7 +2890,12 @@@ static void get_object_list(int ac, con
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(&revs, show_edge);
 -      traverse_commit_list(&revs, show_commit, show_object, NULL);
 +
 +      if (!fn_show_object)
 +              fn_show_object = show_object;
 +      traverse_commit_list_filtered(&filter_options, &revs,
 +                                    show_commit, fn_show_object, NULL,
 +                                    NULL);
  
        if (unpack_unreachable_expiration) {
                revs.ignore_missing_links = 1;
@@@ -3030,12 -2953,6 +3031,12 @@@ int cmd_pack_objects(int argc, const ch
                         N_("use a bitmap index if available to speed up counting objects")),
                OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index,
                         N_("write a bitmap index together with the pack index")),
 +              OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
 +              { OPTION_CALLBACK, 0, "missing", NULL, N_("action"),
 +                N_("handling for missing objects"), PARSE_OPT_NONEG,
 +                option_parse_missing_action },
 +              OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
 +                       N_("do not pack objects in promisor packfiles")),
                OPT_END(),
        };
  
                argv_array_push(&rp, "--unpacked");
        }
  
 +      if (exclude_promisor_objects) {
 +              use_internal_rev_list = 1;
 +              fetch_if_missing = 0;
 +              argv_array_push(&rp, "--exclude-promisor-objects");
 +      }
 +
        if (!reuse_object)
                reuse_delta = 0;
        if (pack_compression_level == -1)
        if (!rev_list_all || !rev_list_reflog || !rev_list_index)
                unpack_unreachable_expiration = 0;
  
 +      if (filter_options.choice) {
 +              if (!pack_to_stdout)
 +                      die("cannot use --filter without --stdout.");
 +              use_bitmap_index = 0;
 +      }
 +
        /*
         * "soft" reasons not to use bitmaps - for on-disk repack by default we want
         *
diff --combined packfile.c
index 234797cde78b0103f327000204c69a5fa99c0be3,502d915991beeb452296dc0c7789806856ed5a7c..f4dc4a242bdf8eb4e063adfdd56eb921c11ef5c0
@@@ -8,11 -8,6 +8,11 @@@
  #include "list.h"
  #include "streaming.h"
  #include "sha1-lookup.h"
 +#include "commit.h"
 +#include "object.h"
 +#include "tag.h"
 +#include "tree-walk.h"
 +#include "tree.h"
  
  char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@@ -45,7 -40,7 +45,7 @@@ static unsigned int pack_max_fds
  static size_t peak_pack_mapped;
  static size_t pack_mapped;
  struct packed_git *packed_git;
- struct mru packed_git_mru;
+ struct mru packed_git_mru = {{&packed_git_mru.list, &packed_git_mru.list}};
  
  #define SZ_FMT PRIuMAX
  static inline uintmax_t sz_fmt(size_t s) { return s; }
@@@ -447,7 -442,6 +447,7 @@@ static int open_packed_git_1(struct pac
        unsigned char sha1[20];
        unsigned char *idx_sha1;
        long fd_flag;
 +      ssize_t read_result;
  
        if (!p->index_data && open_pack_index(p))
                return error("packfile %s index unavailable", p->pack_name);
                return error("cannot set FD_CLOEXEC");
  
        /* Verify we recognize this pack file format. */
 -      if (read_in_full(p->pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
 +      read_result = read_in_full(p->pack_fd, &hdr, sizeof(hdr));
 +      if (read_result < 0)
 +              return error_errno("error reading from %s", p->pack_name);
 +      if (read_result != sizeof(hdr))
                return error("file %s is far too short to be a packfile", p->pack_name);
        if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
                return error("file %s is not a GIT packfile", p->pack_name);
                             p->num_objects);
        if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
                return error("end of packfile %s is unavailable", p->pack_name);
 -      if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
 +      read_result = read_in_full(p->pack_fd, sha1, sizeof(sha1));
 +      if (read_result < 0)
 +              return error_errno("error reading from %s", p->pack_name);
 +      if (read_result != sizeof(sha1))
                return error("packfile %s signature is unavailable", p->pack_name);
        idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
        if (hashcmp(sha1, idx_sha1))
@@@ -648,10 -636,10 +648,10 @@@ struct packed_git *add_packed_git(cons
                return NULL;
  
        /*
 -       * ".pack" is long enough to hold any suffix we're adding (and
 +       * ".promisor" is long enough to hold any suffix we're adding (and
         * the use xsnprintf double-checks that)
         */
 -      alloc = st_add3(path_len, strlen(".pack"), 1);
 +      alloc = st_add3(path_len, strlen(".promisor"), 1);
        p = alloc_packed_git(alloc);
        memcpy(p->pack_name, path, path_len);
  
        if (!access(p->pack_name, F_OK))
                p->pack_keep = 1;
  
 +      xsnprintf(p->pack_name + path_len, alloc - path_len, ".promisor");
 +      if (!access(p->pack_name, F_OK))
 +              p->pack_promisor = 1;
 +
        xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
        if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
                free(p);
@@@ -790,8 -774,7 +790,8 @@@ static void prepare_packed_git_one(cha
                if (ends_with(de->d_name, ".idx") ||
                    ends_with(de->d_name, ".pack") ||
                    ends_with(de->d_name, ".bitmap") ||
 -                  ends_with(de->d_name, ".keep"))
 +                  ends_with(de->d_name, ".keep") ||
 +                  ends_with(de->d_name, ".promisor"))
                        string_list_append(&garbage, path.buf);
                else
                        report_garbage(PACKDIR_FILE_GARBAGE, path.buf);
@@@ -1753,7 -1736,7 +1753,7 @@@ off_t find_pack_entry_one(const unsigne
                       sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
  
        while (lo < hi) {
 -              unsigned mi = (lo + hi) / 2;
 +              unsigned mi = lo + (hi - lo) / 2;
                int cmp = hashcmp(index + mi * stride, sha1);
  
                if (debug_lookup)
@@@ -1841,13 -1824,14 +1841,14 @@@ static int fill_pack_entry(const unsign
   */
  int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
  {
-       struct mru_entry *p;
+       struct list_head *pos;
  
        prepare_packed_git();
        if (!packed_git)
                return 0;
  
-       for (p = packed_git_mru.head; p; p = p->next) {
+       list_for_each(pos, &packed_git_mru.list) {
+               struct mru *p = list_entry(pos, struct mru, list);
                if (fill_pack_entry(sha1, e, p->item)) {
                        mru_mark(&packed_git_mru, p);
                        return 1;
@@@ -1899,9 -1883,6 +1900,9 @@@ int for_each_packed_object(each_packed_
        for (p = packed_git; p; p = p->next) {
                if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
                        continue;
 +              if ((flags & FOR_EACH_OBJECT_PROMISOR_ONLY) &&
 +                  !p->pack_promisor)
 +                      continue;
                if (open_pack_index(p)) {
                        pack_errors = 1;
                        continue;
        }
        return r ? r : pack_errors;
  }
 +
 +static int add_promisor_object(const struct object_id *oid,
 +                             struct packed_git *pack,
 +                             uint32_t pos,
 +                             void *set_)
 +{
 +      struct oidset *set = set_;
 +      struct object *obj = parse_object(oid);
 +      if (!obj)
 +              return 1;
 +
 +      oidset_insert(set, oid);
 +
 +      /*
 +       * If this is a tree, commit, or tag, the objects it refers
 +       * to are also promisor objects. (Blobs refer to no objects.)
 +       */
 +      if (obj->type == OBJ_TREE) {
 +              struct tree *tree = (struct tree *)obj;
 +              struct tree_desc desc;
 +              struct name_entry entry;
 +              if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
 +                      /*
 +                       * Error messages are given when packs are
 +                       * verified, so do not print any here.
 +                       */
 +                      return 0;
 +              while (tree_entry_gently(&desc, &entry))
 +                      oidset_insert(set, entry.oid);
 +      } else if (obj->type == OBJ_COMMIT) {
 +              struct commit *commit = (struct commit *) obj;
 +              struct commit_list *parents = commit->parents;
 +
 +              oidset_insert(set, &commit->tree->object.oid);
 +              for (; parents; parents = parents->next)
 +                      oidset_insert(set, &parents->item->object.oid);
 +      } else if (obj->type == OBJ_TAG) {
 +              struct tag *tag = (struct tag *) obj;
 +              oidset_insert(set, &tag->tagged->oid);
 +      }
 +      return 0;
 +}
 +
 +int is_promisor_object(const struct object_id *oid)
 +{
 +      static struct oidset promisor_objects;
 +      static int promisor_objects_prepared;
 +
 +      if (!promisor_objects_prepared) {
 +              if (repository_format_partial_clone) {
 +                      for_each_packed_object(add_promisor_object,
 +                                             &promisor_objects,
 +                                             FOR_EACH_OBJECT_PROMISOR_ONLY);
 +              }
 +              promisor_objects_prepared = 1;
 +      }
 +      return oidset_contains(&promisor_objects, oid);
 +}