fast-import: implement unpack limit
[gitweb.git] / fast-import.c
index bf01b3422142d6be204a983a9909efdb29d385d0..4fb464c1ef553528021b981132919551517412ac 100644 (file)
@@ -166,6 +166,7 @@ Format of STDIN stream:
 #include "quote.h"
 #include "exec_cmd.h"
 #include "dir.h"
+#include "run-command.h"
 
 #define PACK_ID_BITS 16
 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@ -282,6 +283,7 @@ struct recent_command {
 /* Configured limits on output */
 static unsigned long max_depth = 10;
 static off_t max_packsize;
+static int unpack_limit = 100;
 static int force_update;
 static int pack_compression_level = Z_DEFAULT_COMPRESSION;
 static int pack_compression_seen;
@@ -622,7 +624,7 @@ static void *pool_alloc(size_t len)
                        return xmalloc(len);
                }
                total_allocd += sizeof(struct mem_pool) + mem_pool_alloc;
-               p = xmalloc(sizeof(struct mem_pool) + mem_pool_alloc);
+               p = xmalloc(st_add(sizeof(struct mem_pool), mem_pool_alloc));
                p->next_pool = mem_pool;
                p->next_free = (char *) p->space;
                p->end = p->next_free + mem_pool_alloc;
@@ -814,7 +816,8 @@ static struct tree_entry *new_tree_entry(void)
        if (!avail_tree_entry) {
                unsigned int n = tree_entry_alloc;
                total_allocd += n * sizeof(struct tree_entry);
-               avail_tree_entry = e = xmalloc(n * sizeof(struct tree_entry));
+               ALLOC_ARRAY(e, n);
+               avail_tree_entry = e;
                while (n-- > 1) {
                        *((void**)e) = e + 1;
                        e++;
@@ -864,15 +867,12 @@ static void start_packfile(void)
 {
        static char tmp_file[PATH_MAX];
        struct packed_git *p;
-       int namelen;
        struct pack_header hdr;
        int pack_fd;
 
        pack_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
                              "pack/tmp_pack_XXXXXX");
-       namelen = strlen(tmp_file) + 2;
-       p = xcalloc(1, sizeof(*p) + namelen);
-       xsnprintf(p->pack_name, namelen, "%s", tmp_file);
+       FLEX_ALLOC_STR(p, pack_name, tmp_file);
        p->pack_fd = pack_fd;
        p->do_not_close = 1;
        pack_file = sha1fd(pack_fd, p->pack_name);
@@ -898,7 +898,7 @@ static const char *create_index(void)
        struct object_entry_pool *o;
 
        /* Build the table of object IDs. */
-       idx = xmalloc(object_count * sizeof(*idx));
+       ALLOC_ARRAY(idx, object_count);
        c = idx;
        for (o = blocks; o; o = o->next_pool)
                for (e = o->next_free; e-- != o->entries;)
@@ -952,6 +952,23 @@ static void unkeep_all_packs(void)
        }
 }
 
+static int loosen_small_pack(const struct packed_git *p)
+{
+       struct child_process unpack = CHILD_PROCESS_INIT;
+
+       if (lseek(p->pack_fd, 0, SEEK_SET) < 0)
+               die_errno("Failed seeking to start of '%s'", p->pack_name);
+
+       unpack.in = p->pack_fd;
+       unpack.git_cmd = 1;
+       unpack.stdout_to_stderr = 1;
+       argv_array_push(&unpack.args, "unpack-objects");
+       if (!show_stats)
+               argv_array_push(&unpack.args, "-q");
+
+       return run_command(&unpack);
+}
+
 static void end_packfile(void)
 {
        static int running;
@@ -974,6 +991,12 @@ static void end_packfile(void)
                fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
                                    pack_data->pack_name, object_count,
                                    cur_pack_sha1, pack_size);
+
+               if (object_count <= unpack_limit) {
+                       if (!loosen_small_pack(pack_data))
+                               goto discard_pack;
+               }
+
                close(pack_data->pack_fd);
                idx_name = keep_pack(create_index());
 
@@ -1004,6 +1027,7 @@ static void end_packfile(void)
                pack_id++;
        }
        else {
+discard_pack:
                close(pack_data->pack_fd);
                unlink_or_warn(pack_data->pack_name);
        }
@@ -3319,6 +3343,7 @@ static void parse_option(const char *option)
 static void git_pack_config(void)
 {
        int indexversion_value;
+       int limit;
        unsigned long packsizelimit_value;
 
        if (!git_config_get_ulong("pack.depth", &max_depth)) {
@@ -3343,6 +3368,11 @@ static void git_pack_config(void)
        if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value))
                max_packsize = packsizelimit_value;
 
+       if (!git_config_get_int("fastimport.unpacklimit", &limit))
+               unpack_limit = limit;
+       else if (!git_config_get_int("transfer.unpacklimit", &limit))
+               unpack_limit = limit;
+
        git_config(git_default_config, NULL);
 }