Merge branch 'eb/mail'
[gitweb.git] / pack-objects.c
index 4f8814d8ee27773af52d4b16e5fd82d8b88b57df..179560f2bd67e08d25bef0199c41ee0ee70793f9 100644 (file)
@@ -1,10 +1,15 @@
 #include "cache.h"
 #include "object.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree.h"
 #include "delta.h"
 #include "pack.h"
 #include "csum-file.h"
-#include "diff.h"
+#include "tree-walk.h"
 #include <sys/time.h>
+#include <signal.h>
 
 static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
 
@@ -31,9 +36,6 @@ struct object_entry {
                                 * be used as the base objectto delta huge
                                 * objects against.
                                 */
-       int based_on_preferred; /* current delta candidate is a preferred
-                                * one, or delta against a preferred one.
-                                */
 };
 
 /*
@@ -60,6 +62,7 @@ static int nr_objects = 0, nr_alloc = 0, nr_result = 0;
 static const char *base_name;
 static unsigned char pack_file_sha1[20];
 static int progress = 1;
+static volatile sig_atomic_t progress_update = 0;
 
 /*
  * The object names in objects array are hashed with this hashtable,
@@ -97,7 +100,7 @@ static int reused_delta = 0;
 
 static int pack_revindex_ix(struct packed_git *p)
 {
-       unsigned int ui = (unsigned int) p;
+       unsigned long ui = (unsigned long)p;
        int i;
 
        ui = ui ^ (ui >> 16); /* defeat structure alignment */
@@ -153,7 +156,7 @@ static void prepare_pack_revindex(struct pack_revindex *rix)
 
        rix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1));
        for (i = 0; i < num_ent; i++) {
-               long hl = *((long *)(index + 24 * i));
+               unsigned int hl = *((unsigned int *)(index + 24 * i));
                rix->revindex[i] = ntohl(hl);
        }
        /* This knows the pack format -- the 20-byte trailer
@@ -334,20 +337,41 @@ static void write_pack_file(void)
        struct sha1file *f;
        unsigned long offset;
        struct pack_header hdr;
+       unsigned last_percent = 999;
+       int do_progress = 0;
 
        if (!base_name)
                f = sha1fd(1, "<stdout>");
-       else
+       else {
                f = sha1create("%s-%s.%s", base_name,
                               sha1_to_hex(object_list_sha1), "pack");
+               do_progress = progress;
+       }
+       if (do_progress)
+               fprintf(stderr, "Writing %d objects.\n", nr_result);
+
        hdr.hdr_signature = htonl(PACK_SIGNATURE);
        hdr.hdr_version = htonl(PACK_VERSION);
        hdr.hdr_entries = htonl(nr_result);
        sha1write(f, &hdr, sizeof(hdr));
        offset = sizeof(hdr);
-       for (i = 0; i < nr_objects; i++)
+       if (!nr_result)
+               goto done;
+       for (i = 0; i < nr_objects; i++) {
                offset = write_one(f, objects + i, offset);
-
+               if (do_progress) {
+                       unsigned percent = written * 100 / nr_result;
+                       if (progress_update || percent != last_percent) {
+                               fprintf(stderr, "%4u%% (%u/%u) done\r",
+                                       percent, written, nr_result);
+                               progress_update = 0;
+                               last_percent = percent;
+                       }
+               }
+       }
+       if (do_progress)
+               fputc('\n', stderr);
+ done:
        sha1close(f, pack_file_sha1, 1);
 }
 
@@ -429,7 +453,7 @@ static void rehash_objects(void)
        if (object_ix_hashsz < 1024)
                object_ix_hashsz = 1024;
        object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);
-       object_ix = memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
+       memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
        for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
                int ix = locate_object_entry_hash(oe->sha1);
                if (0 <= ix)
@@ -439,15 +463,32 @@ static void rehash_objects(void)
        }
 }
 
-static int add_object_entry(const unsigned char *sha1, const char *name, int exclude)
+static unsigned name_hash(const char *name)
+{
+       unsigned char c;
+       unsigned hash = 0;
+
+       /*
+        * This effectively just creates a sortable number from the
+        * last sixteen non-whitespace characters. Last characters
+        * count "most", so things that end in ".c" sort together.
+        */
+       while ((c = *name++) != 0) {
+               if (isspace(c))
+                       continue;
+               hash = (hash >> 2) + (c << 24);
+       }
+       return hash;
+}
+
+static int add_object_entry(const unsigned char *sha1, unsigned hash, int exclude)
 {
-       unsigned int hash = 0;
        unsigned int idx = nr_objects;
        struct object_entry *entry;
        struct packed_git *p;
        unsigned int found_offset = 0;
        struct packed_git *found_pack = NULL;
-       int ix;
+       int ix, status = 0;
 
        if (!exclude) {
                for (p = packed_git; p; p = p->next) {
@@ -467,13 +508,6 @@ static int add_object_entry(const unsigned char *sha1, const char *name, int exc
        if ((entry = locate_object_entry(sha1)) != NULL)
                goto already_added;
 
-       while (*name) {
-               unsigned char c = *name++;
-               if (isspace(c))
-                       continue;
-               hash = hash * 11 + c;
-       }
-
        if (idx >= nr_alloc) {
                unsigned int needed = (idx + 1024) * 3 / 2;
                objects = xrealloc(objects, needed * sizeof(*entry));
@@ -493,8 +527,13 @@ static int add_object_entry(const unsigned char *sha1, const char *name, int exc
                        die("internal error in object hashing.");
                object_ix[-1 - ix] = idx + 1;
        }
+       status = 1;
 
  already_added:
+       if (progress_update) {
+               fprintf(stderr, "Counting objects...%d\r", nr_objects);
+               progress_update = 0;
+       }
        if (exclude)
                entry->preferred_base = 1;
        else {
@@ -503,49 +542,248 @@ static int add_object_entry(const unsigned char *sha1, const char *name, int exc
                        entry->in_pack_offset = found_offset;
                }
        }
-       return 1;
+       return status;
 }
 
-static void add_pbase_tree(struct tree_desc *tree)
+struct pbase_tree_cache {
+       unsigned char sha1[20];
+       int ref;
+       int temporary;
+       void *tree_data;
+       unsigned long tree_size;
+};
+
+static struct pbase_tree_cache *(pbase_tree_cache[256]);
+static int pbase_tree_cache_ix(const unsigned char *sha1)
+{
+       return sha1[0] % ARRAY_SIZE(pbase_tree_cache);
+}
+static int pbase_tree_cache_ix_incr(int ix)
 {
-       while (tree->size) {
-               const unsigned char *sha1;
-               const char *name;
-               unsigned mode;
+       return (ix+1) % ARRAY_SIZE(pbase_tree_cache);
+}
+
+static struct pbase_tree {
+       struct pbase_tree *next;
+       /* This is a phony "cache" entry; we are not
+        * going to evict it nor find it through _get()
+        * mechanism -- this is for the toplevel node that
+        * would almost always change with any commit.
+        */
+       struct pbase_tree_cache pcache;
+} *pbase_tree;
+
+static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
+{
+       struct pbase_tree_cache *ent, *nent;
+       void *data;
+       unsigned long size;
+       char type[20];
+       int neigh;
+       int my_ix = pbase_tree_cache_ix(sha1);
+       int available_ix = -1;
+
+       /* pbase-tree-cache acts as a limited hashtable.
+        * your object will be found at your index or within a few
+        * slots after that slot if it is cached.
+        */
+       for (neigh = 0; neigh < 8; neigh++) {
+               ent = pbase_tree_cache[my_ix];
+               if (ent && !memcmp(ent->sha1, sha1, 20)) {
+                       ent->ref++;
+                       return ent;
+               }
+               else if (((available_ix < 0) && (!ent || !ent->ref)) ||
+                        ((0 <= available_ix) &&
+                         (!ent && pbase_tree_cache[available_ix])))
+                       available_ix = my_ix;
+               if (!ent)
+                       break;
+               my_ix = pbase_tree_cache_ix_incr(my_ix);
+       }
+
+       /* 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);
+       if (!data)
+               return NULL;
+       if (strcmp(type, tree_type)) {
+               free(data);
+               return NULL;
+       }
+
+       /* We need to either cache or return a throwaway copy */
+
+       if (available_ix < 0)
+               ent = NULL;
+       else {
+               ent = pbase_tree_cache[available_ix];
+               my_ix = available_ix;
+       }
+
+       if (!ent) {
+               nent = xmalloc(sizeof(*nent));
+               nent->temporary = (available_ix < 0);
+       }
+       else {
+               /* evict and reuse */
+               free(ent->tree_data);
+               nent = ent;
+       }
+       memcpy(nent->sha1, sha1, 20);
+       nent->tree_data = data;
+       nent->tree_size = size;
+       nent->ref = 1;
+       if (!nent->temporary)
+               pbase_tree_cache[my_ix] = nent;
+       return nent;
+}
+
+static void pbase_tree_put(struct pbase_tree_cache *cache)
+{
+       if (!cache->temporary) {
+               cache->ref--;
+               return;
+       }
+       free(cache->tree_data);
+       free(cache);
+}
+
+static int name_cmp_len(const char *name)
+{
+       int i;
+       for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++)
+               ;
+       return i;
+}
+
+static void add_pbase_object(struct tree_desc *tree,
+                            const char *name,
+                            int cmplen,
+                            const char *fullname)
+{
+       struct name_entry entry;
+
+       while (tree_entry(tree,&entry)) {
                unsigned long size;
                char type[20];
 
-               sha1 = tree_entry_extract(tree, &name, &mode);
-               update_tree_entry(tree);
-               if (!has_sha1_file(sha1))
+               if (entry.pathlen != cmplen ||
+                   memcmp(entry.path, name, cmplen) ||
+                   !has_sha1_file(entry.sha1) ||
+                   sha1_object_info(entry.sha1, type, &size))
                        continue;
-               if (sha1_object_info(sha1, type, &size))
-                       continue;
-               add_object_entry(sha1, name, 1);
-               if (!strcmp(type, "tree")) {
+               if (name[cmplen] != '/') {
+                       unsigned hash = name_hash(fullname);
+                       add_object_entry(entry.sha1, hash, 1);
+                       return;
+               }
+               if (!strcmp(type, tree_type)) {
                        struct tree_desc sub;
-                       void *elem;
-                       elem = read_sha1_file(sha1, type, &sub.size);
-                       sub.buf = elem;
-                       if (sub.buf) {
-                               add_pbase_tree(&sub);
-                               free(elem);
-                       }
+                       struct pbase_tree_cache *tree;
+                       const char *down = name+cmplen+1;
+                       int downlen = name_cmp_len(down);
+
+                       tree = pbase_tree_get(entry.sha1);
+                       if (!tree)
+                               return;
+                       sub.buf = tree->tree_data;
+                       sub.size = tree->tree_size;
+
+                       add_pbase_object(&sub, down, downlen, fullname);
+                       pbase_tree_put(tree);
+               }
+       }
+}
+
+static unsigned *done_pbase_paths;
+static int done_pbase_paths_num;
+static int done_pbase_paths_alloc;
+static int done_pbase_path_pos(unsigned hash)
+{
+       int lo = 0;
+       int hi = done_pbase_paths_num;
+       while (lo < hi) {
+               int mi = (hi + lo) / 2;
+               if (done_pbase_paths[mi] == hash)
+                       return mi;
+               if (done_pbase_paths[mi] < hash)
+                       hi = mi;
+               else
+                       lo = mi + 1;
+       }
+       return -lo-1;
+}
+
+static int check_pbase_path(unsigned hash)
+{
+       int pos = (!done_pbase_paths) ? -1 : done_pbase_path_pos(hash);
+       if (0 <= pos)
+               return 1;
+       pos = -pos - 1;
+       if (done_pbase_paths_alloc <= done_pbase_paths_num) {
+               done_pbase_paths_alloc = alloc_nr(done_pbase_paths_alloc);
+               done_pbase_paths = xrealloc(done_pbase_paths,
+                                           done_pbase_paths_alloc *
+                                           sizeof(unsigned));
+       }
+       done_pbase_paths_num++;
+       if (pos < done_pbase_paths_num)
+               memmove(done_pbase_paths + pos + 1,
+                       done_pbase_paths + pos,
+                       (done_pbase_paths_num - pos - 1) * sizeof(unsigned));
+       done_pbase_paths[pos] = hash;
+       return 0;
+}
+
+static void add_preferred_base_object(char *name, unsigned hash)
+{
+       struct pbase_tree *it;
+       int cmplen = name_cmp_len(name);
+
+       if (check_pbase_path(hash))
+               return;
+
+       for (it = pbase_tree; it; it = it->next) {
+               if (cmplen == 0) {
+                       hash = name_hash("");
+                       add_object_entry(it->pcache.sha1, hash, 1);
+               }
+               else {
+                       struct tree_desc tree;
+                       tree.buf = it->pcache.tree_data;
+                       tree.size = it->pcache.tree_size;
+                       add_pbase_object(&tree, name, cmplen, name);
                }
        }
 }
 
 static void add_preferred_base(unsigned char *sha1)
 {
-       struct tree_desc tree;
-       void *elem;
-       elem = read_object_with_reference(sha1, "tree", &tree.size, NULL);
-       tree.buf = elem;
-       if (!tree.buf)
+       struct pbase_tree *it;
+       void *data;
+       unsigned long size;
+       unsigned char tree_sha1[20];
+
+       data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
+       if (!data)
                return;
-       add_object_entry(sha1, "", 1);
-       add_pbase_tree(&tree);
-       free(elem);
+
+       for (it = pbase_tree; it; it = it->next) {
+               if (!memcmp(it->pcache.sha1, tree_sha1, 20)) {
+                       free(data);
+                       return;
+               }
+       }
+
+       it = xcalloc(1, sizeof(*it));
+       it->next = pbase_tree;
+       pbase_tree = it;
+
+       memcpy(it->pcache.sha1, tree_sha1, 20);
+       it->pcache.tree_data = data;
+       it->pcache.tree_size = size;
 }
 
 static void check_object(struct object_entry *entry)
@@ -597,13 +835,13 @@ static void check_object(struct object_entry *entry)
                die("unable to get type of object %s",
                    sha1_to_hex(entry->sha1));
 
-       if (!strcmp(type, "commit")) {
+       if (!strcmp(type, commit_type)) {
                entry->type = OBJ_COMMIT;
-       } else if (!strcmp(type, "tree")) {
+       } else if (!strcmp(type, tree_type)) {
                entry->type = OBJ_TREE;
-       } else if (!strcmp(type, "blob")) {
+       } else if (!strcmp(type, blob_type)) {
                entry->type = OBJ_BLOB;
-       } else if (!strcmp(type, "tag")) {
+       } else if (!strcmp(type, tag_type)) {
                entry->type = OBJ_TAG;
        } else
                die("unable to pack object %s of type %s",
@@ -631,10 +869,23 @@ static void get_object_details(void)
        prepare_pack_ix();
        for (i = 0, entry = objects; i < nr_objects; i++, entry++)
                check_object(entry);
-       for (i = 0, entry = objects; i < nr_objects; i++, entry++)
-               if (!entry->delta && entry->delta_child)
-                       entry->delta_limit =
-                               check_delta_limit(entry, 1);
+
+       if (nr_objects == nr_result) {
+               /*
+                * Depth of objects that depend on the entry -- this
+                * is subtracted from depth-max to break too deep
+                * delta chain because of delta data reusing.
+                * However, we loosen this restriction when we know we
+                * are creating a thin pack -- it will have to be
+                * expanded on the other end anyway, so do not
+                * artificially cut the delta chain and let it go as
+                * deep as it wants.
+                */
+               for (i = 0, entry = objects; i < nr_objects; i++, entry++)
+                       if (!entry->delta && entry->delta_child)
+                               entry->delta_limit =
+                                       check_delta_limit(entry, 1);
+       }
 }
 
 typedef int (*entry_sort_t)(const struct object_entry *, const struct object_entry *);
@@ -665,7 +916,7 @@ static int sha1_sort(const struct object_entry *a, const struct object_entry *b)
        return memcmp(a->sha1, b->sha1, 20);
 }
 
-static struct object_entry **create_final_object_list()
+static struct object_entry **create_final_object_list(void)
 {
        struct object_entry **list;
        int i, j;
@@ -707,6 +958,7 @@ static int type_size_sort(const struct object_entry *a, const struct object_entr
 struct unpacked {
        struct object_entry *entry;
        void *data;
+       struct delta_index *index;
 };
 
 /*
@@ -717,86 +969,64 @@ struct unpacked {
  * more importantly, the bigger file is likely the more recent
  * one.
  */
-static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_depth)
+static int try_delta(struct unpacked *trg, struct unpacked *src,
+                    struct delta_index *src_index, unsigned max_depth)
 {
-       struct object_entry *cur_entry = cur->entry;
-       struct object_entry *old_entry = old->entry;
-       int old_preferred = (old_entry->preferred_base ||
-                            old_entry->based_on_preferred);
-       unsigned long size, oldsize, delta_size, sizediff;
-       long max_size;
+       struct object_entry *trg_entry = trg->entry;
+       struct object_entry *src_entry = src->entry;
+       unsigned long size, src_size, delta_size, sizediff, max_size;
        void *delta_buf;
 
        /* Don't bother doing diffs between different types */
-       if (cur_entry->type != old_entry->type)
+       if (trg_entry->type != src_entry->type)
                return -1;
 
        /* We do not compute delta to *create* objects we are not
         * going to pack.
         */
-       if (cur_entry->preferred_base)
+       if (trg_entry->preferred_base)
                return -1;
 
-       /* If the current object is at pack edge, take the depth the
+       /*
+        * If the current object is at pack edge, take the depth the
         * objects that depend on the current object into account --
         * otherwise they would become too deep.
         */
-       if (cur_entry->delta_child) {
-               if (max_depth <= cur_entry->delta_limit)
+       if (trg_entry->delta_child) {
+               if (max_depth <= trg_entry->delta_limit)
                        return 0;
-               max_depth -= cur_entry->delta_limit;
+               max_depth -= trg_entry->delta_limit;
        }
-
-       size = cur_entry->size;
-       oldsize = old_entry->size;
-       sizediff = oldsize > size ? oldsize - size : size - oldsize;
-
-       if (size < 50)
-               return -1;
-       if (old_entry->depth >= max_depth)
+       if (src_entry->depth >= max_depth)
                return 0;
 
-       /*
-        * NOTE!
-        *
-        * We always delta from the bigger to the smaller, since that's
-        * more space-efficient (deletes don't have to say _what_ they
-        * delete).
-        */
-       max_size = size / 2 - 20;
-       if (cur_entry->delta) {
-               if (cur_entry->based_on_preferred) {
-                       if (old_preferred)
-                               max_size = cur_entry->delta_size-1;
-                       else
-                               /* trying with non-preferred one when we
-                                * already have a delta based on preferred
-                                * one is pointless.
-                                */
-                               return 0;
-               }
-               else if (!old_preferred)
-                       max_size = cur_entry->delta_size-1;
-               else
-                       /* otherwise...  even if delta with a
-                        * preferred one produces a bigger result than
-                        * what we currently have, which is based on a
-                        * non-preferred one, it is OK.
-                        */
-                       ;
-       }
+       /* Now some size filtering heuristics. */
+       size = trg_entry->size;
+       max_size = size/2 - 20;
+       max_size = max_size * (max_depth - src_entry->depth) / max_depth;
+       if (max_size == 0)
+               return 0;
+       if (trg_entry->delta && trg_entry->delta_size <= max_size)
+               max_size = trg_entry->delta_size-1;
+       src_size = src_entry->size;
+       sizediff = src_size < size ? size - src_size : 0;
        if (sizediff >= max_size)
-               return -1;
-       delta_buf = diff_delta(old->data, oldsize,
-                              cur->data, size, &delta_size, max_size);
+               return 0;
+
+       delta_buf = create_delta(src_index, trg->data, size, &delta_size, max_size);
        if (!delta_buf)
                return 0;
-       cur_entry->delta = old_entry;
-       cur_entry->delta_size = delta_size;
-       cur_entry->depth = old_entry->depth + 1;
-       cur_entry->based_on_preferred = old_preferred;
+
+       trg_entry->delta = src_entry;
+       trg_entry->delta_size = delta_size;
+       trg_entry->depth = src_entry->depth + 1;
        free(delta_buf);
-       return 0;
+       return 1;
+}
+
+static void progress_interval(int signum)
+{
+       progress_update = 1;
 }
 
 static void find_deltas(struct object_entry **list, int window, int depth)
@@ -804,12 +1034,14 @@ static void find_deltas(struct object_entry **list, int window, int depth)
        int i, idx;
        unsigned int array_size = window * sizeof(struct unpacked);
        struct unpacked *array = xmalloc(array_size);
-       int eye_candy;
+       unsigned processed = 0;
+       unsigned last_percent = 999;
 
        memset(array, 0, array_size);
        i = nr_objects;
        idx = 0;
-       eye_candy = i - (nr_objects / 20);
+       if (progress)
+               fprintf(stderr, "Deltifying %d objects.\n", nr_result);
 
        while (--i >= 0) {
                struct object_entry *entry = list[i];
@@ -818,9 +1050,17 @@ static void find_deltas(struct object_entry **list, int window, int depth)
                char type[10];
                int j;
 
-               if (progress && i <= eye_candy) {
-                       eye_candy -= nr_objects / 20;
-                       fputc('.', stderr);
+               if (!entry->preferred_base)
+                       processed++;
+
+               if (progress) {
+                       unsigned percent = processed * 100 / nr_result;
+                       if (percent != last_percent || progress_update) {
+                               fprintf(stderr, "%4u%% (%u/%u) done\r",
+                                       percent, processed, nr_result);
+                               progress_update = 0;
+                               last_percent = percent;
+                       }
                }
 
                if (entry->delta)
@@ -829,11 +1069,16 @@ static void find_deltas(struct object_entry **list, int window, int depth)
                         */
                        continue;
 
+               if (entry->size < 50)
+                       continue;
+               free_delta_index(n->index);
+               n->index = NULL;
                free(n->data);
                n->entry = entry;
                n->data = read_sha1_file(entry->sha1, type, &size);
                if (size != entry->size)
-                       die("object %s inconsistent object length (%lu vs %lu)", sha1_to_hex(entry->sha1), size, entry->size);
+                       die("object %s inconsistent object length (%lu vs %lu)",
+                           sha1_to_hex(entry->sha1), size, entry->size);
 
                j = window;
                while (--j > 0) {
@@ -844,33 +1089,41 @@ static void find_deltas(struct object_entry **list, int window, int depth)
                        m = array + other_idx;
                        if (!m->entry)
                                break;
-                       if (try_delta(n, m, depth) < 0)
+                       if (try_delta(n, m, m->index, depth) < 0)
                                break;
                }
+               /* if we made n a delta, and if n is already at max
+                * depth, leaving it in the window is pointless.  we
+                * should evict it first.
+                */
+               if (entry->delta && depth <= entry->depth)
+                       continue;
+
+               n->index = create_delta_index(n->data, size);
+               if (!n->index)
+                       die("out of memory");
+
                idx++;
                if (idx >= window)
                        idx = 0;
        }
 
-       for (i = 0; i < window; ++i)
+       if (progress)
+               fputc('\n', stderr);
+
+       for (i = 0; i < window; ++i) {
+               free_delta_index(array[i].index);
                free(array[i].data);
+       }
        free(array);
 }
 
 static void prepare_pack(int window, int depth)
 {
-       if (progress)
-               fprintf(stderr, "Packing %d objects", nr_result);
        get_object_details();
-       if (progress)
-               fputc('.', stderr);
-
        sorted_by_type = create_sorted_list(type_size_sort);
        if (window && depth)
                find_deltas(sorted_by_type, window+1, depth);
-       if (progress)
-               fputc('\n', stderr);
-       write_pack_file();
 }
 
 static int reuse_cached_pack(unsigned char *sha1, int pack_to_stdout)
@@ -927,20 +1180,35 @@ static int reuse_cached_pack(unsigned char *sha1, int pack_to_stdout)
        return 1;
 }
 
+static void setup_progress_signal(void)
+{
+       struct sigaction sa;
+       struct itimerval v;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = progress_interval;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_RESTART;
+       sigaction(SIGALRM, &sa, NULL);
+
+       v.it_interval.tv_sec = 1;
+       v.it_interval.tv_usec = 0;
+       v.it_value = v.it_interval;
+       setitimer(ITIMER_REAL, &v, NULL);
+}
+
 int main(int argc, char **argv)
 {
        SHA_CTX ctx;
-       char line[PATH_MAX + 20];
+       char line[40 + 1 + PATH_MAX + 2];
        int window = 10, depth = 10, pack_to_stdout = 0;
        struct object_entry **list;
+       int num_preferred_base = 0;
        int i;
-       struct timeval prev_tv;
-       int eye_candy = 0;
-       int eye_candy_incr = 500;
-
 
        setup_git_directory();
 
+       progress = isatty(2);
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
@@ -971,6 +1239,10 @@ int main(int argc, char **argv)
                                        usage(pack_usage);
                                continue;
                        }
+                       if (!strcmp("--progress", arg)) {
+                               progress = 1;
+                               continue;
+                       }
                        if (!strcmp("-q", arg)) {
                                progress = 0;
                                continue;
@@ -994,46 +1266,47 @@ int main(int argc, char **argv)
                usage(pack_usage);
 
        prepare_packed_git();
+
        if (progress) {
                fprintf(stderr, "Generating pack...\n");
-               gettimeofday(&prev_tv, NULL);
+               setup_progress_signal();
        }
-       while (fgets(line, sizeof(line), stdin) != NULL) {
+
+       for (;;) {
                unsigned char sha1[20];
+               unsigned hash;
 
-               if (progress && (eye_candy <= nr_objects)) {
-                       fprintf(stderr, "Counting objects...%d\r", nr_objects);
-                       if (eye_candy && (50 <= eye_candy_incr)) {
-                               struct timeval tv;
-                               int time_diff;
-                               gettimeofday(&tv, NULL);
-                               time_diff = (tv.tv_sec - prev_tv.tv_sec);
-                               time_diff <<= 10;
-                               time_diff += (tv.tv_usec - prev_tv.tv_usec);
-                               if ((1 << 9) < time_diff)
-                                       eye_candy_incr += 50;
-                               else if (50 < eye_candy_incr)
-                                       eye_candy_incr -= 50;
-                       }
-                       eye_candy += eye_candy_incr;
+               if (!fgets(line, sizeof(line), stdin)) {
+                       if (feof(stdin))
+                               break;
+                       if (!ferror(stdin))
+                               die("fgets returned NULL, not EOF, not error!");
+                       if (errno != EINTR)
+                               die("fgets: %s", strerror(errno));
+                       clearerr(stdin);
+                       continue;
                }
+
                if (line[0] == '-') {
                        if (get_sha1_hex(line+1, sha1))
                                die("expected edge sha1, got garbage:\n %s",
                                    line+1);
-                       add_preferred_base(sha1);
+                       if (num_preferred_base++ < window)
+                               add_preferred_base(sha1);
                        continue;
                }
                if (get_sha1_hex(line, sha1))
                        die("expected sha1, got garbage:\n %s", line);
-               add_object_entry(sha1, line+40, 0);
+               hash = name_hash(line+41);
+               add_preferred_base_object(line+41, hash);
+               add_object_entry(sha1, hash, 0);
        }
        if (progress)
                fprintf(stderr, "Done counting %d objects.\n", nr_objects);
-       if (non_empty && !nr_objects)
+       sorted_by_sha = create_final_object_list();
+       if (non_empty && !nr_result)
                return 0;
 
-       sorted_by_sha = create_final_object_list();
        SHA1_Init(&ctx);
        list = sorted_by_sha;
        for (i = 0; i < nr_result; i++) {
@@ -1047,7 +1320,16 @@ int main(int argc, char **argv)
        if (reuse_cached_pack(object_list_sha1, pack_to_stdout))
                ;
        else {
-               prepare_pack(window, depth);
+               if (nr_result)
+                       prepare_pack(window, depth);
+               if (progress && pack_to_stdout) {
+                       /* the other end usually displays progress itself */
+                       struct itimerval v = {{0,},};
+                       setitimer(ITIMER_REAL, &v, NULL);
+                       signal(SIGALRM, SIG_IGN );
+                       progress_update = 0;
+               }
+               write_pack_file();
                if (!pack_to_stdout) {
                        write_index_file();
                        puts(sha1_to_hex(object_list_sha1));