Skip to content

Commit f9825d1

Browse files
ttaylorrgitster
authored andcommitted
builtin/repack.c: support generating a cruft pack
Expose a way to split the contents of a repository into a main and cruft pack when doing an all-into-one repack with `git repack --cruft -d`, and a complementary configuration variable. Signed-off-by: Taylor Blau <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a7d4938 commit f9825d1

File tree

4 files changed

+319
-6
lines changed

4 files changed

+319
-6
lines changed

Documentation/git-repack.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ to the new separate pack will be written.
6363
Also run 'git prune-packed' to remove redundant
6464
loose object files.
6565

66+
--cruft::
67+
Same as `-a`, unless `-d` is used. Then any unreachable objects
68+
are packed into a separate cruft pack. Unreachable objects can
69+
be pruned using the normal expiry rules with the next `git gc`
70+
invocation (see linkgit:git-gc[1]). Incompatible with `-k`.
71+
72+
--cruft-expiration=<approxidate>::
73+
Expire unreachable objects older than `<approxidate>`
74+
immediately instead of waiting for the next `git gc` invocation.
75+
Only useful with `--cruft -d`.
76+
6677
-l::
6778
Pass the `--local` option to 'git pack-objects'. See
6879
linkgit:git-pack-objects[1].

Documentation/technical/cruft-packs.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pruned according to normal expiry rules with the next 'git gc' invocation.
1717

1818
Unreachable objects aren't removed immediately, since doing so could race with
1919
an incoming push which may reference an object which is about to be deleted.
20-
Instead, those unreachable objects are stored as loose object and stay that way
20+
Instead, those unreachable objects are stored as loose objects and stay that way
2121
until they are older than the expiration window, at which point they are removed
2222
by linkgit:git-prune[1].
2323

builtin/repack.c

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@
1818
#include "pack-bitmap.h"
1919
#include "refs.h"
2020

21+
#define ALL_INTO_ONE 1
22+
#define LOOSEN_UNREACHABLE 2
23+
#define PACK_CRUFT 4
24+
25+
static int pack_everything;
2126
static int delta_base_offset = 1;
2227
static int pack_kept_objects = -1;
2328
static int write_bitmaps = -1;
2429
static int use_delta_islands;
2530
static int run_update_server_info = 1;
2631
static char *packdir, *packtmp_name, *packtmp;
32+
static char *cruft_expiration;
2733

2834
static const char *const git_repack_usage[] = {
2935
N_("git repack [<options>]"),
@@ -305,9 +311,6 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
305311
die(_("could not finish pack-objects to repack promisor objects"));
306312
}
307313

308-
#define ALL_INTO_ONE 1
309-
#define LOOSEN_UNREACHABLE 2
310-
311314
struct pack_geometry {
312315
struct packed_git **pack;
313316
uint32_t pack_nr, pack_alloc;
@@ -344,6 +347,8 @@ static void init_pack_geometry(struct pack_geometry **geometry_p)
344347
for (p = get_all_packs(the_repository); p; p = p->next) {
345348
if (!pack_kept_objects && p->pack_keep)
346349
continue;
350+
if (p->is_cruft)
351+
continue;
347352

348353
ALLOC_GROW(geometry->pack,
349354
geometry->pack_nr + 1,
@@ -605,6 +610,67 @@ static int write_midx_included_packs(struct string_list *include,
605610
return finish_command(&cmd);
606611
}
607612

613+
static int write_cruft_pack(const struct pack_objects_args *args,
614+
const char *pack_prefix,
615+
struct string_list *names,
616+
struct string_list *existing_packs,
617+
struct string_list *existing_kept_packs)
618+
{
619+
struct child_process cmd = CHILD_PROCESS_INIT;
620+
struct strbuf line = STRBUF_INIT;
621+
struct string_list_item *item;
622+
FILE *in, *out;
623+
int ret;
624+
625+
prepare_pack_objects(&cmd, args);
626+
627+
strvec_push(&cmd.args, "--cruft");
628+
if (cruft_expiration)
629+
strvec_pushf(&cmd.args, "--cruft-expiration=%s",
630+
cruft_expiration);
631+
632+
strvec_push(&cmd.args, "--honor-pack-keep");
633+
strvec_push(&cmd.args, "--non-empty");
634+
strvec_push(&cmd.args, "--max-pack-size=0");
635+
636+
cmd.in = -1;
637+
638+
ret = start_command(&cmd);
639+
if (ret)
640+
return ret;
641+
642+
/*
643+
* names has a confusing double use: it both provides the list
644+
* of just-written new packs, and accepts the name of the cruft
645+
* pack we are writing.
646+
*
647+
* By the time it is read here, it contains only the pack(s)
648+
* that were just written, which is exactly the set of packs we
649+
* want to consider kept.
650+
*/
651+
in = xfdopen(cmd.in, "w");
652+
for_each_string_list_item(item, names)
653+
fprintf(in, "%s-%s.pack\n", pack_prefix, item->string);
654+
for_each_string_list_item(item, existing_packs)
655+
fprintf(in, "-%s.pack\n", item->string);
656+
for_each_string_list_item(item, existing_kept_packs)
657+
fprintf(in, "%s.pack\n", item->string);
658+
fclose(in);
659+
660+
out = xfdopen(cmd.out, "r");
661+
while (strbuf_getline_lf(&line, out) != EOF) {
662+
if (line.len != the_hash_algo->hexsz)
663+
die(_("repack: Expecting full hex object ID lines only "
664+
"from pack-objects."));
665+
string_list_append(names, line.buf);
666+
}
667+
fclose(out);
668+
669+
strbuf_release(&line);
670+
671+
return finish_command(&cmd);
672+
}
673+
608674
int cmd_repack(int argc, const char **argv, const char *prefix)
609675
{
610676
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -621,7 +687,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
621687
int show_progress;
622688

623689
/* variables to be filled by option parsing */
624-
int pack_everything = 0;
625690
int delete_redundant = 0;
626691
const char *unpack_unreachable = NULL;
627692
int keep_unreachable = 0;
@@ -636,6 +701,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
636701
OPT_BIT('A', NULL, &pack_everything,
637702
N_("same as -a, and turn unreachable objects loose"),
638703
LOOSEN_UNREACHABLE | ALL_INTO_ONE),
704+
OPT_BIT(0, "cruft", &pack_everything,
705+
N_("same as -a, pack unreachable cruft objects separately"),
706+
PACK_CRUFT),
707+
OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"),
708+
N_("with -C, expire objects older than this")),
639709
OPT_BOOL('d', NULL, &delete_redundant,
640710
N_("remove redundant packs, and run git-prune-packed")),
641711
OPT_BOOL('f', NULL, &po_args.no_reuse_delta,
@@ -688,6 +758,15 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
688758
(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
689759
die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A");
690760

761+
if (pack_everything & PACK_CRUFT) {
762+
pack_everything |= ALL_INTO_ONE;
763+
764+
if (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))
765+
die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-A");
766+
if (keep_unreachable)
767+
die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-k");
768+
}
769+
691770
if (write_bitmaps < 0) {
692771
if (!write_midx &&
693772
(!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
@@ -771,7 +850,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
771850
if (pack_everything & ALL_INTO_ONE) {
772851
repack_promisor_objects(&po_args, &names);
773852

774-
if (existing_nonkept_packs.nr && delete_redundant) {
853+
if (existing_nonkept_packs.nr && delete_redundant &&
854+
!(pack_everything & PACK_CRUFT)) {
775855
for_each_string_list_item(item, &names) {
776856
strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack",
777857
packtmp_name, item->string);
@@ -833,6 +913,21 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
833913
if (!names.nr && !po_args.quiet)
834914
printf_ln(_("Nothing new to pack."));
835915

916+
if (pack_everything & PACK_CRUFT) {
917+
const char *pack_prefix;
918+
if (!skip_prefix(packtmp, packdir, &pack_prefix))
919+
die(_("pack prefix %s does not begin with objdir %s"),
920+
packtmp, packdir);
921+
if (*pack_prefix == '/')
922+
pack_prefix++;
923+
924+
ret = write_cruft_pack(&po_args, pack_prefix, &names,
925+
&existing_nonkept_packs,
926+
&existing_kept_packs);
927+
if (ret)
928+
return ret;
929+
}
930+
836931
for_each_string_list_item(item, &names) {
837932
item->util = (void *)(uintptr_t)populate_pack_exts(item->string);
838933
}

0 commit comments

Comments
 (0)