diff --git a/Documentation/config.txt b/Documentation/config.txt index 2e1b2e486e..58673cf21e 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1189,6 +1189,15 @@ difftool..cmd:: difftool.prompt:: Prompt before each invocation of the diff tool. +fastimport.unpackLimit:: + If the number of objects imported by linkgit:git-fast-import[1] + is below this limit, then the objects will be unpacked into + loose object files. However if the number of imported objects + equals or exceeds this limit then the pack will be stored as a + pack. Storing the pack from a fast-import can make the import + operation complete faster, especially on slow filesystems. If + not set, the value of `transfer.unpackLimit` is used instead. + fetch.recurseSubmodules:: This option can be either set to a boolean value or to 'on-demand'. Setting it to a boolean changes the behavior of fetch and pull to diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index 66910aa2fa..644df993f9 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -136,6 +136,8 @@ Performance and Compression Tuning Maximum size of each output packfile. The default is unlimited. +fastimport.unpackLimit:: + See linkgit:git-config[1] Performance ----------- diff --git a/fast-import.c b/fast-import.c index c504ef752d..59630cee14 100644 --- a/fast-import.c +++ b/fast-import.c @@ -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<next) + if (e->pack_id == id) + e->pack_id = MAX_PACK_ID; + } + + for (lu = 0; lu < branch_table_sz; lu++) { + struct branch *b; + + for (b = branch_table[lu]; b; b = b->table_next_branch) + if (b->pack_id == id) + b->pack_id = MAX_PACK_ID; + } + + for (t = first_tag; t; t = t->next_tag) + if (t->pack_id == id) + t->pack_id = MAX_PACK_ID; +} + static unsigned int hc_str(const char *s, size_t len) { unsigned int r = 0; @@ -951,6 +980,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; @@ -973,6 +1019,14 @@ 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)) { + invalidate_pack_id(pack_id); + goto discard_pack; + } + } + close(pack_data->pack_fd); idx_name = keep_pack(create_index()); @@ -1003,6 +1057,7 @@ static void end_packfile(void) pack_id++; } else { +discard_pack: close(pack_data->pack_fd); unlink_or_warn(pack_data->pack_name); } @@ -3320,6 +3375,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)) { @@ -3344,6 +3400,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); } diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 4bca35c259..74d740de41 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -52,6 +52,7 @@ echo "$@"' ### test_expect_success 'empty stream succeeds' ' + git config fastimport.unpackLimit 0 && git fast-import >input && test_create_repo R && + git --git-dir=R/.git config fastimport.unpackLimit 0 && git --git-dir=R/.git fast-import --big-file-threshold=1 input <<-INPUT_END && + commit refs/heads/master + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <input <<-INPUT_END && + commit refs/heads/master + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data < $GIT_COMMITTER_DATE + data <&2 "checkpoint did not update branch" + exit 1 + else + n=$(($n + 1)) + fi && + sleep 1 && + from=$(git rev-parse refs/heads/master^0) + done && + cat <<-INPUT_END && + commit refs/heads/master + committer $id + data <