Merge branch 'jc/maint-index-pack-keep' into jc/index-pack
authorJunio C Hamano <gitster@pobox.com>
Thu, 3 Mar 2016 21:16:45 +0000 (13:16 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 3 Mar 2016 21:16:45 +0000 (13:16 -0800)
* jc/maint-index-pack-keep:
index-pack: correct --keep[=<msg>]

1  2 
builtin/index-pack.c
t/t5300-pack-object.sh
diff --combined builtin/index-pack.c
index 193908a619307ff38bacb212bd0450aae53e2510,3814731f54a727b2114f9cbdd137f85d03e56d0b..285424f3d4ed66e4d9ae18abedbb4ab04948fa21
@@@ -18,14 -18,16 +18,14 @@@ static const char index_pack_usage[] 
  struct object_entry {
        struct pack_idx_entry idx;
        unsigned long size;
 -      unsigned int hdr_size;
 -      enum object_type type;
 -      enum object_type real_type;
 -      unsigned delta_depth;
 -      int base_object_no;
 +      unsigned char hdr_size;
 +      signed char type;
 +      signed char real_type;
  };
  
 -union delta_base {
 -      unsigned char sha1[20];
 -      off_t offset;
 +struct object_stat {
 +      unsigned delta_depth;
 +      int base_object_no;
  };
  
  struct base_data {
@@@ -47,35 -49,31 +47,35 @@@ struct thread_local 
        int pack_fd;
  };
  
 -/*
 - * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
 - * to memcmp() only the first 20 bytes.
 - */
 -#define UNION_BASE_SZ 20
 -
  #define FLAG_LINK (1u<<20)
  #define FLAG_CHECKED (1u<<21)
  
 -struct delta_entry {
 -      union delta_base base;
 +struct ofs_delta_entry {
 +      off_t offset;
 +      int obj_no;
 +};
 +
 +struct ref_delta_entry {
 +      unsigned char sha1[20];
        int obj_no;
  };
  
  static struct object_entry *objects;
 -static struct delta_entry *deltas;
 +static struct object_stat *obj_stat;
 +static struct ofs_delta_entry *ofs_deltas;
 +static struct ref_delta_entry *ref_deltas;
  static struct thread_local nothread_data;
  static int nr_objects;
 -static int nr_deltas;
 +static int nr_ofs_deltas;
 +static int nr_ref_deltas;
 +static int ref_deltas_alloc;
  static int nr_resolved_deltas;
  static int nr_threads;
  
  static int from_stdin;
  static int strict;
  static int do_fsck_object;
 +static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
  static int verbose;
  static int show_stat;
  static int check_self_contained_and_connected;
@@@ -114,10 -112,6 +114,10 @@@ static pthread_mutex_t deepest_delta_mu
  #define deepest_delta_lock()  lock_mutex(&deepest_delta_mutex)
  #define deepest_delta_unlock()        unlock_mutex(&deepest_delta_mutex)
  
 +static pthread_mutex_t type_cas_mutex;
 +#define type_cas_lock()               lock_mutex(&type_cas_mutex)
 +#define type_cas_unlock()     unlock_mutex(&type_cas_mutex)
 +
  static pthread_key_t key;
  
  static inline void lock_mutex(pthread_mutex_t *mutex)
@@@ -141,7 -135,6 +141,7 @@@ static void init_thread(void
        init_recursive_mutex(&read_mutex);
        pthread_mutex_init(&counter_mutex, NULL);
        pthread_mutex_init(&work_mutex, NULL);
 +      pthread_mutex_init(&type_cas_mutex, NULL);
        if (show_stat)
                pthread_mutex_init(&deepest_delta_mutex, NULL);
        pthread_key_create(&key, NULL);
@@@ -164,7 -157,6 +164,7 @@@ static void cleanup_thread(void
        pthread_mutex_destroy(&read_mutex);
        pthread_mutex_destroy(&counter_mutex);
        pthread_mutex_destroy(&work_mutex);
 +      pthread_mutex_destroy(&type_cas_mutex);
        if (show_stat)
                pthread_mutex_destroy(&deepest_delta_mutex);
        for (i = 0; i < nr_threads; i++)
  #define deepest_delta_lock()
  #define deepest_delta_unlock()
  
 +#define type_cas_lock()
 +#define type_cas_unlock()
 +
  #endif
  
  
 -static int mark_link(struct object *obj, int type, void *data)
 +static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)
  {
        if (!obj)
                return -1;
  
        if (type != OBJ_ANY && obj->type != type)
 -              die(_("object type mismatch at %s"), sha1_to_hex(obj->sha1));
 +              die(_("object type mismatch at %s"), oid_to_hex(&obj->oid));
  
        obj->flags |= FLAG_LINK;
        return 0;
@@@ -217,13 -206,13 +217,13 @@@ static unsigned check_object(struct obj
  
        if (!(obj->flags & FLAG_CHECKED)) {
                unsigned long size;
 -              int type = sha1_object_info(obj->sha1, &size);
 +              int type = sha1_object_info(obj->oid.hash, &size);
                if (type <= 0)
                        die(_("did not receive expected object %s"),
 -                            sha1_to_hex(obj->sha1));
 +                            oid_to_hex(&obj->oid));
                if (type != obj->type)
                        die(_("object %s: expected type %s, found %s"),
 -                          sha1_to_hex(obj->sha1),
 +                          oid_to_hex(&obj->oid),
                            typename(obj->type), typename(type));
                obj->flags |= FLAG_CHECKED;
                return 1;
@@@ -373,7 -362,8 +373,7 @@@ static void set_thread_data(struct thre
  
  static struct base_data *alloc_base_data(void)
  {
 -      struct base_data *base = xmalloc(sizeof(struct base_data));
 -      memset(base, 0, sizeof(*base));
 +      struct base_data *base = xcalloc(1, sizeof(struct base_data));
        base->ref_last = -1;
        base->ofs_last = -1;
        return base;
@@@ -441,7 -431,7 +441,7 @@@ static void *unpack_entry_data(unsigne
        int hdrlen;
  
        if (!is_delta_type(type)) {
 -              hdrlen = sprintf(hdr, "%s %lu", typename(type), size) + 1;
 +              hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), size) + 1;
                git_SHA1_Init(&c);
                git_SHA1_Update(&c, hdr, hdrlen);
        } else
        if (type == OBJ_BLOB && size > big_file_threshold)
                buf = fixed_buf;
        else
 -              buf = xmalloc(size);
 +              buf = xmallocz(size);
  
        memset(&stream, 0, sizeof(stream));
        git_inflate_init(&stream);
  }
  
  static void *unpack_raw_entry(struct object_entry *obj,
 -                            union delta_base *delta_base,
 +                            off_t *ofs_offset,
 +                            unsigned char *ref_sha1,
                              unsigned char *sha1)
  {
        unsigned char *p;
  
        switch (obj->type) {
        case OBJ_REF_DELTA:
 -              hashcpy(delta_base->sha1, fill(20));
 +              hashcpy(ref_sha1, fill(20));
                use(20);
                break;
        case OBJ_OFS_DELTA:
 -              memset(delta_base, 0, sizeof(*delta_base));
                p = fill(1);
                c = *p;
                use(1);
                        use(1);
                        base_offset = (base_offset << 7) + (c & 127);
                }
 -              delta_base->offset = obj->idx.offset - base_offset;
 -              if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset)
 +              *ofs_offset = obj->idx.offset - base_offset;
 +              if (*ofs_offset <= 0 || *ofs_offset >= obj->idx.offset)
                        bad_object(obj->idx.offset, _("delta base offset is out of bound"));
                break;
        case OBJ_COMMIT:
@@@ -554,7 -544,7 +554,7 @@@ static void *unpack_data(struct object_
        git_zstream stream;
        int status;
  
 -      data = xmalloc(consume ? 64*1024 : obj->size);
 +      data = xmallocz(consume ? 64*1024 : obj->size);
        inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
  
        memset(&stream, 0, sizeof(stream));
@@@ -610,110 -600,55 +610,110 @@@ static void *get_data_from_pack(struct 
        return unpack_data(obj, NULL, NULL);
  }
  
 -static int compare_delta_bases(const union delta_base *base1,
 -                             const union delta_base *base2,
 -                             enum object_type type1,
 -                             enum object_type type2)
 +static int compare_ofs_delta_bases(off_t offset1, off_t offset2,
 +                                 enum object_type type1,
 +                                 enum object_type type2)
  {
        int cmp = type1 - type2;
        if (cmp)
                return cmp;
 -      return memcmp(base1, base2, UNION_BASE_SZ);
 +      return offset1 < offset2 ? -1 :
 +             offset1 > offset2 ?  1 :
 +             0;
  }
  
 -static int find_delta(const union delta_base *base, enum object_type type)
 +static int find_ofs_delta(const off_t offset, enum object_type type)
  {
 -      int first = 0, last = nr_deltas;
 -
 -        while (first < last) {
 -                int next = (first + last) / 2;
 -                struct delta_entry *delta = &deltas[next];
 -                int cmp;
 -
 -              cmp = compare_delta_bases(base, &delta->base,
 -                                        type, objects[delta->obj_no].type);
 -                if (!cmp)
 -                        return next;
 -                if (cmp < 0) {
 -                        last = next;
 -                        continue;
 -                }
 -                first = next+1;
 -        }
 -        return -first-1;
 +      int first = 0, last = nr_ofs_deltas;
 +
 +      while (first < last) {
 +              int next = (first + last) / 2;
 +              struct ofs_delta_entry *delta = &ofs_deltas[next];
 +              int cmp;
 +
 +              cmp = compare_ofs_delta_bases(offset, delta->offset,
 +                                            type, objects[delta->obj_no].type);
 +              if (!cmp)
 +                      return next;
 +              if (cmp < 0) {
 +                      last = next;
 +                      continue;
 +              }
 +              first = next+1;
 +      }
 +      return -first-1;
  }
  
 -static void find_delta_children(const union delta_base *base,
 -                              int *first_index, int *last_index,
 -                              enum object_type type)
 +static void find_ofs_delta_children(off_t offset,
 +                                  int *first_index, int *last_index,
 +                                  enum object_type type)
  {
 -      int first = find_delta(base, type);
 +      int first = find_ofs_delta(offset, type);
        int last = first;
 -      int end = nr_deltas - 1;
 +      int end = nr_ofs_deltas - 1;
  
        if (first < 0) {
                *first_index = 0;
                *last_index = -1;
                return;
        }
 -      while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
 +      while (first > 0 && ofs_deltas[first - 1].offset == offset)
                --first;
 -      while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
 +      while (last < end && ofs_deltas[last + 1].offset == offset)
 +              ++last;
 +      *first_index = first;
 +      *last_index = last;
 +}
 +
 +static int compare_ref_delta_bases(const unsigned char *sha1,
 +                                 const unsigned char *sha2,
 +                                 enum object_type type1,
 +                                 enum object_type type2)
 +{
 +      int cmp = type1 - type2;
 +      if (cmp)
 +              return cmp;
 +      return hashcmp(sha1, sha2);
 +}
 +
 +static int find_ref_delta(const unsigned char *sha1, enum object_type type)
 +{
 +      int first = 0, last = nr_ref_deltas;
 +
 +      while (first < last) {
 +              int next = (first + last) / 2;
 +              struct ref_delta_entry *delta = &ref_deltas[next];
 +              int cmp;
 +
 +              cmp = compare_ref_delta_bases(sha1, delta->sha1,
 +                                            type, objects[delta->obj_no].type);
 +              if (!cmp)
 +                      return next;
 +              if (cmp < 0) {
 +                      last = next;
 +                      continue;
 +              }
 +              first = next+1;
 +      }
 +      return -first-1;
 +}
 +
 +static void find_ref_delta_children(const unsigned char *sha1,
 +                                  int *first_index, int *last_index,
 +                                  enum object_type type)
 +{
 +      int first = find_ref_delta(sha1, type);
 +      int last = first;
 +      int end = nr_ref_deltas - 1;
 +
 +      if (first < 0) {
 +              *first_index = 0;
 +              *last_index = -1;
 +              return;
 +      }
 +      while (first > 0 && !hashcmp(ref_deltas[first - 1].sha1, sha1))
 +              --first;
 +      while (last < end && !hashcmp(ref_deltas[last + 1].sha1, sha1))
                ++last;
        *first_index = first;
        *last_index = last;
@@@ -787,7 -722,7 +787,7 @@@ static void sha1_object(const void *dat
        assert(data || obj_entry);
  
        read_lock();
 -      collision_test_needed = has_sha1_file(sha1);
 +      collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK);
        read_unlock();
  
        if (collision_test_needed && !data) {
                        if (!obj)
                                die(_("invalid %s"), typename(type));
                        if (do_fsck_object &&
 -                          fsck_object(obj, 1, fsck_error_function))
 +                          fsck_object(obj, buf, size, &fsck_options))
                                die(_("Error in object"));
 -                      if (fsck_walk(obj, mark_link, NULL))
 -                              die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
 +                      if (fsck_walk(obj, NULL, &fsck_options))
 +                              die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid));
  
                        if (obj->type == OBJ_TREE) {
                                struct tree *item = (struct tree *) obj;
                        }
                        if (obj->type == OBJ_COMMIT) {
                                struct commit *commit = (struct commit *) obj;
 -                              commit->buffer = NULL;
 +                              if (detach_commit_buffer(commit, NULL) != data)
 +                                      die("BUG: parse_object_buffer transmogrified our buffer");
                        }
                        obj->flags |= FLAG_CHECKED;
                }
@@@ -928,16 -862,15 +928,16 @@@ static void resolve_delta(struct object
  {
        void *base_data, *delta_data;
  
 -      delta_obj->real_type = base->obj->real_type;
        if (show_stat) {
 -              delta_obj->delta_depth = base->obj->delta_depth + 1;
 +              int i = delta_obj - objects;
 +              int j = base->obj - objects;
 +              obj_stat[i].delta_depth = obj_stat[j].delta_depth + 1;
                deepest_delta_lock();
 -              if (deepest_delta < delta_obj->delta_depth)
 -                      deepest_delta = delta_obj->delta_depth;
 +              if (deepest_delta < obj_stat[i].delta_depth)
 +                      deepest_delta = obj_stat[i].delta_depth;
                deepest_delta_unlock();
 +              obj_stat[i].base_object_no = j;
        }
 -      delta_obj->base_object_no = base->obj - objects;
        delta_data = get_data_from_pack(delta_obj);
        base_data = get_base_data(base);
        result->obj = delta_obj;
        counter_unlock();
  }
  
 +/*
 + * Standard boolean compare-and-swap: atomically check whether "*type" is
 + * "want"; if so, swap in "set" and return true. Otherwise, leave it untouched
 + * and return false.
 + */
 +static int compare_and_swap_type(signed char *type,
 +                               enum object_type want,
 +                               enum object_type set)
 +{
 +      enum object_type old;
 +
 +      type_cas_lock();
 +      old = *type;
 +      if (old == want)
 +              *type = set;
 +      type_cas_unlock();
 +
 +      return old == want;
 +}
 +
  static struct base_data *find_unresolved_deltas_1(struct base_data *base,
                                                  struct base_data *prev_base)
  {
        if (base->ref_last == -1 && base->ofs_last == -1) {
 -              union delta_base base_spec;
 +              find_ref_delta_children(base->obj->idx.sha1,
 +                                      &base->ref_first, &base->ref_last,
 +                                      OBJ_REF_DELTA);
  
 -              hashcpy(base_spec.sha1, base->obj->idx.sha1);
 -              find_delta_children(&base_spec,
 -                                  &base->ref_first, &base->ref_last, OBJ_REF_DELTA);
 -
 -              memset(&base_spec, 0, sizeof(base_spec));
 -              base_spec.offset = base->obj->idx.offset;
 -              find_delta_children(&base_spec,
 -                                  &base->ofs_first, &base->ofs_last, OBJ_OFS_DELTA);
 +              find_ofs_delta_children(base->obj->idx.offset,
 +                                      &base->ofs_first, &base->ofs_last,
 +                                      OBJ_OFS_DELTA);
  
                if (base->ref_last == -1 && base->ofs_last == -1) {
                        free(base->data);
        }
  
        if (base->ref_first <= base->ref_last) {
 -              struct object_entry *child = objects + deltas[base->ref_first].obj_no;
 +              struct object_entry *child = objects + ref_deltas[base->ref_first].obj_no;
                struct base_data *result = alloc_base_data();
  
 -              assert(child->real_type == OBJ_REF_DELTA);
 +              if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA,
 +                                         base->obj->real_type))
 +                      die("BUG: child->real_type != OBJ_REF_DELTA");
 +
                resolve_delta(child, base, result);
                if (base->ref_first == base->ref_last && base->ofs_last == -1)
                        free_base_data(base);
        }
  
        if (base->ofs_first <= base->ofs_last) {
 -              struct object_entry *child = objects + deltas[base->ofs_first].obj_no;
 +              struct object_entry *child = objects + ofs_deltas[base->ofs_first].obj_no;
                struct base_data *result = alloc_base_data();
  
                assert(child->real_type == OBJ_OFS_DELTA);
 +              child->real_type = base->obj->real_type;
                resolve_delta(child, base, result);
                if (base->ofs_first == base->ofs_last)
                        free_base_data(base);
@@@ -1048,22 -960,15 +1048,22 @@@ static void find_unresolved_deltas(stru
        }
  }
  
 -static int compare_delta_entry(const void *a, const void *b)
 +static int compare_ofs_delta_entry(const void *a, const void *b)
 +{
 +      const struct ofs_delta_entry *delta_a = a;
 +      const struct ofs_delta_entry *delta_b = b;
 +
 +      return delta_a->offset < delta_b->offset ? -1 :
 +             delta_a->offset > delta_b->offset ?  1 :
 +             0;
 +}
 +
 +static int compare_ref_delta_entry(const void *a, const void *b)
  {
 -      const struct delta_entry *delta_a = a;
 -      const struct delta_entry *delta_b = b;
 +      const struct ref_delta_entry *delta_a = a;
 +      const struct ref_delta_entry *delta_b = b;
  
 -      /* group by type (ref vs ofs) and then by value (sha-1 or offset) */
 -      return compare_delta_bases(&delta_a->base, &delta_b->base,
 -                                 objects[delta_a->obj_no].type,
 -                                 objects[delta_b->obj_no].type);
 +      return hashcmp(delta_a->sha1, delta_b->sha1);
  }
  
  static void resolve_base(struct object_entry *obj)
@@@ -1109,8 -1014,7 +1109,8 @@@ static void *threaded_second_pass(void 
  static void parse_pack_objects(unsigned char *sha1)
  {
        int i, nr_delays = 0;
 -      struct delta_entry *delta = deltas;
 +      struct ofs_delta_entry *ofs_delta = ofs_deltas;
 +      unsigned char ref_delta_sha1[20];
        struct stat st;
  
        if (verbose)
                                nr_objects);
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *obj = &objects[i];
 -              void *data = unpack_raw_entry(obj, &delta->base, obj->idx.sha1);
 +              void *data = unpack_raw_entry(obj, &ofs_delta->offset,
 +                                            ref_delta_sha1, obj->idx.sha1);
                obj->real_type = obj->type;
 -              if (is_delta_type(obj->type)) {
 -                      nr_deltas++;
 -                      delta->obj_no = i;
 -                      delta++;
 +              if (obj->type == OBJ_OFS_DELTA) {
 +                      nr_ofs_deltas++;
 +                      ofs_delta->obj_no = i;
 +                      ofs_delta++;
 +              } else if (obj->type == OBJ_REF_DELTA) {
 +                      ALLOC_GROW(ref_deltas, nr_ref_deltas + 1, ref_deltas_alloc);
 +                      hashcpy(ref_deltas[nr_ref_deltas].sha1, ref_delta_sha1);
 +                      ref_deltas[nr_ref_deltas].obj_no = i;
 +                      nr_ref_deltas++;
                } else if (!data) {
                        /* large blobs, check later */
                        obj->real_type = OBJ_BAD;
@@@ -1181,18 -1079,15 +1181,18 @@@ static void resolve_deltas(void
  {
        int i;
  
 -      if (!nr_deltas)
 +      if (!nr_ofs_deltas && !nr_ref_deltas)
                return;
  
        /* Sort deltas by base SHA1/offset for fast searching */
 -      qsort(deltas, nr_deltas, sizeof(struct delta_entry),
 -            compare_delta_entry);
 +      qsort(ofs_deltas, nr_ofs_deltas, sizeof(struct ofs_delta_entry),
 +            compare_ofs_delta_entry);
 +      qsort(ref_deltas, nr_ref_deltas, sizeof(struct ref_delta_entry),
 +            compare_ref_delta_entry);
  
        if (verbose)
 -              progress = start_progress(_("Resolving deltas"), nr_deltas);
 +              progress = start_progress(_("Resolving deltas"),
 +                                        nr_ref_deltas + nr_ofs_deltas);
  
  #ifndef NO_PTHREADS
        nr_dispatched = 0;
   * - append objects to convert thin pack to full pack if required
   * - write the final 20-byte SHA-1
   */
 -static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved);
 +static void fix_unresolved_deltas(struct sha1file *f);
  static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_sha1)
  {
 -      if (nr_deltas == nr_resolved_deltas) {
 +      if (nr_ref_deltas + nr_ofs_deltas == nr_resolved_deltas) {
                stop_progress(&progress);
                /* Flush remaining pack final 20-byte SHA1. */
                flush();
                struct sha1file *f;
                unsigned char read_sha1[20], tail_sha1[20];
                struct strbuf msg = STRBUF_INIT;
 -              int nr_unresolved = nr_deltas - nr_resolved_deltas;
 +              int nr_unresolved = nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas;
                int nr_objects_initial = nr_objects;
                if (nr_unresolved <= 0)
                        die(_("confusion beyond insanity"));
 -              objects = xrealloc(objects,
 -                                 (nr_objects + nr_unresolved + 1)
 -                                 * sizeof(*objects));
 +              REALLOC_ARRAY(objects, nr_objects + nr_unresolved + 1);
                memset(objects + nr_objects + 1, 0,
                       nr_unresolved * sizeof(*objects));
                f = sha1fd(output_fd, curr_pack);
 -              fix_unresolved_deltas(f, nr_unresolved);
 +              fix_unresolved_deltas(f);
                strbuf_addf(&msg, _("completed with %d local objects"),
                            nr_objects - nr_objects_initial);
                stop_progress_msg(&progress, msg.buf);
                        die(_("Unexpected tail checksum for %s "
                              "(disk corruption?)"), curr_pack);
        }
 -      if (nr_deltas != nr_resolved_deltas)
 +      if (nr_ofs_deltas + nr_ref_deltas != nr_resolved_deltas)
                die(Q_("pack has %d unresolved delta",
                       "pack has %d unresolved deltas",
 -                     nr_deltas - nr_resolved_deltas),
 -                  nr_deltas - nr_resolved_deltas);
 +                     nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas),
 +                  nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas);
  }
  
  static int write_compressed(struct sha1file *f, void *in, unsigned int size)
        int status;
        unsigned char outbuf[4096];
  
 -      memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, zlib_compression_level);
        stream.next_in = in;
        stream.avail_in = size;
@@@ -1326,15 -1224,15 +1326,15 @@@ static struct object_entry *append_obj_
  
  static int delta_pos_compare(const void *_a, const void *_b)
  {
 -      struct delta_entry *a = *(struct delta_entry **)_a;
 -      struct delta_entry *b = *(struct delta_entry **)_b;
 +      struct ref_delta_entry *a = *(struct ref_delta_entry **)_a;
 +      struct ref_delta_entry *b = *(struct ref_delta_entry **)_b;
        return a->obj_no - b->obj_no;
  }
  
 -static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
 +static void fix_unresolved_deltas(struct sha1file *f)
  {
 -      struct delta_entry **sorted_by_pos;
 -      int i, n = 0;
 +      struct ref_delta_entry **sorted_by_pos;
 +      int i;
  
        /*
         * Since many unresolved deltas may well be themselves base objects
         * before deltas depending on them, a good heuristic is to start
         * resolving deltas in the same order as their position in the pack.
         */
 -      sorted_by_pos = xmalloc(nr_unresolved * sizeof(*sorted_by_pos));
 -      for (i = 0; i < nr_deltas; i++) {
 -              if (objects[deltas[i].obj_no].real_type != OBJ_REF_DELTA)
 -                      continue;
 -              sorted_by_pos[n++] = &deltas[i];
 -      }
 -      qsort(sorted_by_pos, n, sizeof(*sorted_by_pos), delta_pos_compare);
 +      ALLOC_ARRAY(sorted_by_pos, nr_ref_deltas);
 +      for (i = 0; i < nr_ref_deltas; i++)
 +              sorted_by_pos[i] = &ref_deltas[i];
 +      qsort(sorted_by_pos, nr_ref_deltas, sizeof(*sorted_by_pos), delta_pos_compare);
  
 -      for (i = 0; i < n; i++) {
 -              struct delta_entry *d = sorted_by_pos[i];
 +      for (i = 0; i < nr_ref_deltas; i++) {
 +              struct ref_delta_entry *d = sorted_by_pos[i];
                enum object_type type;
                struct base_data *base_obj = alloc_base_data();
  
                if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
                        continue;
 -              base_obj->data = read_sha1_file(d->base.sha1, &type, &base_obj->size);
 +              base_obj->data = read_sha1_file(d->sha1, &type, &base_obj->size);
                if (!base_obj->data)
                        continue;
  
 -              if (check_sha1_signature(d->base.sha1, base_obj->data,
 +              if (check_sha1_signature(d->sha1, base_obj->data,
                                base_obj->size, typename(type)))
 -                      die(_("local object %s is corrupt"), sha1_to_hex(d->base.sha1));
 -              base_obj->obj = append_obj_to_pack(f, d->base.sha1,
 +                      die(_("local object %s is corrupt"), sha1_to_hex(d->sha1));
 +              base_obj->obj = append_obj_to_pack(f, d->sha1,
                                        base_obj->data, base_obj->size, type);
                find_unresolved_deltas(base_obj);
                display_progress(progress, nr_resolved_deltas);
@@@ -1421,7 -1322,7 +1421,7 @@@ static void final(const char *final_pac
                                 get_object_directory(), sha1_to_hex(sha1));
                        final_pack_name = name;
                }
 -              if (move_temp_to_file(curr_pack_name, final_pack_name))
 +              if (finalize_object_file(curr_pack_name, final_pack_name))
                        die(_("cannot store pack file"));
        } else if (from_stdin)
                chmod(final_pack_name, 0444);
                                 get_object_directory(), sha1_to_hex(sha1));
                        final_index_name = name;
                }
 -              if (move_temp_to_file(curr_index_name, final_index_name))
 +              if (finalize_object_file(curr_index_name, final_index_name))
                        die(_("cannot store index file"));
        } else
                chmod(final_index_name, 0444);
@@@ -1557,7 -1458,7 +1557,7 @@@ static void read_idx_option(struct pack
  
  static void show_pack_info(int stat_only)
  {
 -      int i, baseobjects = nr_objects - nr_deltas;
 +      int i, baseobjects = nr_objects - nr_ref_deltas - nr_ofs_deltas;
        unsigned long *chain_histogram = NULL;
  
        if (deepest_delta)
                struct object_entry *obj = &objects[i];
  
                if (is_delta_type(obj->type))
 -                      chain_histogram[obj->delta_depth - 1]++;
 +                      chain_histogram[obj_stat[i].delta_depth - 1]++;
                if (stat_only)
                        continue;
                printf("%s %-6s %lu %lu %"PRIuMAX,
                       (unsigned long)(obj[1].idx.offset - obj->idx.offset),
                       (uintmax_t)obj->idx.offset);
                if (is_delta_type(obj->type)) {
 -                      struct object_entry *bobj = &objects[obj->base_object_no];
 -                      printf(" %u %s", obj->delta_depth, sha1_to_hex(bobj->idx.sha1));
 +                      struct object_entry *bobj = &objects[obj_stat[i].base_object_no];
 +                      printf(" %u %s", obj_stat[i].delta_depth, sha1_to_hex(bobj->idx.sha1));
                }
                putchar('\n');
        }
@@@ -1615,7 -1516,6 +1615,7 @@@ int cmd_index_pack(int argc, const cha
                usage(index_pack_usage);
  
        check_replace_refs = 0;
 +      fsck_options.walk = mark_link;
  
        reset_pack_idx_option(&opts);
        git_config(git_index_pack_config, &opts);
                        } else if (!strcmp(arg, "--strict")) {
                                strict = 1;
                                do_fsck_object = 1;
 +                      } else if (skip_prefix(arg, "--strict=", &arg)) {
 +                              strict = 1;
 +                              do_fsck_object = 1;
 +                              fsck_set_msg_types(&fsck_options, arg);
                        } else if (!strcmp(arg, "--check-self-contained-and-connected")) {
                                strict = 1;
                                check_self_contained_and_connected = 1;
                        die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
                strbuf_add(&keep_name_buf, pack_name, len);
-               strbuf_addstr(&keep_name_buf, ".idx");
+               strbuf_addstr(&keep_name_buf, ".keep");
                keep_name = keep_name_buf.buf;
        }
        if (verify) {
  
        curr_pack = open_pack_file(pack_name);
        parse_pack_header();
 -      objects = xcalloc(nr_objects + 1, sizeof(struct object_entry));
 -      deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
 +      objects = xcalloc(st_add(nr_objects, 1), sizeof(struct object_entry));
 +      if (show_stat)
 +              obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat));
 +      ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry));
        parse_pack_objects(pack_sha1);
        resolve_deltas();
        conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
 -      free(deltas);
 +      free(ofs_deltas);
 +      free(ref_deltas);
        if (strict)
                foreign_nr = check_objects();
  
        if (show_stat)
                show_pack_info(stat_only);
  
 -      idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
 +      ALLOC_ARRAY(idx_objects, nr_objects);
        for (i = 0; i < nr_objects; i++)
                idx_objects[i] = &objects[i].idx;
        curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_sha1);
diff --combined t/t5300-pack-object.sh
index fc2be63e02d85682cd24457ee646c8c626ca967a,0e3cadfd577b2d6691bc9b8e9bfd6f83b99c4525..899e52d50f0d73e4a755c4f7991364f34912b336
@@@ -8,7 -8,7 +8,7 @@@ test_description='git pack-objec
  '
  . ./test-lib.sh
  
 -TRASH=`pwd`
 +TRASH=$(pwd)
  
  test_expect_success \
      'setup' \
@@@ -20,8 -20,8 +20,8 @@@
       test-genrandom "seed b" 2097152 > b_big &&
       git update-index --add a a_big b b_big c &&
       cat c >d && echo foo >>d && git update-index --add d &&
 -     tree=`git write-tree` &&
 -     commit=`git commit-tree $tree </dev/null` && {
 +     tree=$(git write-tree) &&
 +     commit=$(git commit-tree $tree </dev/null) && {
         echo $tree &&
         echo $commit &&
         git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\)       .*/\\1/"
@@@ -29,7 -29,7 +29,7 @@@
         git diff-tree --root -p $commit &&
         while read object
         do
 -          t=`git cat-file -t $object` &&
 +          t=$(git cat-file -t $object) &&
            git cat-file $t $object || return 1
         done <obj-list
       } >expect'
@@@ -147,7 -147,7 +147,7 @@@ test_expect_success 
         git diff-tree --root -p $commit &&
         while read object
         do
 -          t=`git cat-file -t $object` &&
 +          t=$(git cat-file -t $object) &&
            git cat-file $t $object || return 1
         done <obj-list
      } >current &&
@@@ -162,7 -162,7 +162,7 @@@ test_expect_success 
         git diff-tree --root -p $commit &&
         while read object
         do
 -          t=`git cat-file -t $object` &&
 +          t=$(git cat-file -t $object) &&
            git cat-file $t $object || return 1
         done <obj-list
      } >current &&
@@@ -177,7 -177,7 +177,7 @@@ test_expect_success 
         git diff-tree --root -p $commit &&
         while read object
         do
 -          t=`git cat-file -t $object` &&
 +          t=$(git cat-file -t $object) &&
            git cat-file $t $object || return 1
         done <obj-list
      } >current &&
@@@ -252,8 -252,8 +252,8 @@@ test_expect_success 
  
  test_expect_success \
      'verify-pack catches a corrupted sum of the index file itself' \
 -    'l=`wc -c <test-3.idx` &&
 -     l=`expr $l - 20` &&
 +    'l=$(wc -c <test-3.idx) &&
 +     l=$(expr $l - 20) &&
       cat test-1-${packname_1}.pack >test-3.pack &&
       printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
       if git verify-pack test-3.pack
@@@ -284,6 -284,12 +284,12 @@@ test_expect_success 
       git index-pack test-3.pack &&
       cmp test-3.idx test-3-${packname_3}.idx &&
  
+      cat test-1-${packname_1}.pack >test-4.pack &&
+      rm -f test-4.keep &&
+      git index-pack --keep=why test-4.pack &&
+      cmp test-1-${packname_1}.idx test-4.idx &&
+      test -f test-4.keep &&
       :'
  
  test_expect_success 'unpacking with --strict' '