nd/repack-keep-pack update
cover nd/repack-keep-pack update
This is basically a resend from the last round but rebased on 'master'. The old base results in some conflicts with packfile and oid conversion series. This one should reduce merge conflicts on 'pu' to trivial ones.
Nguyễn Thái Ngọc Duy (7):
t7700: have closing quote of a test at the beginning of line
repack: add --keep-pack option
gc: add --keep-largest-pack option
gc: add gc.bigPackThreshold config
gc: handle a corner case in gc.bigPackThreshold
gc --auto: exclude base pack if not enough mem to "repack -ad"
pack-objects: show some progress when counting kept objects
Documentation/config.txt | 12 +++ Documentation/git-gc.txt | 19 +++- Documentation/git-pack-objects.txt | 9 +- Documentation/git-repack.txt | 9 +- builtin/gc.c | 165 +++++++++++++++++++++++++++-- builtin/pack-objects.c | 83 +++++++++++---- builtin/repack.c | 21 +++- config.mak.uname | 1 + git-compat-util.h | 4 + object-store.h | 1 + pack-objects.h | 2 + t/t6500-gc.sh | 32 ++++++ t/t7700-repack.sh | 27 ++++- 13 files changed, 349 insertions(+), 36 deletions(-)
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH 1/7 t7700: have closing quote of a test at the beginning of line
The closing quote of a test body by convention is always at the start of line.
--- t/t7700-repack.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
t/t7700-repack.sh +1 -1
@@ -194,7 +194,7 @@ test_expect_success 'objects made unreachable by grafts only are kept' ' git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all && git repack -a -d && git cat-file -t $H1 - ' +' test_done
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH 2/7 repack: add --keep-pack option
In reply to Nguyễn Thái Ngọc DuyWe allow to keep existing packs by having companion .keep files. This is helpful when a pack is permanently kept. In the next patch, git-gc just wants to keep a pack temporarily, for one pack-objects run. git-gc can use --keep-pack for this use case.
A note about why the pack_keep field cannot be reused and pack_keep_in_core has to be added. This is about the case when --keep-pack is specified together with either --keep-unreachable or --unpack-unreachable, but --honor-pack-keep is NOT specified.
In this case, we want to exclude objects from the packs specified on command line, not from ones with .keep files. If only one bit flag is used, we have to clear pack_keep on pack files with the .keep file.
But we can't make any assumption about unreachable objects in .keep packs. If "pack_keep" field is false for .keep packs, we could potentially pull lots of unreachable objects into the new pack, or unpack them loose. The safer approach is ignore all packs with either .keep file or --keep-pack.
--- Documentation/git-pack-objects.txt | 9 ++++- Documentation/git-repack.txt | 9 ++++- builtin/pack-objects.c | 63 ++++++++++++++++++++++++------ builtin/repack.c | 21 ++++++++-- object-store.h | 1 + t/t7700-repack.sh | 25 ++++++++++++ 6 files changed, 110 insertions(+), 18 deletions(-)
Documentation/git-pack-objects.txt +8 -1
@@ -12,7 +12,7 @@ SYNOPSIS 'git pack-objects' [-q | --progress | --all-progress] [--all-progress-implied] [--no-reuse-delta] [--delta-base-offset] [--non-empty] [--local] [--incremental] [--window=<n>] [--depth=<n>] - [--revs [--unpacked | --all]] + [--revs [--unpacked | --all]] [--keep-pack=<pack-name>] [--stdout [--filter=<filter-spec>] | base-name] [--shallow] [--keep-true-parents] < object-list
@@ -126,6 +126,13 @@ base-name:: has a .keep file to be ignored, even if it would have otherwise been packed. +--keep-pack=<pack-name>:: + This flag causes an object already in the given pack to be + ignored, even if it would have otherwise been + packed. `<pack-name>` is the the pack file name without + leading directory (e.g. `pack-123.pack`). The option could be + specified multiple times to keep multiple packs. + --incremental:: This flag causes an object already in a pack to be ignored even if it would have otherwise been packed.
Documentation/git-repack.txt +8 -1
@@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository SYNOPSIS -------- [verse] -'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>] +'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>] DESCRIPTION -----------
@@ -133,6 +133,13 @@ other objects in that pack they already have locally. with `-b` or `repack.writeBitmaps`, as it ensures that the bitmapped packfile has the necessary objects. +--keep-pack=<pack-name>:: + Exclude the given pack from repacking. This is the equivalent + of having `.keep` file on the pack. `<pack-name>` is the the + pack file name without leading directory (e.g. `pack-123.pack`). + The option could be specified multiple times to keep multiple + packs. + --unpack-unreachable=<when>:: When loosening unreachable objects, do not bother loosening any objects older than `<when>`. This can be used to optimize out
builtin/pack-objects.c +50 -13
143 lines not on this page · raw message
builtin/repack.c +18 -3
@@ -86,7 +86,8 @@ static void remove_pack_on_signal(int signo)* have a corresponding .keep or .promisor file. These packs are not to * be kept if we are going to pack everything into one file. */ -static void get_non_kept_pack_filenames (struct string_list *fname_list) +static void get_non_kept_pack_filenames (struct string_list *fname_list, +const struct string_list *extra_keep) {DIR *dir;struct dirent *e;
@@ -97,6 +98,14 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)while ((e =readdir (dir)) !=NULL ) {size_t len; +int i; + +for (i =0 ; i < extra_keep->nr; i++) +if (!fspathcmp (e->d_name, extra_keep->items[i].string)) +break ; +if (extra_keep->nr >0 && i < extra_keep->nr) +continue ; +if (!strip_suffix (e->d_name,".pack" , &len))continue ;
@@ -148,7 +157,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)struct string_list rollback =STRING_LIST_INIT_NODUP ;struct string_list existing_packs =STRING_LIST_INIT_DUP ;struct strbuf line =STRBUF_INIT ; -int ext, ret, failed; +int i, ext, ret, failed;FILE *out;/* variables to be filled by option parsing */
@@ -160,6 +169,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)const char *depth =NULL ;const char *threads =NULL ;const char *max_pack_size =NULL ; +struct string_list keep_pack_list =STRING_LIST_INIT_NODUP ;int no_reuse_delta =0 , no_reuse_object =0 ;int no_update_server_info =0 ;int quiet =0 ;
@@ -200,6 +210,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("maximum size of each packfile")),OPT_BOOL (0 ,"pack-kept-objects" , &pack_kept_objects,N_ ("repack objects in packs marked with .keep" )), +OPT_STRING_LIST (0 ,"keep-pack" , &keep_pack_list,N_ ("name" ), +N_ ("do not repack this pack" )), OPT_END() };
@@ -230,6 +242,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)argv_array_push (&cmd.args,"--keep-true-parents" );if (!pack_kept_objects)argv_array_push (&cmd.args,"--honor-pack-keep" ); +for (i =0 ; i < keep_pack_list.nr; i++) +argv_array_pushf (&cmd.args,"--keep-pack=%s" , + keep_pack_list.items[i].string);argv_array_push (&cmd.args,"--non-empty" );argv_array_push (&cmd.args,"--all" );argv_array_push (&cmd.args,"--reflog" );
@@ -254,7 +269,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)argv_array_push (&cmd.args,"--write-bitmap-index" );if (pack_everything &ALL_INTO_ONE ) { -get_non_kept_pack_filenames (&existing_packs); +get_non_kept_pack_filenames (&existing_packs, &keep_pack_list);if (existing_packs.nr && delete_redundant) {if (unpack_unreachable) {
object-store.h +1 -0
@@ -71,6 +71,7 @@ struct packed_git {int pack_fd; unsigned pack_local:1, pack_keep:1, + pack_keep_in_core:1, freshened:1, do_not_close:1, pack_promisor:1;
t/t7700-repack.sh +25 -0
@@ -4,6 +4,12 @@ test_description='git repack works correctly' . ./test-lib.sh +commit_and_pack() { + test_commit "$@" >/dev/null && + SHA1=$(git pack-objects --all --unpacked --incremental .git/objects/pack/pack </dev/null) && + echo pack-${SHA1}.pack +} + test_expect_success 'objects in packs marked .keep are not repacked' ' echo content1 > file1 && echo content2 > file2 &&
@@ -196,5 +202,24 @@ test_expect_success 'objects made unreachable by grafts only are kept' ' git cat-file -t $H1 ' +test_expect_success 'repack --keep-pack' ' + test_create_repo keep-pack && + ( + cd keep-pack && + P1=$(commit_and_pack 1) && + P2=$(commit_and_pack 2) && + P3=$(commit_and_pack 3) && + P4=$(commit_and_pack 4) && + ls .git/objects/pack/*.pack >old-counts && + test_line_count = 4 old-counts && + git repack -a -d --keep-pack $P1 --keep-pack $P4 && + ls .git/objects/pack/*.pack >new-counts && + grep -q $P1 new-counts && + grep -q $P4 new-counts && + test_line_count = 3 new-counts && + git fsck + ) +' + test_done
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH 3/7 gc: add --keep-largest-pack option
In reply to Nguyễn Thái Ngọc DuyThis adds a new repack mode that combines everything into a secondary pack, leaving the largest pack alone.
This could help reduce memory pressure. On linux-2.6.git, valgrind massif reports 1.6GB heap in "pack all" case, and 535MB in "pack all except the base pack" case. We save roughly 1GB memory by excluding the base pack.
This should also lower I/O because we don't have to rewrite a giant pack every time (e.g. for linux-2.6.git that's a 1.4GB pack file)..
PS. The use of string_list here seems overkill, but we'll need it in the next patch...
--- Documentation/git-gc.txt | 6 +++++- builtin/gc.c | 45 ++++++++++++++++++++++++++++++++++++---- t/t6500-gc.sh | 25 ++++++++++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-)
Documentation/git-gc.txt +5 -1
@@ -9,7 +9,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository SYNOPSIS -------- [verse] -'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] [--force] +'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] [--force] [--keep-largest-pack] DESCRIPTION -----------
@@ -84,6 +84,10 @@ be performed as well. Force `git gc` to run even if there may be another `git gc` instance running on this repository. +--keep-largest-pack:: + All packs except the largest pack and those marked with a + `.keep` files are consolidated into a single pack. + Configuration -------------
builtin/gc.c +41 -4
87 lines not on this page · raw message
t/t6500-gc.sh +25 -0
@@ -43,6 +43,31 @@ test_expect_success 'gc is not aborted due to a stale symref' ' ) ' +test_expect_success 'gc --keep-largest-pack' ' + test_create_repo keep-pack && + ( + cd keep-pack && + test_commit one && + test_commit two && + test_commit three && + git gc && + ( cd .git/objects/pack && ls *.pack ) >pack-list && + test_line_count = 1 pack-list && + BASE_PACK=.git/objects/pack/pack-*.pack && + test_commit four && + git repack -d && + test_commit five && + git repack -d && + ( cd .git/objects/pack && ls *.pack ) >pack-list && + test_line_count = 3 pack-list && + git gc --keep-largest-pack && + ( cd .git/objects/pack && ls *.pack ) >pack-list && + test_line_count = 2 pack-list && + test_path_is_file $BASE_PACK && + git fsck + ) +' + test_expect_success 'auto gc with too many loose objects does not attempt to create bitmaps' ' test_config gc.auto 3 && test_config gc.autodetach false &&
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH 4/7 gc: add gc.bigPackThreshold config
In reply to Nguyễn Thái Ngọc DuyThe --keep-largest-pack option is not very convenient to use because you need to tell gc to do this explicitly (and probably on just a few large repos).
Add a config key that enables this mode when packs larger than a limit are found. Note that there's a slight behavior difference compared to --keep-largest-pack: all packs larger than the threshold are kept, not just the largest one.
--- Documentation/config.txt | 8 ++++++++ Documentation/git-gc.txt | 6 ++++-- builtin/gc.c | 26 ++++++++++++++++++++------ 3 files changed, 32 insertions(+), 8 deletions(-)
Documentation/config.txt +8 -0
@@ -1558,6 +1558,14 @@ gc.autoDetach:: Make `git gc --auto` return immediately and run in background if the system supports it. Default is true. +gc.bigPackThreshold:: + If non-zero, all packs larger than this limit are kept when + `git gc` is run. This is very similar to `--keep-base-pack` + except that all packs that meet the threshold are kept, not + just the base pack. Defaults to zero. Common unit suffixes of + 'k', 'm', or 'g' are supported. + + gc.logExpiry:: If the file gc.log exists, then `git gc --auto` won't run unless that file is more than 'gc.logExpiry' old. Default is
Documentation/git-gc.txt +4 -2
@@ -56,7 +56,8 @@ single pack using `git repack -d -l`. Setting the value of `gc.auto` to 0 disables automatic packing of loose objects. + If the number of packs exceeds the value of `gc.autoPackLimit`, -then existing packs (except those marked with a `.keep` file) +then existing packs (except those marked with a `.keep` file +or over `gc.bigPackThreshold` limit) are consolidated into a single pack by using the `-A` option of 'git repack'. Setting `gc.autoPackLimit` to 0 disables automatic consolidation of packs.
@@ -86,7 +87,8 @@ be performed as well. --keep-largest-pack:: All packs except the largest pack and those marked with a - `.keep` files are consolidated into a single pack. + `.keep` files are consolidated into a single pack. When this + option is used, `gc.bigPackThreshold` is ignored. Configuration -------------
builtin/gc.c +20 -6
@@ -41,6 +41,7 @@ static timestamp_t gc_log_expire_time;static const char *gc_log_expire ="1.day.ago" ;static const char *prune_expire ="2.weeks.ago" ;static const char *prune_worktrees_expire ="3.months.ago" ; +static unsigned long big_pack_threshold;static struct argv_array pack_refs_cmd =ARGV_ARRAY_INIT ;static struct argv_array reflog =ARGV_ARRAY_INIT ;
@@ -128,6 +129,8 @@ static void gc_config(void)git_config_get_expiry ("gc.worktreepruneexpire" , &prune_worktrees_expire);git_config_get_expiry ("gc.logexpiry" , &gc_log_expire); +git_config_get_ulong ("gc.bigpackthreshold" , &big_pack_threshold); +git_config (git_default_config,NULL ); }
@@ -166,14 +169,17 @@ static int too_many_loose_objects(void)return needed; } -static void find_base_packs (struct string_list *packs) +static void find_base_packs (struct string_list *packs,unsigned long limit) {struct packed_git *p, *base =NULL ;for (p =get_packed_git (the_repository); p; p = p->next) {if (!p->pack_local)continue ; -if (!base || base->pack_size < p->pack_size) { +if (limit) { +if (p->pack_size >= limit) +string_list_append (packs, p->pack_name); + }else if (!base || base->pack_size < p->pack_size) { base = p; } }
@@ -244,9 +250,15 @@ static int need_to_gc(void)* we run "repack -A -d -l". Otherwise we tell the caller * there is no need. */ -if (too_many_packs ()) -add_repack_all_option (NULL ); -else if (too_many_loose_objects ()) +if (too_many_packs ()) { +struct string_list keep_pack =STRING_LIST_INIT_NODUP ; + +if (big_pack_threshold) +find_base_packs (&keep_pack, big_pack_threshold); + +add_repack_all_option (&keep_pack); +string_list_clear (&keep_pack,0 ); + }else if (too_many_loose_objects ())add_repack_incremental_option ();else return 0 ;
@@ -464,7 +476,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)if (keep_base_pack !=-1 ) {if (keep_base_pack) -find_base_packs (&keep_pack); +find_base_packs (&keep_pack,0 ); + }else if (big_pack_threshold) { +find_base_packs (&keep_pack, big_pack_threshold); }add_repack_all_option (&keep_pack);
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH 5/7 gc: handle a corner case in gc.bigPackThreshold
In reply to Nguyễn Thái Ngọc DuyThis config allows us to keep <N> packs back if their size is larger than a limit. But if this N >= gc.autoPackLimit, we may have a problem. We are supposed to reduce the number of packs after a threshold because it affects performance.
We could tell the user that they have incompatible gc.bigPackThreshold and gc.autoPackLimit, but it's kinda hard when 'git gc --auto' runs in background. Instead let's fall back to the next best stategy: try to reduce the number of packs anyway, but keep the base pack out. This reduces the number of packs to two and hopefully won't take up too much resources to repack (the assumption still is the base pack takes most resources to handle).
--- Documentation/config.txt | 6 +++++- builtin/gc.c | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-)
Documentation/config.txt +5 -1
@@ -1564,7 +1564,11 @@ gc.bigPackThreshold:: except that all packs that meet the threshold are kept, not just the base pack. Defaults to zero. Common unit suffixes of 'k', 'm', or 'g' are supported. - ++ +Note that if the number of kept packs is more than gc.autoPackLimit, +this configuration variable is ignored, all packs except the base pack +will be repacked. After this the number of packs should go below +gc.autoPackLimit and gc.bigPackThreshold should be respected again. gc.logExpiry:: If the file gc.log exists, then `git gc --auto` won't run
builtin/gc.c +7 -1
@@ -253,8 +253,14 @@ static int need_to_gc(void)if (too_many_packs ()) {struct string_list keep_pack =STRING_LIST_INIT_NODUP ; -if (big_pack_threshold) +if (big_pack_threshold) {find_base_packs (&keep_pack, big_pack_threshold); +if (keep_pack.nr >= gc_auto_pack_limit) { + big_pack_threshold =0 ; +string_list_clear (&keep_pack,0 ); +find_base_packs (&keep_pack,0 ); + } + }add_repack_all_option (&keep_pack);string_list_clear (&keep_pack,0 );
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH 6/7 gc --auto: exclude base pack if not enough mem to "repack -ad"
In reply to Nguyễn Thái Ngọc Duypack-objects could be a big memory hog especially on large repos, everybody knows that. The suggestion to stick a .keep file on the giant base pack to avoid this problem is also known for a long time.
Recent patches add an option to do just this, but it has to be either configured or activated manually. This patch lets git gc --auto activate this mode automatically when it thinks repack -ad will use a lot of memory and start affecting the system due to swapping or flushing OS cache.
gc --auto decides to do this based on an estimation of pack-objects memory usage, which is quite accurate at least for the heap part, and whether that fits in half of system memory (the assumption here is for desktop environment where there are many other applications running).
This mechanism only kicks in if gc.bigBasePackThreshold is not configured. If it is, it is assumed that the user already knows what they want.
--- Documentation/git-gc.txt | 9 +++- builtin/gc.c | 98 +++++++++++++++++++++++++++++++++++++++- builtin/pack-objects.c | 2 +- config.mak.uname | 1 + git-compat-util.h | 4 ++ pack-objects.h | 2 + t/t6500-gc.sh | 7 +++ 7 files changed, 119 insertions(+), 4 deletions(-)
Documentation/git-gc.txt +7 -2
@@ -59,8 +59,13 @@ If the number of packs exceeds the value of `gc.autoPackLimit`, then existing packs (except those marked with a `.keep` file or over `gc.bigPackThreshold` limit) are consolidated into a single pack by using the `-A` option of -'git repack'. Setting `gc.autoPackLimit` to 0 disables -automatic consolidation of packs. +'git repack'. +If the amount of memory is estimated not enough for `git repack` to +run smoothly and `gc.bigPackThreshold` is not set, the largest +pack will also be excluded (this is the equivalent of running `git gc` +with `--keep-base-pack`). +Setting `gc.autoPackLimit` to 0 disables automatic consolidation of +packs. + If houskeeping is required due to many loose objects or packs, all other housekeeping tasks (e.g. rerere, working trees, reflog...) will
builtin/gc.c +97 -1
140 lines not on this page · raw message
builtin/pack-objects.c +1 -1
@@ -82,7 +82,7 @@ static uint16_t write_bitmap_options;static int exclude_promisor_objects;static unsigned long delta_cache_size =0 ; -static unsigned long max_delta_cache_size =256 *1024 *1024 ; +static unsigned long max_delta_cache_size =DEFAULT_DELTA_CACHE_SIZE ;static unsigned long cache_max_small_delta_size =1000 ;static unsigned long window_memory_limit =0 ;
config.mak.uname +1 -0
@@ -37,6 +37,7 @@ ifeq ($(uname_S),Linux) HAVE_GETDELIM = YesPlease SANE_TEXT_GREP=-a FREAD_READS_DIRECTORIES = UnfortunatelyYes + BASIC_CFLAGS += -DHAVE_SYSINFO endif ifeq ($(uname_S),GNU/kFreeBSD) HAVE_ALLOCA_H = YesPlease
git-compat-util.h +4 -0
@@ -284,6 +284,10 @@ extern char *gitdirname(char *);#include <openssl/err.h> #endif +#ifdef HAVE_SYSINFO +# include <sys/sysinfo.h> +#endif +/* On most systems <netdb.h> would have given us this, but * not on some systems (e.g. z/OS). */
pack-objects.h +2 -0
@@ -1,6 +1,8 @@ #ifndef PACK_OBJECTS_H #define PACK_OBJECTS_H +#define DEFAULT_DELTA_CACHE_SIZE (256 * 1024 * 1024) + struct object_entry { struct pack_idx_entry idx; unsigned long size; /* uncompressed size */
t/t6500-gc.sh +7 -0
@@ -5,6 +5,13 @@ test_description='basic git gc tests . ./test-lib.sh +test_expect_success 'setup' ' + # do not let the amount of physical memory affects gc + # behavior, make sure we always pack everything to one pack by + # default + git config gc.bigPackThreshold 2g +' + test_expect_success 'gc empty repository' ' git gc '
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH 7/7 pack-objects: show some progress when counting kept objects
In reply to Nguyễn Thái Ngọc DuyWe only show progress when there are new objects to be packed. But when --keep-pack is specified on the base pack, we will exclude most of objects. This makes 'pack-objects' stay silent for a long time while the counting phase is going.
Let's show some progress whenever we visit an object instead. The old "Counting objects" is renamed to "Enumerating objects" and a new progress "Counting objects" line is added.
This new "Counting objects" line should progress pretty quick when the system is beefy. But when the system is under pressure, the reading object header done in this phase could be slow and showing progress is an improvement over staying silent in the current code.
--- builtin/pack-objects.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
builtin/pack-objects.c +12 -6
@@ -46,7 +46,7 @@ static const char *pack_usage[] = {static struct packing_data to_pack;static struct pack_idx_entry **written_list; -static uint32_t nr_result, nr_written; +static uint32_t nr_result, nr_written, nr_seen;static int non_empty;static int reuse_delta =1 , reuse_object =1 ;
@@ -1096,6 +1096,8 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,off_t found_offset =0 ;uint32_t index_pos; +display_progress (progress_state, ++nr_seen); +if (have_duplicate_entry (oid, exclude, &index_pos))return 0 ;
@@ -1111,8 +1113,6 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,create_object_entry (oid, type,pack_name_hash (name), exclude, name &&no_try_delta (name), index_pos, found_pack, found_offset); - -display_progress (progress_state, nr_result);return 1 ; }
@@ -1123,6 +1123,8 @@ static int add_object_entry_from_bitmap(const struct object_id *oid, {uint32_t index_pos; +display_progress (progress_state, ++nr_seen); +if (have_duplicate_entry (oid,0 , &index_pos))return 0 ;
@@ -1130,8 +1132,6 @@ static int add_object_entry_from_bitmap(const struct object_id *oid,return 0 ;create_object_entry (oid, type, name_hash,0 ,0 , index_pos, pack, offset); - -display_progress (progress_state, nr_result);return 1 ; }
@@ -1716,6 +1716,10 @@ static void get_object_details(void)uint32_t i;struct object_entry **sorted_by_offset; +if (progress) + progress_state =start_progress (_ ("Counting objects" ), + to_pack.nr_objects); + sorted_by_offset =xcalloc (to_pack.nr_objects,sizeof (struct object_entry *));for (i =0 ; i < to_pack.nr_objects; i++) sorted_by_offset[i] = to_pack.objects + i;
@@ -1726,7 +1730,9 @@ static void get_object_details(void)check_object (entry);if (big_file_threshold < entry->size) entry->no_try_delta =1 ; +display_progress (progress_state, i +1 ); } +stop_progress (&progress_state);/* * This must happen in a second pass, since we rely on the delta
@@ -3209,7 +3215,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) }if (progress) - progress_state =start_progress (_ ("Counting objects" ),0 ); + progress_state =start_progress (_ ("Enumerating objects" ),0 );if (!use_internal_rev_list)read_object_list_from_stdin ();else {
Signature · 1 line
2.17.0.367.g5dd2e386c3
Quoted 4 lines · Nguyễn Thái Ngọc Duy
This is basically a resend from the last round but rebased on 'master'. The old base results in some conflicts with packfile and oid conversion series. This one should reduce merge conflicts on 'pu' to trivial ones.
Thanks. I've been running this at work and as noted in
https://public-inbox.org/git/[email protected]/ it's had big performance impact to the better, users even started noticing it (they'd previously get noticeable slowdowns while doing other task on GC).
I also tried to see just how much worse this was making performance, my hunch was that the difference should be trivial but noticeable since we'll produce a less efficient pack.
What I found was the opposite, under real-world conditions it seems to be making things 1-2% better on common git operations, which I suspect is because once we've done a few pulls and coalesced those into their own pack(s) there's more cache locality for the data we're actually looking at.
I.e. once you've got a repo has a big pack you're not touching, and a few weeks of updates from upstream that you've coalesced into another pack there's a higher density of stuff you care about near HEAD per FS page in the recent smaller pack, which if you're pressed for memory and parts of your pack are getting paged out of the FS cache is a win. I haven't confirmed that, it's just a hypothesis.
The only (trivial) issue I found in the patches themselves was that between 4/5 and 5/7 you're adding an empty line to config.txt in 4/7 just to erase it in 5/7, better not to add it to begin with, but hopefully Junio can fix that up (if he cares).
Quoted 5 lines · Ævar Arnfjörð Bjarmason
The only (trivial) issue I found in the patches themselves was that between 4/5 and 5/7 you're adding an empty line to config.txt in 4/7 just to erase it in 5/7, better not to add it to begin with, but hopefully Junio can fix that up (if he cares).
I do care but I'd wish I do not have to waste time and concentration to spend on doing so, even though I would be fully capable of skipping this round and remembering to queue a rerolled one.
I've seen mentions like the above one a few times on the list recently, so let me make it clear. Some things are easier to tweak locally than others, and I'd rather not to waste my time on cleaning other people's mess. A simple typofix that does not cascade through to later steps in a series is one thing. A tweak that changes number of lines in a hunk that forces a later step to compensate is more involved.
Don't expect your traffic cop to wash your care while you're stopping at a red signal.
v2 cover nd/repack-keep-pack update
In reply to Nguyễn Thái Ngọc DuyQuoted 4 lines · Ævar Arnfjörð Bjarmason
The only (trivial) issue I found in the patches themselves was that between 4/5 and 5/7 you're adding an empty line to config.txt in 4/7 just to erase it in 5/7, better not to add it to begin with, but hopefully Junio can fix that up (if he cares).
Fixed (and is the only change in v2) in case Junio has not picked up the last round and fixed it himself yet.
Nguyễn Thái Ngọc Duy (7):
t7700: have closing quote of a test at the beginning of line
repack: add --keep-pack option
gc: add --keep-largest-pack option
gc: add gc.bigPackThreshold config
gc: handle a corner case in gc.bigPackThreshold
gc --auto: exclude base pack if not enough mem to "repack -ad"
pack-objects: show some progress when counting kept objects
Documentation/config.txt | 12 +++ Documentation/git-gc.txt | 19 +++- Documentation/git-pack-objects.txt | 9 +- Documentation/git-repack.txt | 9 +- builtin/gc.c | 165 +++++++++++++++++++++++++++-- builtin/pack-objects.c | 83 +++++++++++---- builtin/repack.c | 21 +++- config.mak.uname | 1 + git-compat-util.h | 4 + object-store.h | 1 + pack-objects.h | 2 + t/t6500-gc.sh | 32 ++++++ t/t7700-repack.sh | 27 ++++- 13 files changed, 349 insertions(+), 36 deletions(-)
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH v2 1/7 t7700: have closing quote of a test at the beginning of line
The closing quote of a test body by convention is always at the start of line.
--- t/t7700-repack.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
t/t7700-repack.sh +1 -1
@@ -194,7 +194,7 @@ test_expect_success 'objects made unreachable by grafts only are kept' ' git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all && git repack -a -d && git cat-file -t $H1 - ' +' test_done
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH v2 2/7 repack: add --keep-pack option
In reply to Nguyễn Thái Ngọc DuyWe allow to keep existing packs by having companion .keep files. This is helpful when a pack is permanently kept. In the next patch, git-gc just wants to keep a pack temporarily, for one pack-objects run. git-gc can use --keep-pack for this use case.
A note about why the pack_keep field cannot be reused and pack_keep_in_core has to be added. This is about the case when --keep-pack is specified together with either --keep-unreachable or --unpack-unreachable, but --honor-pack-keep is NOT specified.
In this case, we want to exclude objects from the packs specified on command line, not from ones with .keep files. If only one bit flag is used, we have to clear pack_keep on pack files with the .keep file.
But we can't make any assumption about unreachable objects in .keep packs. If "pack_keep" field is false for .keep packs, we could potentially pull lots of unreachable objects into the new pack, or unpack them loose. The safer approach is ignore all packs with either .keep file or --keep-pack.
--- Documentation/git-pack-objects.txt | 9 ++++- Documentation/git-repack.txt | 9 ++++- builtin/pack-objects.c | 63 ++++++++++++++++++++++++------ builtin/repack.c | 21 ++++++++-- object-store.h | 1 + t/t7700-repack.sh | 25 ++++++++++++ 6 files changed, 110 insertions(+), 18 deletions(-)
Documentation/git-pack-objects.txt +8 -1
@@ -12,7 +12,7 @@ SYNOPSIS 'git pack-objects' [-q | --progress | --all-progress] [--all-progress-implied] [--no-reuse-delta] [--delta-base-offset] [--non-empty] [--local] [--incremental] [--window=<n>] [--depth=<n>] - [--revs [--unpacked | --all]] + [--revs [--unpacked | --all]] [--keep-pack=<pack-name>] [--stdout [--filter=<filter-spec>] | base-name] [--shallow] [--keep-true-parents] < object-list
@@ -126,6 +126,13 @@ base-name:: has a .keep file to be ignored, even if it would have otherwise been packed. +--keep-pack=<pack-name>:: + This flag causes an object already in the given pack to be + ignored, even if it would have otherwise been + packed. `<pack-name>` is the the pack file name without + leading directory (e.g. `pack-123.pack`). The option could be + specified multiple times to keep multiple packs. + --incremental:: This flag causes an object already in a pack to be ignored even if it would have otherwise been packed.
Documentation/git-repack.txt +8 -1
@@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository SYNOPSIS -------- [verse] -'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>] +'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>] DESCRIPTION -----------
@@ -133,6 +133,13 @@ other objects in that pack they already have locally. with `-b` or `repack.writeBitmaps`, as it ensures that the bitmapped packfile has the necessary objects. +--keep-pack=<pack-name>:: + Exclude the given pack from repacking. This is the equivalent + of having `.keep` file on the pack. `<pack-name>` is the the + pack file name without leading directory (e.g. `pack-123.pack`). + The option could be specified multiple times to keep multiple + packs. + --unpack-unreachable=<when>:: When loosening unreachable objects, do not bother loosening any objects older than `<when>`. This can be used to optimize out
builtin/pack-objects.c +50 -13
143 lines not on this page · raw message
builtin/repack.c +18 -3
@@ -86,7 +86,8 @@ static void remove_pack_on_signal(int signo)* have a corresponding .keep or .promisor file. These packs are not to * be kept if we are going to pack everything into one file. */ -static void get_non_kept_pack_filenames (struct string_list *fname_list) +static void get_non_kept_pack_filenames (struct string_list *fname_list, +const struct string_list *extra_keep) {DIR *dir;struct dirent *e;
@@ -97,6 +98,14 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)while ((e =readdir (dir)) !=NULL ) {size_t len; +int i; + +for (i =0 ; i < extra_keep->nr; i++) +if (!fspathcmp (e->d_name, extra_keep->items[i].string)) +break ; +if (extra_keep->nr >0 && i < extra_keep->nr) +continue ; +if (!strip_suffix (e->d_name,".pack" , &len))continue ;
@@ -148,7 +157,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)struct string_list rollback =STRING_LIST_INIT_NODUP ;struct string_list existing_packs =STRING_LIST_INIT_DUP ;struct strbuf line =STRBUF_INIT ; -int ext, ret, failed; +int i, ext, ret, failed;FILE *out;/* variables to be filled by option parsing */
@@ -160,6 +169,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)const char *depth =NULL ;const char *threads =NULL ;const char *max_pack_size =NULL ; +struct string_list keep_pack_list =STRING_LIST_INIT_NODUP ;int no_reuse_delta =0 , no_reuse_object =0 ;int no_update_server_info =0 ;int quiet =0 ;
@@ -200,6 +210,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("maximum size of each packfile")),OPT_BOOL (0 ,"pack-kept-objects" , &pack_kept_objects,N_ ("repack objects in packs marked with .keep" )), +OPT_STRING_LIST (0 ,"keep-pack" , &keep_pack_list,N_ ("name" ), +N_ ("do not repack this pack" )), OPT_END() };
@@ -230,6 +242,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)argv_array_push (&cmd.args,"--keep-true-parents" );if (!pack_kept_objects)argv_array_push (&cmd.args,"--honor-pack-keep" ); +for (i =0 ; i < keep_pack_list.nr; i++) +argv_array_pushf (&cmd.args,"--keep-pack=%s" , + keep_pack_list.items[i].string);argv_array_push (&cmd.args,"--non-empty" );argv_array_push (&cmd.args,"--all" );argv_array_push (&cmd.args,"--reflog" );
@@ -254,7 +269,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)argv_array_push (&cmd.args,"--write-bitmap-index" );if (pack_everything &ALL_INTO_ONE ) { -get_non_kept_pack_filenames (&existing_packs); +get_non_kept_pack_filenames (&existing_packs, &keep_pack_list);if (existing_packs.nr && delete_redundant) {if (unpack_unreachable) {
object-store.h +1 -0
@@ -71,6 +71,7 @@ struct packed_git {int pack_fd; unsigned pack_local:1, pack_keep:1, + pack_keep_in_core:1, freshened:1, do_not_close:1, pack_promisor:1;
t/t7700-repack.sh +25 -0
@@ -4,6 +4,12 @@ test_description='git repack works correctly' . ./test-lib.sh +commit_and_pack() { + test_commit "$@" >/dev/null && + SHA1=$(git pack-objects --all --unpacked --incremental .git/objects/pack/pack </dev/null) && + echo pack-${SHA1}.pack +} + test_expect_success 'objects in packs marked .keep are not repacked' ' echo content1 > file1 && echo content2 > file2 &&
@@ -196,5 +202,24 @@ test_expect_success 'objects made unreachable by grafts only are kept' ' git cat-file -t $H1 ' +test_expect_success 'repack --keep-pack' ' + test_create_repo keep-pack && + ( + cd keep-pack && + P1=$(commit_and_pack 1) && + P2=$(commit_and_pack 2) && + P3=$(commit_and_pack 3) && + P4=$(commit_and_pack 4) && + ls .git/objects/pack/*.pack >old-counts && + test_line_count = 4 old-counts && + git repack -a -d --keep-pack $P1 --keep-pack $P4 && + ls .git/objects/pack/*.pack >new-counts && + grep -q $P1 new-counts && + grep -q $P4 new-counts && + test_line_count = 3 new-counts && + git fsck + ) +' + test_done
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH v2 3/7 gc: add --keep-largest-pack option
In reply to Nguyễn Thái Ngọc DuyThis adds a new repack mode that combines everything into a secondary pack, leaving the largest pack alone.
This could help reduce memory pressure. On linux-2.6.git, valgrind massif reports 1.6GB heap in "pack all" case, and 535MB in "pack all except the base pack" case. We save roughly 1GB memory by excluding the base pack.
This should also lower I/O because we don't have to rewrite a giant pack every time (e.g. for linux-2.6.git that's a 1.4GB pack file)..
PS. The use of string_list here seems overkill, but we'll need it in the next patch...
--- Documentation/git-gc.txt | 6 +++++- builtin/gc.c | 45 ++++++++++++++++++++++++++++++++++++---- t/t6500-gc.sh | 25 ++++++++++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-)
Documentation/git-gc.txt +5 -1
@@ -9,7 +9,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository SYNOPSIS -------- [verse] -'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] [--force] +'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] [--force] [--keep-largest-pack] DESCRIPTION -----------
@@ -84,6 +84,10 @@ be performed as well. Force `git gc` to run even if there may be another `git gc` instance running on this repository. +--keep-largest-pack:: + All packs except the largest pack and those marked with a + `.keep` files are consolidated into a single pack. + Configuration -------------
builtin/gc.c +41 -4
87 lines not on this page · raw message
t/t6500-gc.sh +25 -0
@@ -43,6 +43,31 @@ test_expect_success 'gc is not aborted due to a stale symref' ' ) ' +test_expect_success 'gc --keep-largest-pack' ' + test_create_repo keep-pack && + ( + cd keep-pack && + test_commit one && + test_commit two && + test_commit three && + git gc && + ( cd .git/objects/pack && ls *.pack ) >pack-list && + test_line_count = 1 pack-list && + BASE_PACK=.git/objects/pack/pack-*.pack && + test_commit four && + git repack -d && + test_commit five && + git repack -d && + ( cd .git/objects/pack && ls *.pack ) >pack-list && + test_line_count = 3 pack-list && + git gc --keep-largest-pack && + ( cd .git/objects/pack && ls *.pack ) >pack-list && + test_line_count = 2 pack-list && + test_path_is_file $BASE_PACK && + git fsck + ) +' + test_expect_success 'auto gc with too many loose objects does not attempt to create bitmaps' ' test_config gc.auto 3 && test_config gc.autodetach false &&
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH v2 4/7 gc: add gc.bigPackThreshold config
In reply to Nguyễn Thái Ngọc DuyThe --keep-largest-pack option is not very convenient to use because you need to tell gc to do this explicitly (and probably on just a few large repos).
Add a config key that enables this mode when packs larger than a limit are found. Note that there's a slight behavior difference compared to --keep-largest-pack: all packs larger than the threshold are kept, not just the largest one.
--- Documentation/config.txt | 7 +++++++ Documentation/git-gc.txt | 6 ++++-- builtin/gc.c | 26 ++++++++++++++++++++------ 3 files changed, 31 insertions(+), 8 deletions(-)
Documentation/config.txt +7 -0
@@ -1558,6 +1558,13 @@ gc.autoDetach:: Make `git gc --auto` return immediately and run in background if the system supports it. Default is true. +gc.bigPackThreshold:: + If non-zero, all packs larger than this limit are kept when + `git gc` is run. This is very similar to `--keep-base-pack` + except that all packs that meet the threshold are kept, not + just the base pack. Defaults to zero. Common unit suffixes of + 'k', 'm', or 'g' are supported. + gc.logExpiry:: If the file gc.log exists, then `git gc --auto` won't run unless that file is more than 'gc.logExpiry' old. Default is
Documentation/git-gc.txt +4 -2
@@ -56,7 +56,8 @@ single pack using `git repack -d -l`. Setting the value of `gc.auto` to 0 disables automatic packing of loose objects. + If the number of packs exceeds the value of `gc.autoPackLimit`, -then existing packs (except those marked with a `.keep` file) +then existing packs (except those marked with a `.keep` file +or over `gc.bigPackThreshold` limit) are consolidated into a single pack by using the `-A` option of 'git repack'. Setting `gc.autoPackLimit` to 0 disables automatic consolidation of packs.
@@ -86,7 +87,8 @@ be performed as well. --keep-largest-pack:: All packs except the largest pack and those marked with a - `.keep` files are consolidated into a single pack. + `.keep` files are consolidated into a single pack. When this + option is used, `gc.bigPackThreshold` is ignored. Configuration -------------
builtin/gc.c +20 -6
@@ -41,6 +41,7 @@ static timestamp_t gc_log_expire_time;static const char *gc_log_expire ="1.day.ago" ;static const char *prune_expire ="2.weeks.ago" ;static const char *prune_worktrees_expire ="3.months.ago" ; +static unsigned long big_pack_threshold;static struct argv_array pack_refs_cmd =ARGV_ARRAY_INIT ;static struct argv_array reflog =ARGV_ARRAY_INIT ;
@@ -128,6 +129,8 @@ static void gc_config(void)git_config_get_expiry ("gc.worktreepruneexpire" , &prune_worktrees_expire);git_config_get_expiry ("gc.logexpiry" , &gc_log_expire); +git_config_get_ulong ("gc.bigpackthreshold" , &big_pack_threshold); +git_config (git_default_config,NULL ); }
@@ -166,14 +169,17 @@ static int too_many_loose_objects(void)return needed; } -static void find_base_packs (struct string_list *packs) +static void find_base_packs (struct string_list *packs,unsigned long limit) {struct packed_git *p, *base =NULL ;for (p =get_packed_git (the_repository); p; p = p->next) {if (!p->pack_local)continue ; -if (!base || base->pack_size < p->pack_size) { +if (limit) { +if (p->pack_size >= limit) +string_list_append (packs, p->pack_name); + }else if (!base || base->pack_size < p->pack_size) { base = p; } }
@@ -244,9 +250,15 @@ static int need_to_gc(void)* we run "repack -A -d -l". Otherwise we tell the caller * there is no need. */ -if (too_many_packs ()) -add_repack_all_option (NULL ); -else if (too_many_loose_objects ()) +if (too_many_packs ()) { +struct string_list keep_pack =STRING_LIST_INIT_NODUP ; + +if (big_pack_threshold) +find_base_packs (&keep_pack, big_pack_threshold); + +add_repack_all_option (&keep_pack); +string_list_clear (&keep_pack,0 ); + }else if (too_many_loose_objects ())add_repack_incremental_option ();else return 0 ;
@@ -464,7 +476,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)if (keep_base_pack !=-1 ) {if (keep_base_pack) -find_base_packs (&keep_pack); +find_base_packs (&keep_pack,0 ); + }else if (big_pack_threshold) { +find_base_packs (&keep_pack, big_pack_threshold); }add_repack_all_option (&keep_pack);
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH v2 5/7 gc: handle a corner case in gc.bigPackThreshold
In reply to Nguyễn Thái Ngọc DuyThis config allows us to keep <N> packs back if their size is larger than a limit. But if this N >= gc.autoPackLimit, we may have a problem. We are supposed to reduce the number of packs after a threshold because it affects performance.
We could tell the user that they have incompatible gc.bigPackThreshold and gc.autoPackLimit, but it's kinda hard when 'git gc --auto' runs in background. Instead let's fall back to the next best stategy: try to reduce the number of packs anyway, but keep the base pack out. This reduces the number of packs to two and hopefully won't take up too much resources to repack (the assumption still is the base pack takes most resources to handle).
--- Documentation/config.txt | 5 +++++ builtin/gc.c | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-)
Documentation/config.txt +5 -0
@@ -1564,6 +1564,11 @@ gc.bigPackThreshold:: except that all packs that meet the threshold are kept, not just the base pack. Defaults to zero. Common unit suffixes of 'k', 'm', or 'g' are supported. ++ +Note that if the number of kept packs is more than gc.autoPackLimit, +this configuration variable is ignored, all packs except the base pack +will be repacked. After this the number of packs should go below +gc.autoPackLimit and gc.bigPackThreshold should be respected again. gc.logExpiry:: If the file gc.log exists, then `git gc --auto` won't run
builtin/gc.c +7 -1
@@ -253,8 +253,14 @@ static int need_to_gc(void)if (too_many_packs ()) {struct string_list keep_pack =STRING_LIST_INIT_NODUP ; -if (big_pack_threshold) +if (big_pack_threshold) {find_base_packs (&keep_pack, big_pack_threshold); +if (keep_pack.nr >= gc_auto_pack_limit) { + big_pack_threshold =0 ; +string_list_clear (&keep_pack,0 ); +find_base_packs (&keep_pack,0 ); + } + }add_repack_all_option (&keep_pack);string_list_clear (&keep_pack,0 );
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH v2 6/7 gc --auto: exclude base pack if not enough mem to "repack -ad"
In reply to Nguyễn Thái Ngọc Duypack-objects could be a big memory hog especially on large repos, everybody knows that. The suggestion to stick a .keep file on the giant base pack to avoid this problem is also known for a long time.
Recent patches add an option to do just this, but it has to be either configured or activated manually. This patch lets git gc --auto activate this mode automatically when it thinks repack -ad will use a lot of memory and start affecting the system due to swapping or flushing OS cache.
gc --auto decides to do this based on an estimation of pack-objects memory usage, which is quite accurate at least for the heap part, and whether that fits in half of system memory (the assumption here is for desktop environment where there are many other applications running).
This mechanism only kicks in if gc.bigBasePackThreshold is not configured. If it is, it is assumed that the user already knows what they want.
--- Documentation/git-gc.txt | 9 +++- builtin/gc.c | 98 +++++++++++++++++++++++++++++++++++++++- builtin/pack-objects.c | 2 +- config.mak.uname | 1 + git-compat-util.h | 4 ++ pack-objects.h | 2 + t/t6500-gc.sh | 7 +++ 7 files changed, 119 insertions(+), 4 deletions(-)
Documentation/git-gc.txt +7 -2
@@ -59,8 +59,13 @@ If the number of packs exceeds the value of `gc.autoPackLimit`, then existing packs (except those marked with a `.keep` file or over `gc.bigPackThreshold` limit) are consolidated into a single pack by using the `-A` option of -'git repack'. Setting `gc.autoPackLimit` to 0 disables -automatic consolidation of packs. +'git repack'. +If the amount of memory is estimated not enough for `git repack` to +run smoothly and `gc.bigPackThreshold` is not set, the largest +pack will also be excluded (this is the equivalent of running `git gc` +with `--keep-base-pack`). +Setting `gc.autoPackLimit` to 0 disables automatic consolidation of +packs. + If houskeeping is required due to many loose objects or packs, all other housekeeping tasks (e.g. rerere, working trees, reflog...) will
builtin/gc.c +97 -1
140 lines not on this page · raw message
builtin/pack-objects.c +1 -1
@@ -82,7 +82,7 @@ static uint16_t write_bitmap_options;static int exclude_promisor_objects;static unsigned long delta_cache_size =0 ; -static unsigned long max_delta_cache_size =256 *1024 *1024 ; +static unsigned long max_delta_cache_size =DEFAULT_DELTA_CACHE_SIZE ;static unsigned long cache_max_small_delta_size =1000 ;static unsigned long window_memory_limit =0 ;
config.mak.uname +1 -0
@@ -37,6 +37,7 @@ ifeq ($(uname_S),Linux) HAVE_GETDELIM = YesPlease SANE_TEXT_GREP=-a FREAD_READS_DIRECTORIES = UnfortunatelyYes + BASIC_CFLAGS += -DHAVE_SYSINFO endif ifeq ($(uname_S),GNU/kFreeBSD) HAVE_ALLOCA_H = YesPlease
git-compat-util.h +4 -0
@@ -284,6 +284,10 @@ extern char *gitdirname(char *);#include <openssl/err.h> #endif +#ifdef HAVE_SYSINFO +# include <sys/sysinfo.h> +#endif +/* On most systems <netdb.h> would have given us this, but * not on some systems (e.g. z/OS). */
pack-objects.h +2 -0
@@ -1,6 +1,8 @@ #ifndef PACK_OBJECTS_H #define PACK_OBJECTS_H +#define DEFAULT_DELTA_CACHE_SIZE (256 * 1024 * 1024) + struct object_entry { struct pack_idx_entry idx; unsigned long size; /* uncompressed size */
t/t6500-gc.sh +7 -0
@@ -5,6 +5,13 @@ test_description='basic git gc tests . ./test-lib.sh +test_expect_success 'setup' ' + # do not let the amount of physical memory affects gc + # behavior, make sure we always pack everything to one pack by + # default + git config gc.bigPackThreshold 2g +' + test_expect_success 'gc empty repository' ' git gc '
Signature · 1 line
2.17.0.367.g5dd2e386c3
PATCH v2 7/7 pack-objects: show some progress when counting kept objects
In reply to Nguyễn Thái Ngọc DuyWe only show progress when there are new objects to be packed. But when --keep-pack is specified on the base pack, we will exclude most of objects. This makes 'pack-objects' stay silent for a long time while the counting phase is going.
Let's show some progress whenever we visit an object instead. The old "Counting objects" is renamed to "Enumerating objects" and a new progress "Counting objects" line is added.
This new "Counting objects" line should progress pretty quick when the system is beefy. But when the system is under pressure, the reading object header done in this phase could be slow and showing progress is an improvement over staying silent in the current code.
--- builtin/pack-objects.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
builtin/pack-objects.c +12 -6
@@ -46,7 +46,7 @@ static const char *pack_usage[] = {static struct packing_data to_pack;static struct pack_idx_entry **written_list; -static uint32_t nr_result, nr_written; +static uint32_t nr_result, nr_written, nr_seen;static int non_empty;static int reuse_delta =1 , reuse_object =1 ;
@@ -1096,6 +1096,8 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,off_t found_offset =0 ;uint32_t index_pos; +display_progress (progress_state, ++nr_seen); +if (have_duplicate_entry (oid, exclude, &index_pos))return 0 ;
@@ -1111,8 +1113,6 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,create_object_entry (oid, type,pack_name_hash (name), exclude, name &&no_try_delta (name), index_pos, found_pack, found_offset); - -display_progress (progress_state, nr_result);return 1 ; }
@@ -1123,6 +1123,8 @@ static int add_object_entry_from_bitmap(const struct object_id *oid, {uint32_t index_pos; +display_progress (progress_state, ++nr_seen); +if (have_duplicate_entry (oid,0 , &index_pos))return 0 ;
@@ -1130,8 +1132,6 @@ static int add_object_entry_from_bitmap(const struct object_id *oid,return 0 ;create_object_entry (oid, type, name_hash,0 ,0 , index_pos, pack, offset); - -display_progress (progress_state, nr_result);return 1 ; }
@@ -1716,6 +1716,10 @@ static void get_object_details(void)uint32_t i;struct object_entry **sorted_by_offset; +if (progress) + progress_state =start_progress (_ ("Counting objects" ), + to_pack.nr_objects); + sorted_by_offset =xcalloc (to_pack.nr_objects,sizeof (struct object_entry *));for (i =0 ; i < to_pack.nr_objects; i++) sorted_by_offset[i] = to_pack.objects + i;
@@ -1726,7 +1730,9 @@ static void get_object_details(void)check_object (entry);if (big_file_threshold < entry->size) entry->no_try_delta =1 ; +display_progress (progress_state, i +1 ); } +stop_progress (&progress_state);/* * This must happen in a second pass, since we rely on the delta
@@ -3209,7 +3215,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) }if (progress) - progress_state =start_progress (_ ("Counting objects" ),0 ); + progress_state =start_progress (_ ("Enumerating objects" ),0 );if (!use_internal_rev_list)read_object_list_from_stdin ();else {
Signature · 1 line
2.17.0.367.g5dd2e386c3
Quoted 8 lines · Nguyễn Thái Ngọc Duy
Quoted 4 lines · Ævar Arnfjörð Bjarmason
The only (trivial) issue I found in the patches themselves was that between 4/5 and 5/7 you're adding an empty line to config.txt in 4/7 just to erase it in 5/7, better not to add it to begin with, but hopefully Junio can fix that up (if he cares).
Fixed (and is the only change in v2) in case Junio has not picked up the last round and fixed it himself yet.
Thanks.