Skip to content

Commit f979bcd

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 <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent f747278 commit f979bcd

File tree

4 files changed

+320
-6
lines changed

4 files changed

+320
-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: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,17 @@
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 char *packdir, *packtmp_name, *packtmp;
31+
static char *cruft_expiration;
2632

2733
static const char *const git_repack_usage[] = {
2834
N_("git repack [<options>]"),
@@ -54,6 +60,7 @@ static int repack_config(const char *var, const char *value, void *cb)
5460
use_delta_islands = git_config_bool(var, value);
5561
return 0;
5662
}
63+
5764
return git_default_config(var, value, cb);
5865
}
5966

@@ -300,9 +307,6 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
300307
die(_("could not finish pack-objects to repack promisor objects"));
301308
}
302309

303-
#define ALL_INTO_ONE 1
304-
#define LOOSEN_UNREACHABLE 2
305-
306310
struct pack_geometry {
307311
struct packed_git **pack;
308312
uint32_t pack_nr, pack_alloc;
@@ -339,6 +343,8 @@ static void init_pack_geometry(struct pack_geometry **geometry_p)
339343
for (p = get_all_packs(the_repository); p; p = p->next) {
340344
if (!pack_kept_objects && p->pack_keep)
341345
continue;
346+
if (p->is_cruft)
347+
continue;
342348

343349
ALLOC_GROW(geometry->pack,
344350
geometry->pack_nr + 1,
@@ -600,6 +606,67 @@ static int write_midx_included_packs(struct string_list *include,
600606
return finish_command(&cmd);
601607
}
602608

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

618685
/* variables to be filled by option parsing */
619-
int pack_everything = 0;
620686
int delete_redundant = 0;
621687
const char *unpack_unreachable = NULL;
622688
int keep_unreachable = 0;
@@ -632,6 +698,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
632698
OPT_BIT('A', NULL, &pack_everything,
633699
N_("same as -a, and turn unreachable objects loose"),
634700
LOOSEN_UNREACHABLE | ALL_INTO_ONE),
701+
OPT_BIT(0, "cruft", &pack_everything,
702+
N_("same as -a, pack unreachable cruft objects separately"),
703+
PACK_CRUFT),
704+
OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"),
705+
N_("with -C, expire objects older than this")),
635706
OPT_BOOL('d', NULL, &delete_redundant,
636707
N_("remove redundant packs, and run git-prune-packed")),
637708
OPT_BOOL('f', NULL, &po_args.no_reuse_delta,
@@ -684,6 +755,15 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
684755
(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
685756
die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A");
686757

758+
if (pack_everything & PACK_CRUFT) {
759+
pack_everything |= ALL_INTO_ONE;
760+
761+
if (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))
762+
die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-A");
763+
if (keep_unreachable)
764+
die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-k");
765+
}
766+
687767
if (write_bitmaps < 0) {
688768
if (!write_midx &&
689769
(!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
@@ -767,7 +847,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
767847
if (pack_everything & ALL_INTO_ONE) {
768848
repack_promisor_objects(&po_args, &names);
769849

770-
if (existing_nonkept_packs.nr && delete_redundant) {
850+
if (existing_nonkept_packs.nr && delete_redundant &&
851+
!(pack_everything & PACK_CRUFT)) {
771852
for_each_string_list_item(item, &names) {
772853
strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack",
773854
packtmp_name, item->string);
@@ -829,6 +910,21 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
829910
if (!names.nr && !po_args.quiet)
830911
printf_ln(_("Nothing new to pack."));
831912

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

0 commit comments

Comments
 (0)