Git fork

builtin/repack: fix `--keep-unreachable` when there are no packs

The "--keep-unreachable" flag is supposed to append any unreachable
objects to the newly written pack. This flag is explicitly documented as
appending both packed and loose unreachable objects to the new packfile.
And while this works alright when repacking with preexisting packfiles,
it stops working when the repository does not have any packfiles at all.

The root cause are the conditions used to decide whether or not we want
to append "--pack-loose-unreachable" to git-pack-objects(1). There are
a couple of conditions here:

- `has_existing_non_kept_packs()` checks whether there are existing
packfiles. This condition makes sense to guard "--keep-pack=",
"--unpack-unreachable" and "--keep-unreachable", because all of
these flags only make sense in combination with existing packfiles.
But it does not make sense to disable `--pack-loose-unreachable`
when there aren't any preexisting packfiles, as loose objects can be
packed into the new packfile regardless of that.

- `delete_redundant` checks whether we want to delete any objects or
packs that are about to become redundant. The documentation of
`--keep-unreachable` explicitly says that `git repack -ad` needs to
be executed for the flag to have an effect.

It is not immediately obvious why such redundant objects need to be
deleted in order for "--pack-unreachable-objects" to be effective.
But as things are working as documented this is nothing we'll change
for now.

- `pack_everything & PACK_CRUFT` checks that we're not creating a
cruft pack. This condition makes sense in the context of
"--pack-loose-unreachable", as unreachable objects would end up in
the cruft pack anyway.

So while the second and third condition are sensible, it does not make
any sense to condition `--pack-loose-unreachable` on the existence of
packfiles.

Fix the bug by splitting out the "--pack-loose-unreachable" and only
making it depend on the second and third condition. Like this, loose
unreachable objects will be packed regardless of any preexisting
packfiles.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Acked-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Patrick Steinhardt and committed by
Junio C Hamano
414c8230 f93ff170

+20 -1
+4 -1
builtin/repack.c
··· 1370 1370 "--unpack-unreachable"); 1371 1371 } else if (keep_unreachable) { 1372 1372 strvec_push(&cmd.args, "--keep-unreachable"); 1373 - strvec_push(&cmd.args, "--pack-loose-unreachable"); 1374 1373 } 1375 1374 } 1375 + 1376 + if (keep_unreachable && delete_redundant && 1377 + !(pack_everything & PACK_CRUFT)) 1378 + strvec_push(&cmd.args, "--pack-loose-unreachable"); 1376 1379 } else if (geometry.split_factor) { 1377 1380 strvec_push(&cmd.args, "--stdin-packs"); 1378 1381 strvec_push(&cmd.args, "--unpacked");
+16
t/t7701-repack-unpack-unreachable.sh
··· 195 195 git cat-file -p $sha1 196 196 ' 197 197 198 + test_expect_success 'repack -k packs unreachable loose objects without existing packfiles' ' 199 + test_when_finished "rm -rf repo" && 200 + git init repo && 201 + ( 202 + cd repo && 203 + 204 + oid=$(echo would-be-deleted-loose | git hash-object -w --stdin) && 205 + objpath=.git/objects/$(echo $sha1 | sed "s,..,&/,") && 206 + test_path_is_file $objpath && 207 + 208 + git repack -ad --keep-unreachable && 209 + test_path_is_missing $objpath && 210 + git cat-file -p $oid 211 + ) 212 + ' 213 + 198 214 test_done