Merge branch 'js/pack-objects-mutex-init-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 30 Oct 2018 06:43:43 +0000 (15:43 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 30 Oct 2018 06:43:43 +0000 (15:43 +0900)
A mutex used in "git pack-objects" were not correctly initialized
and this caused "git repack" to dump core on Windows.

* js/pack-objects-mutex-init-fix:
pack-objects (mingw): initialize `packing_data` mutex in the correct spot
pack-objects (mingw): demonstrate a segmentation fault with large deltas
pack-objects: fix typo 'detla' -> 'delta'

1  2 
builtin/pack-objects.c
pack-objects.c
pack-objects.h
diff --combined builtin/pack-objects.c
index b059b86aee68ceb63e06bc0d8e9ec680686e6bd4,29d48f38671116594494ca8904193936ed7ea77b..e50c6cd1ff25ce4d65e0ebd854d052c3d1160a2a
@@@ -24,7 -24,6 +24,7 @@@
  #include "streaming.h"
  #include "thread-utils.h"
  #include "pack-bitmap.h"
 +#include "delta-islands.h"
  #include "reachable.h"
  #include "sha1-array.h"
  #include "argv-array.h"
@@@ -32,7 -31,6 +32,7 @@@
  #include "packfile.h"
  #include "object-store.h"
  #include "dir.h"
 +#include "midx.h"
  
  #define IN_PACK(obj) oe_in_pack(&to_pack, obj)
  #define SIZE(obj) oe_size(&to_pack, obj)
@@@ -42,7 -40,6 +42,7 @@@
  #define DELTA_CHILD(obj) oe_delta_child(&to_pack, obj)
  #define DELTA_SIBLING(obj) oe_delta_sibling(&to_pack, obj)
  #define SET_DELTA(obj, val) oe_set_delta(&to_pack, obj, val)
 +#define SET_DELTA_EXT(obj, oid) oe_set_delta_ext(&to_pack, obj, oid)
  #define SET_DELTA_SIZE(obj, val) oe_set_delta_size(&to_pack, obj, val)
  #define SET_DELTA_CHILD(obj, val) oe_set_delta_child(&to_pack, obj, val)
  #define SET_DELTA_SIBLING(obj, val) oe_set_delta_sibling(&to_pack, obj, val)
@@@ -62,8 -59,6 +62,8 @@@ static struct packing_data to_pack
  
  static struct pack_idx_entry **written_list;
  static uint32_t nr_result, nr_written, nr_seen;
 +static struct bitmap_index *bitmap_git;
 +static uint32_t write_layer;
  
  static int non_empty;
  static int reuse_delta = 1, reuse_object = 1;
@@@ -84,7 -79,6 +84,7 @@@ static unsigned long pack_size_limit
  static int depth = 50;
  static int delta_search_threads;
  static int pack_to_stdout;
 +static int thin;
  static int num_preferred_base;
  static struct progress *progress_state;
  
@@@ -99,8 -93,6 +99,8 @@@ static uint16_t write_bitmap_options
  
  static int exclude_promisor_objects;
  
 +static int use_delta_islands;
 +
  static unsigned long delta_cache_size = 0;
  static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
  static unsigned long cache_max_small_delta_size = 1000;
@@@ -620,7 -612,7 +620,7 @@@ static inline void add_to_write_order(s
                               unsigned int *endp,
                               struct object_entry *e)
  {
 -      if (e->filled)
 +      if (e->filled || oe_layer(&to_pack, e) != write_layer)
                return;
        wo[(*endp)++] = e;
        e->filled = 1;
@@@ -680,15 -672,48 +680,15 @@@ static void add_family_to_write_order(s
        add_descendants_to_write_order(wo, endp, root);
  }
  
 -static struct object_entry **compute_write_order(void)
 +static void compute_layer_order(struct object_entry **wo, unsigned int *wo_end)
  {
 -      unsigned int i, wo_end, last_untagged;
 -
 -      struct object_entry **wo;
 +      unsigned int i, last_untagged;
        struct object_entry *objects = to_pack.objects;
  
        for (i = 0; i < to_pack.nr_objects; i++) {
 -              objects[i].tagged = 0;
 -              objects[i].filled = 0;
 -              SET_DELTA_CHILD(&objects[i], NULL);
 -              SET_DELTA_SIBLING(&objects[i], NULL);
 -      }
 -
 -      /*
 -       * Fully connect delta_child/delta_sibling network.
 -       * Make sure delta_sibling is sorted in the original
 -       * recency order.
 -       */
 -      for (i = to_pack.nr_objects; i > 0;) {
 -              struct object_entry *e = &objects[--i];
 -              if (!DELTA(e))
 -                      continue;
 -              /* Mark me as the first child */
 -              e->delta_sibling_idx = DELTA(e)->delta_child_idx;
 -              SET_DELTA_CHILD(DELTA(e), e);
 -      }
 -
 -      /*
 -       * Mark objects that are at the tip of tags.
 -       */
 -      for_each_tag_ref(mark_tagged, NULL);
 -
 -      /*
 -       * Give the objects in the original recency order until
 -       * we see a tagged tip.
 -       */
 -      ALLOC_ARRAY(wo, to_pack.nr_objects);
 -      for (i = wo_end = 0; i < to_pack.nr_objects; i++) {
                if (objects[i].tagged)
                        break;
 -              add_to_write_order(wo, &wo_end, &objects[i]);
 +              add_to_write_order(wo, wo_end, &objects[i]);
        }
        last_untagged = i;
  
         */
        for (; i < to_pack.nr_objects; i++) {
                if (objects[i].tagged)
 -                      add_to_write_order(wo, &wo_end, &objects[i]);
 +                      add_to_write_order(wo, wo_end, &objects[i]);
        }
  
        /*
                if (oe_type(&objects[i]) != OBJ_COMMIT &&
                    oe_type(&objects[i]) != OBJ_TAG)
                        continue;
 -              add_to_write_order(wo, &wo_end, &objects[i]);
 +              add_to_write_order(wo, wo_end, &objects[i]);
        }
  
        /*
        for (i = last_untagged; i < to_pack.nr_objects; i++) {
                if (oe_type(&objects[i]) != OBJ_TREE)
                        continue;
 -              add_to_write_order(wo, &wo_end, &objects[i]);
 +              add_to_write_order(wo, wo_end, &objects[i]);
        }
  
        /*
         * Finally all the rest in really tight order
         */
        for (i = last_untagged; i < to_pack.nr_objects; i++) {
 -              if (!objects[i].filled)
 -                      add_family_to_write_order(wo, &wo_end, &objects[i]);
 +              if (!objects[i].filled && oe_layer(&to_pack, &objects[i]) == write_layer)
 +                      add_family_to_write_order(wo, wo_end, &objects[i]);
 +      }
 +}
 +
 +static struct object_entry **compute_write_order(void)
 +{
 +      uint32_t max_layers = 1;
 +      unsigned int i, wo_end;
 +
 +      struct object_entry **wo;
 +      struct object_entry *objects = to_pack.objects;
 +
 +      for (i = 0; i < to_pack.nr_objects; i++) {
 +              objects[i].tagged = 0;
 +              objects[i].filled = 0;
 +              SET_DELTA_CHILD(&objects[i], NULL);
 +              SET_DELTA_SIBLING(&objects[i], NULL);
 +      }
 +
 +      /*
 +       * Fully connect delta_child/delta_sibling network.
 +       * Make sure delta_sibling is sorted in the original
 +       * recency order.
 +       */
 +      for (i = to_pack.nr_objects; i > 0;) {
 +              struct object_entry *e = &objects[--i];
 +              if (!DELTA(e))
 +                      continue;
 +              /* Mark me as the first child */
 +              e->delta_sibling_idx = DELTA(e)->delta_child_idx;
 +              SET_DELTA_CHILD(DELTA(e), e);
        }
  
 +      /*
 +       * Mark objects that are at the tip of tags.
 +       */
 +      for_each_tag_ref(mark_tagged, NULL);
 +
 +      if (use_delta_islands)
 +              max_layers = compute_pack_layers(&to_pack);
 +
 +      ALLOC_ARRAY(wo, to_pack.nr_objects);
 +      wo_end = 0;
 +
 +      for (; write_layer < max_layers; ++write_layer)
 +              compute_layer_order(wo, &wo_end);
 +
        if (wo_end != to_pack.nr_objects)
                die(_("ordered %u objects, expected %"PRIu32),
                    wo_end, to_pack.nr_objects);
@@@ -970,7 -951,8 +970,7 @@@ static int no_try_delta(const char *pat
  
        if (!check)
                check = attr_check_initl("delta", NULL);
 -      if (git_check_attr(&the_index, path, check))
 -              return 0;
 +      git_check_attr(&the_index, path, check);
        if (ATTR_FALSE(check->items[0].value))
                return 1;
        return 0;
@@@ -1058,7 -1040,6 +1058,7 @@@ static int want_object_in_pack(const st
  {
        int want;
        struct list_head *pos;
 +      struct multi_pack_index *m;
  
        if (!exclude && local && has_loose_object_nonlocal(oid))
                return 0;
                if (want != -1)
                        return want;
        }
 +
 +      for (m = get_multi_pack_index(the_repository); m; m = m->next) {
 +              struct pack_entry e;
 +              if (fill_midx_entry(oid, &e, m)) {
 +                      struct packed_git *p = e.p;
 +                      off_t offset;
 +
 +                      if (p == *found_pack)
 +                              offset = *found_offset;
 +                      else
 +                              offset = find_pack_entry_one(oid->hash, p);
 +
 +                      if (offset) {
 +                              if (!*found_pack) {
 +                                      if (!is_pack_valid(p))
 +                                              continue;
 +                                      *found_offset = offset;
 +                                      *found_pack = p;
 +                              }
 +                              want = want_found_object(exclude, p);
 +                              if (want != -1)
 +                                      return want;
 +                      }
 +              }
 +      }
 +
        list_for_each(pos, get_packed_git_mru(the_repository)) {
                struct packed_git *p = list_entry(pos, struct packed_git, mru);
                off_t offset;
@@@ -1247,7 -1202,7 +1247,7 @@@ static struct pbase_tree_cache *pbase_t
         */
        for (neigh = 0; neigh < 8; neigh++) {
                ent = pbase_tree_cache[my_ix];
 -              if (ent && !oidcmp(&ent->oid, oid)) {
 +              if (ent && oideq(&ent->oid, oid)) {
                        ent->ref++;
                        return ent;
                }
@@@ -1429,7 -1384,7 +1429,7 @@@ static void add_preferred_base(struct o
                return;
  
        for (it = pbase_tree; it; it = it->next) {
 -              if (!oidcmp(&it->pcache.oid, &tree_oid)) {
 +              if (oideq(&it->pcache.oid, &tree_oid)) {
                        free(data);
                        return;
                }
@@@ -1469,57 -1424,6 +1469,57 @@@ static void cleanup_preferred_base(void
        done_pbase_paths_num = done_pbase_paths_alloc = 0;
  }
  
 +/*
 + * Return 1 iff the object specified by "delta" can be sent
 + * literally as a delta against the base in "base_sha1". If
 + * so, then *base_out will point to the entry in our packing
 + * list, or NULL if we must use the external-base list.
 + *
 + * Depth value does not matter - find_deltas() will
 + * never consider reused delta as the base object to
 + * deltify other objects against, in order to avoid
 + * circular deltas.
 + */
 +static int can_reuse_delta(const unsigned char *base_sha1,
 +                         struct object_entry *delta,
 +                         struct object_entry **base_out)
 +{
 +      struct object_entry *base;
 +
 +      if (!base_sha1)
 +              return 0;
 +
 +      /*
 +       * First see if we're already sending the base (or it's explicitly in
 +       * our "excluded" list).
 +       */
 +      base = packlist_find(&to_pack, base_sha1, NULL);
 +      if (base) {
 +              if (!in_same_island(&delta->idx.oid, &base->idx.oid))
 +                      return 0;
 +              *base_out = base;
 +              return 1;
 +      }
 +
 +      /*
 +       * Otherwise, reachability bitmaps may tell us if the receiver has it,
 +       * even if it was buried too deep in history to make it into the
 +       * packing list.
 +       */
 +      if (thin && bitmap_has_sha1_in_uninteresting(bitmap_git, base_sha1)) {
 +              if (use_delta_islands) {
 +                      struct object_id base_oid;
 +                      hashcpy(base_oid.hash, base_sha1);
 +                      if (!in_same_island(&delta->idx.oid, &base_oid))
 +                              return 0;
 +              }
 +              *base_out = NULL;
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
  static void check_object(struct object_entry *entry)
  {
        unsigned long canonical_size;
                        break;
                }
  
 -              if (base_ref && (base_entry = packlist_find(&to_pack, base_ref, NULL))) {
 -                      /*
 -                       * If base_ref was set above that means we wish to
 -                       * reuse delta data, and we even found that base
 -                       * in the list of objects we want to pack. Goodie!
 -                       *
 -                       * Depth value does not matter - find_deltas() will
 -                       * never consider reused delta as the base object to
 -                       * deltify other objects against, in order to avoid
 -                       * circular deltas.
 -                       */
 +              if (can_reuse_delta(base_ref, entry, &base_entry)) {
                        oe_set_type(entry, entry->in_pack_type);
                        SET_SIZE(entry, in_pack_size); /* delta size */
 -                      SET_DELTA(entry, base_entry);
                        SET_DELTA_SIZE(entry, in_pack_size);
 -                      entry->delta_sibling_idx = base_entry->delta_child_idx;
 -                      SET_DELTA_CHILD(base_entry, entry);
 +
 +                      if (base_entry) {
 +                              SET_DELTA(entry, base_entry);
 +                              entry->delta_sibling_idx = base_entry->delta_child_idx;
 +                              SET_DELTA_CHILD(base_entry, entry);
 +                      } else {
 +                              SET_DELTA_EXT(entry, base_ref);
 +                      }
 +
                        unuse_pack(&w_curs);
                        return;
                }
@@@ -1918,11 -1826,6 +1918,11 @@@ static int type_size_sort(const void *_
                return -1;
        if (a->preferred_base < b->preferred_base)
                return 1;
 +      if (use_delta_islands) {
 +              int island_cmp = island_delta_cmp(&a->idx.oid, &b->idx.oid);
 +              if (island_cmp)
 +                      return island_cmp;
 +      }
        if (a_size > b_size)
                return -1;
        if (a_size < b_size)
@@@ -2083,9 -1986,6 +2083,9 @@@ static int try_delta(struct unpacked *t
        if (trg_size < src_size / 32)
                return 0;
  
 +      if (!in_same_island(&trg->entry->idx.oid, &src->entry->idx.oid))
 +              return 0;
 +
        /* Load data if not already done */
        if (!trg->data) {
                read_lock();
@@@ -2399,7 -2299,6 +2399,6 @@@ static void init_threaded_search(void
        pthread_mutex_init(&cache_mutex, NULL);
        pthread_mutex_init(&progress_mutex, NULL);
        pthread_cond_init(&progress_cond, NULL);
-       pthread_mutex_init(&to_pack.lock, NULL);
        old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
  }
  
@@@ -2628,9 -2527,6 +2627,9 @@@ static void prepare_pack(int window, in
        uint32_t i, nr_deltas;
        unsigned n;
  
 +      if (use_delta_islands)
 +              resolve_tree_islands(progress, &to_pack);
 +
        get_object_details();
  
        /*
@@@ -2794,9 -2690,6 +2793,9 @@@ static void show_commit(struct commit *
  
        if (write_bitmap_index)
                index_commit_for_bitmap(commit);
 +
 +      if (use_delta_islands)
 +              propagate_island_marks(commit);
  }
  
  static void show_object(struct object *obj, const char *name, void *data)
        add_preferred_base_object(name);
        add_object_entry(&obj->oid, obj->type, name, 0);
        obj->flags |= OBJECT_ADDED;
 +
 +      if (use_delta_islands) {
 +              const char *p;
 +              unsigned depth = 0;
 +              struct object_entry *ent;
 +
 +              for (p = strchr(name, '/'); p; p = strchr(p + 1, '/'))
 +                      depth++;
 +
 +              ent = packlist_find(&to_pack, obj->oid.hash, NULL);
 +              if (ent && depth > oe_tree_depth(&to_pack, ent))
 +                      oe_set_tree_depth(&to_pack, ent, depth);
 +      }
  }
  
  static void show_object__ma_allow_any(struct object *obj, const char *name, void *data)
@@@ -2925,7 -2805,7 +2924,7 @@@ static void add_objects_in_unpacked_pac
  
        memset(&in_pack, 0, sizeof(in_pack));
  
 -      for (p = get_packed_git(the_repository); p; p = p->next) {
 +      for (p = get_all_packs(the_repository); p; p = p->next) {
                struct object_id oid;
                struct object *o;
  
@@@ -2989,7 -2869,7 +2988,7 @@@ static int has_sha1_pack_kept_or_nonloc
        struct packed_git *p;
  
        p = (last_found != (void *)1) ? last_found :
 -                                      get_packed_git(the_repository);
 +                                      get_all_packs(the_repository);
  
        while (p) {
                if ((!p->pack_local || p->pack_keep ||
                        return 1;
                }
                if (p == last_found)
 -                      p = get_packed_git(the_repository);
 +                      p = get_all_packs(the_repository);
                else
                        p = p->next;
                if (p == last_found)
@@@ -3035,7 -2915,7 +3034,7 @@@ static void loosen_unused_packed_object
        uint32_t i;
        struct object_id oid;
  
 -      for (p = get_packed_git(the_repository); p; p = p->next) {
 +      for (p = get_all_packs(the_repository); p; p = p->next) {
                if (!p->pack_local || p->pack_keep || p->pack_keep_in_core)
                        continue;
  
@@@ -3070,6 -2950,7 +3069,6 @@@ static int pack_options_allow_reuse(voi
  
  static int get_object_list_from_bitmap(struct rev_info *revs)
  {
 -      struct bitmap_index *bitmap_git;
        if (!(bitmap_git = prepare_bitmap_walk(revs)))
                return -1;
  
        }
  
        traverse_bitmap_commit_list(bitmap_git, &add_object_entry_from_bitmap);
 -      free_bitmap_index(bitmap_git);
        return 0;
  }
  
@@@ -3106,7 -2988,7 +3105,7 @@@ static void get_object_list(int ac, con
        char line[1000];
        int flags = 0;
  
 -      init_revisions(&revs, NULL);
 +      repo_init_revisions(the_repository, &revs, NULL);
        save_commit_buffer = 0;
        setup_revisions(ac, av, &revs, NULL);
  
        if (use_bitmap_index && !get_object_list_from_bitmap(&revs))
                return;
  
 +      if (use_delta_islands)
 +              load_delta_islands();
 +
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
        mark_edges_uninteresting(&revs, show_edge);
@@@ -3183,7 -3062,7 +3182,7 @@@ static void add_extra_kept_packs(const 
        if (!names->nr)
                return;
  
 -      for (p = get_packed_git(the_repository); p; p = p->next) {
 +      for (p = get_all_packs(the_repository); p; p = p->next) {
                const char *name = basename(p->pack_name);
                int i;
  
@@@ -3235,6 -3114,7 +3234,6 @@@ static int option_parse_unpack_unreacha
  int cmd_pack_objects(int argc, const char **argv, const char *prefix)
  {
        int use_internal_rev_list = 0;
 -      int thin = 0;
        int shallow = 0;
        int all_progress_implied = 0;
        struct argv_array rp = ARGV_ARRAY_INIT;
                  option_parse_missing_action },
                OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
                         N_("do not pack objects in promisor packfiles")),
 +              OPT_BOOL(0, "delta-islands", &use_delta_islands,
 +                       N_("respect islands during delta compression")),
                OPT_END(),
        };
  
        if (pack_to_stdout || !rev_list_all)
                write_bitmap_index = 0;
  
 +      if (use_delta_islands)
 +              argv_array_push(&rp, "--topo-order");
 +
        if (progress && all_progress_implied)
                progress = 2;
  
        add_extra_kept_packs(&keep_pack_list);
        if (ignore_packed_keep_on_disk) {
                struct packed_git *p;
 -              for (p = get_packed_git(the_repository); p; p = p->next)
 +              for (p = get_all_packs(the_repository); p; p = p->next)
                        if (p->pack_local && p->pack_keep)
                                break;
                if (!p) /* no keep-able packs found */
                 * it also covers non-local objects
                 */
                struct packed_git *p;
 -              for (p = get_packed_git(the_repository); p; p = p->next) {
 +              for (p = get_all_packs(the_repository); p; p = p->next) {
                        if (!p->pack_local) {
                                have_non_local_packs = 1;
                                break;
diff --combined pack-objects.c
index 7e624c30ebd7f344d711b608b29b2d48d21a1ba0,f73f609884ca0d9fdf3d4109b245981c4c073fcb..b6cdbb0166fb5a3d6af3cfda2e7e259e3d54abe8
@@@ -16,7 -16,7 +16,7 @@@ static uint32_t locate_object_entry_has
        while (pdata->index[i] > 0) {
                uint32_t pos = pdata->index[i] - 1;
  
 -              if (!hashcmp(sha1, pdata->objects[pos].idx.oid.hash)) {
 +              if (hasheq(sha1, pdata->objects[pos].idx.oid.hash)) {
                        *found = 1;
                        return i;
                }
@@@ -99,7 -99,7 +99,7 @@@ static void prepare_in_pack_by_idx(stru
         * (i.e. in_pack_idx also zero) should return NULL.
         */
        mapping[cnt++] = NULL;
 -      for (p = get_packed_git(the_repository); p; p = p->next, cnt++) {
 +      for (p = get_all_packs(the_repository); p; p = p->next, cnt++) {
                if (cnt == nr) {
                        free(mapping);
                        return;
@@@ -148,6 -148,9 +148,9 @@@ void prepare_packing_data(struct packin
                                             1U << OE_SIZE_BITS);
        pdata->oe_delta_size_limit = git_env_ulong("GIT_TEST_OE_DELTA_SIZE",
                                                   1UL << OE_DELTA_SIZE_BITS);
+ #ifndef NO_PTHREADS
+       pthread_mutex_init(&pdata->lock, NULL);
+ #endif
  }
  
  struct object_entry *packlist_alloc(struct packing_data *pdata,
                        REALLOC_ARRAY(pdata->in_pack, pdata->nr_alloc);
                if (pdata->delta_size)
                        REALLOC_ARRAY(pdata->delta_size, pdata->nr_alloc);
 +
 +              if (pdata->tree_depth)
 +                      REALLOC_ARRAY(pdata->tree_depth, pdata->nr_alloc);
 +
 +              if (pdata->layer)
 +                      REALLOC_ARRAY(pdata->layer, pdata->nr_alloc);
        }
  
        new_entry = pdata->objects + pdata->nr_objects++;
        if (pdata->in_pack)
                pdata->in_pack[pdata->nr_objects - 1] = NULL;
  
 +      if (pdata->tree_depth)
 +              pdata->tree_depth[pdata->nr_objects - 1] = 0;
 +
 +      if (pdata->layer)
 +              pdata->layer[pdata->nr_objects - 1] = 0;
 +
        return new_entry;
  }
 +
 +void oe_set_delta_ext(struct packing_data *pdata,
 +                    struct object_entry *delta,
 +                    const unsigned char *sha1)
 +{
 +      struct object_entry *base;
 +
 +      ALLOC_GROW(pdata->ext_bases, pdata->nr_ext + 1, pdata->alloc_ext);
 +      base = &pdata->ext_bases[pdata->nr_ext++];
 +      memset(base, 0, sizeof(*base));
 +      hashcpy(base->idx.oid.hash, sha1);
 +
 +      /* These flags mark that we are not part of the actual pack output. */
 +      base->preferred_base = 1;
 +      base->filled = 1;
 +
 +      delta->ext_base = 1;
 +      delta->delta_idx = base - pdata->ext_bases + 1;
 +}
diff --combined pack-objects.h
index 2ca39cfcfe26aea164fc4173c49f0ffd05d4ef04,c34036166a20affd53785f58e74d270ee25e9209..86ee93feb4f7d14518ed1ffe9cac8d8a7cd817fe
@@@ -103,7 -103,6 +103,7 @@@ struct object_entry 
        unsigned no_try_delta:1;
        unsigned type_:TYPE_BITS;
        unsigned in_pack_type:TYPE_BITS; /* could be delta */
 +
        unsigned preferred_base:1; /*
                                    * we do not pack this, but is available
                                    * to be used as the base object to delta
        unsigned filled:1; /* assigned write-order */
        unsigned dfs_state:OE_DFS_STATE_BITS;
        unsigned depth:OE_DEPTH_BITS;
 +      unsigned ext_base:1; /* delta_idx points outside packlist */
  
        /*
         * pahole results on 64-bit linux (gcc and clang)
@@@ -149,20 -147,8 +149,20 @@@ struct packing_data 
        pthread_mutex_t lock;
  #endif
  
 +      /*
 +       * This list contains entries for bases which we know the other side
 +       * has (e.g., via reachability bitmaps), but which aren't in our
 +       * "objects" list.
 +       */
 +      struct object_entry *ext_bases;
 +      uint32_t nr_ext, alloc_ext;
 +
        uintmax_t oe_size_limit;
        uintmax_t oe_delta_size_limit;
 +
 +      /* delta islands */
 +      unsigned int *tree_depth;
 +      unsigned char *layer;
  };
  
  void prepare_packing_data(struct packing_data *pdata);
@@@ -263,12 -249,9 +263,12 @@@ static inline struct object_entry *oe_d
                const struct packing_data *pack,
                const struct object_entry *e)
  {
 -      if (e->delta_idx)
 +      if (!e->delta_idx)
 +              return NULL;
 +      if (e->ext_base)
 +              return &pack->ext_bases[e->delta_idx - 1];
 +      else
                return &pack->objects[e->delta_idx - 1];
 -      return NULL;
  }
  
  static inline void oe_set_delta(struct packing_data *pack,
                e->delta_idx = 0;
  }
  
 +void oe_set_delta_ext(struct packing_data *pack,
 +                    struct object_entry *e,
 +                    const unsigned char *sha1);
 +
  static inline struct object_entry *oe_delta_child(
                const struct packing_data *pack,
                const struct object_entry *e)
@@@ -377,7 -356,7 +377,7 @@@ static inline unsigned long oe_delta_si
                return e->delta_size_;
  
        /*
-        * pack->detla_size[] can't be NULL because oe_set_delta_size()
+        * pack->delta_size[] can't be NULL because oe_set_delta_size()
         * must have been called when a new delta is saved with
         * oe_set_delta().
         * If oe_delta() returns NULL (i.e. default state, which means
@@@ -405,38 -384,4 +405,38 @@@ static inline void oe_set_delta_size(st
        }
  }
  
 +static inline unsigned int oe_tree_depth(struct packing_data *pack,
 +                                       struct object_entry *e)
 +{
 +      if (!pack->tree_depth)
 +              return 0;
 +      return pack->tree_depth[e - pack->objects];
 +}
 +
 +static inline void oe_set_tree_depth(struct packing_data *pack,
 +                                   struct object_entry *e,
 +                                   unsigned int tree_depth)
 +{
 +      if (!pack->tree_depth)
 +              ALLOC_ARRAY(pack->tree_depth, pack->nr_objects);
 +      pack->tree_depth[e - pack->objects] = tree_depth;
 +}
 +
 +static inline unsigned char oe_layer(struct packing_data *pack,
 +                                   struct object_entry *e)
 +{
 +      if (!pack->layer)
 +              return 0;
 +      return pack->layer[e - pack->objects];
 +}
 +
 +static inline void oe_set_layer(struct packing_data *pack,
 +                              struct object_entry *e,
 +                              unsigned char layer)
 +{
 +      if (!pack->layer)
 +              ALLOC_ARRAY(pack->layer, pack->nr_objects);
 +      pack->layer[e - pack->objects] = layer;
 +}
 +
  #endif