Skip to content

Commit 5b92477

Browse files
ttaylorrgitster
authored andcommitted
builtin/gc.c: conditionally avoid pruning objects via loose
Expose the new `git repack --cruft` mode from `git gc` via a new opt-in flag. When invoked like `git gc --cruft`, `git gc` will avoid exploding unreachable objects as loose ones, and instead create a cruft pack and `.mtimes` file. Signed-off-by: Taylor Blau <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ddee370 commit 5b92477

File tree

4 files changed

+65
-8
lines changed

4 files changed

+65
-8
lines changed

Documentation/config/gc.txt

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,21 @@ gc.packRefs::
8181
to enable it within all non-bare repos or it can be set to a
8282
boolean value. The default is `true`.
8383

84+
gc.cruftPacks::
85+
Store unreachable objects in a cruft pack (see
86+
linkgit:git-repack[1]) instead of as loose objects. The default
87+
is `false`.
88+
8489
gc.pruneExpire::
85-
When 'git gc' is run, it will call 'prune --expire 2.weeks.ago'.
86-
Override the grace period with this config variable. The value
87-
"now" may be used to disable this grace period and always prune
88-
unreachable objects immediately, or "never" may be used to
89-
suppress pruning. This feature helps prevent corruption when
90-
'git gc' runs concurrently with another process writing to the
91-
repository; see the "NOTES" section of linkgit:git-gc[1].
90+
When 'git gc' is run, it will call 'prune --expire 2.weeks.ago'
91+
(and 'repack --cruft --cruft-expiration 2.weeks.ago' if using
92+
cruft packs via `gc.cruftPacks` or `--cruft`). Override the
93+
grace period with this config variable. The value "now" may be
94+
used to disable this grace period and always prune unreachable
95+
objects immediately, or "never" may be used to suppress pruning.
96+
This feature helps prevent corruption when 'git gc' runs
97+
concurrently with another process writing to the repository; see
98+
the "NOTES" section of linkgit:git-gc[1].
9299

93100
gc.worktreePruneExpire::
94101
When 'git gc' is run, it calls

Documentation/git-gc.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ other housekeeping tasks (e.g. rerere, working trees, reflog...) will
5454
be performed as well.
5555

5656

57+
--cruft::
58+
When expiring unreachable objects, pack them separately into a
59+
cruft pack instead of storing the loose objects as loose
60+
objects.
61+
5762
--prune=<date>::
5863
Prune loose objects older than date (default is 2 weeks ago,
5964
overridable by the config variable `gc.pruneExpire`).

builtin/gc.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ static const char * const builtin_gc_usage[] = {
4242

4343
static int pack_refs = 1;
4444
static int prune_reflogs = 1;
45+
static int cruft_packs = 0;
4546
static int aggressive_depth = 50;
4647
static int aggressive_window = 250;
4748
static int gc_auto_threshold = 6700;
@@ -152,6 +153,7 @@ static void gc_config(void)
152153
git_config_get_int("gc.auto", &gc_auto_threshold);
153154
git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
154155
git_config_get_bool("gc.autodetach", &detach_auto);
156+
git_config_get_bool("gc.cruftpacks", &cruft_packs);
155157
git_config_get_expiry("gc.pruneexpire", &prune_expire);
156158
git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
157159
git_config_get_expiry("gc.logexpiry", &gc_log_expire);
@@ -331,7 +333,11 @@ static void add_repack_all_option(struct string_list *keep_pack)
331333
{
332334
if (prune_expire && !strcmp(prune_expire, "now"))
333335
strvec_push(&repack, "-a");
334-
else {
336+
else if (cruft_packs) {
337+
strvec_push(&repack, "--cruft");
338+
if (prune_expire)
339+
strvec_pushf(&repack, "--cruft-expiration=%s", prune_expire);
340+
} else {
335341
strvec_push(&repack, "-A");
336342
if (prune_expire)
337343
strvec_pushf(&repack, "--unpack-unreachable=%s", prune_expire);
@@ -551,6 +557,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
551557
{ OPTION_STRING, 0, "prune", &prune_expire, N_("date"),
552558
N_("prune unreferenced objects"),
553559
PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
560+
OPT_BOOL(0, "cruft", &cruft_packs, N_("pack unreferenced objects separately")),
554561
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
555562
OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
556563
PARSE_OPT_NOCOMPLETE),
@@ -670,6 +677,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
670677
die(FAILED_RUN, repack.v[0]);
671678

672679
if (prune_expire) {
680+
/* run `git prune` even if using cruft packs */
673681
strvec_push(&prune, prune_expire);
674682
if (quiet)
675683
strvec_push(&prune, "--no-progress");

t/t5329-pack-objects-cruft.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,43 @@ test_expect_success 'loose objects mtimes upsert others' '
429429
)
430430
'
431431

432+
test_expect_success 'expiring cruft objects with git gc' '
433+
git init repo &&
434+
test_when_finished "rm -fr repo" &&
435+
(
436+
cd repo &&
437+
438+
test_commit reachable &&
439+
git branch -M main &&
440+
git checkout --orphan other &&
441+
test_commit unreachable &&
442+
443+
git checkout main &&
444+
git branch -D other &&
445+
git tag -d unreachable &&
446+
# objects are not cruft if they are contained in the reflogs
447+
git reflog expire --all --expire=all &&
448+
449+
git rev-list --objects --all --no-object-names >reachable.raw &&
450+
git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
451+
sort <reachable.raw >reachable &&
452+
comm -13 reachable objects >unreachable &&
453+
454+
git repack --cruft -d &&
455+
456+
mtimes=$(ls .git/objects/pack/pack-*.mtimes) &&
457+
test_path_is_file $mtimes &&
458+
459+
git gc --cruft --prune=now &&
460+
461+
git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
462+
463+
comm -23 unreachable objects >removed &&
464+
test_cmp unreachable removed &&
465+
test_path_is_missing $mtimes
466+
)
467+
'
468+
432469
test_expect_success 'cruft packs are not included in geometric repack' '
433470
git init repo &&
434471
test_when_finished "rm -fr repo" &&

0 commit comments

Comments
 (0)