repack: add --keep-unreachable option

The usual way to do a full repack (and what is done by
git-gc) is to run "repack -Ad --unpack-unreachable=<when>",
which will loosen any unreachable objects newer than
"<when>", and drop any older ones.

This is a safer alternative to "repack -ad", because
"<when>" becomes a grace period during which we will not
drop any new objects that are about to be referenced.
However, it isn't perfectly safe. It's always possible that
a process is about to reference an old object. Even if that
process were to take care to update the timestamp on the
object, there is no atomicity with a simultaneously running
"repack" process.

So while unlikely, there is a small race wherein we may drop
an object that is in the process of being referenced. If you
do automated repacking on a large number of active
repositories, you may hit it eventually, and the result is a
corrupted repository.

It would be nice to fix that race in the long run, but it's
complicated.  In the meantime, there is a much simpler
strategy for automated repository maintenance: do not drop
objects at all. We already have a "--keep-unreachable"
option in pack-objects; we just need to plumb it through
from git-repack.

Note that this _isn't_ plumbed through from git-gc, so at
this point it's strictly a tool for people doing their own
advanced repository maintenance strategy.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jeff King 2016-06-13 00:36:28 -04:00 committed by Junio C Hamano
parent 6a7bcb5471
commit 905f27b86a
3 changed files with 30 additions and 0 deletions

View File

@ -134,6 +134,12 @@ other objects in that pack they already have locally.
the write of any objects that would be immediately pruned by the write of any objects that would be immediately pruned by
a follow-up `git prune`. a follow-up `git prune`.
-k::
--keep-unreachable::
When used with `-ad`, any unreachable objects from existing
packs will be appended to the end of the packfile instead of
being removed.
Configuration Configuration
------------- -------------

View File

@ -146,6 +146,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int pack_everything = 0; int pack_everything = 0;
int delete_redundant = 0; int delete_redundant = 0;
const char *unpack_unreachable = NULL; const char *unpack_unreachable = NULL;
int keep_unreachable = 0;
const char *window = NULL, *window_memory = NULL; const char *window = NULL, *window_memory = NULL;
const char *depth = NULL; const char *depth = NULL;
const char *max_pack_size = NULL; const char *max_pack_size = NULL;
@ -175,6 +176,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("write bitmap index")), N_("write bitmap index")),
OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"), OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
N_("with -A, do not loosen objects older than this")), N_("with -A, do not loosen objects older than this")),
OPT_BOOL('k', "keep-unreachable", &keep_unreachable,
N_("with -a, repack unreachable objects")),
OPT_STRING(0, "window", &window, N_("n"), OPT_STRING(0, "window", &window, N_("n"),
N_("size of the window used for delta compression")), N_("size of the window used for delta compression")),
OPT_STRING(0, "window-memory", &window_memory, N_("bytes"), OPT_STRING(0, "window-memory", &window_memory, N_("bytes"),
@ -196,6 +199,10 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (delete_redundant && repository_format_precious_objects) if (delete_redundant && repository_format_precious_objects)
die(_("cannot delete packs in a precious-objects repo")); die(_("cannot delete packs in a precious-objects repo"));
if (keep_unreachable &&
(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
die(_("--keep-unreachable and -A are incompatible"));
if (pack_kept_objects < 0) if (pack_kept_objects < 0)
pack_kept_objects = write_bitmaps; pack_kept_objects = write_bitmaps;
@ -239,6 +246,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
} else if (pack_everything & LOOSEN_UNREACHABLE) { } else if (pack_everything & LOOSEN_UNREACHABLE) {
argv_array_push(&cmd.args, argv_array_push(&cmd.args,
"--unpack-unreachable"); "--unpack-unreachable");
} else if (keep_unreachable) {
argv_array_push(&cmd.args, "--keep-unreachable");
} else { } else {
argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
} }

View File

@ -122,4 +122,19 @@ test_expect_success 'keep packed objects found only in index' '
git cat-file blob :file git cat-file blob :file
' '
test_expect_success 'repack -k keeps unreachable packed objects' '
# create packed-but-unreachable object
sha1=$(echo unreachable-packed | git hash-object -w --stdin) &&
pack=$(echo $sha1 | git pack-objects .git/objects/pack/pack) &&
git prune-packed &&
# -k should keep it
git repack -adk &&
git cat-file -p $sha1 &&
# and double check that without -k it would have been removed
git repack -ad &&
test_must_fail git cat-file -p $sha1
'
test_done test_done