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>
215 lines
6.3 KiB
Bash
Executable File
215 lines
6.3 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
test_description='git repack works correctly'
|
|
|
|
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
|
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
|
|
|
. ./test-lib.sh
|
|
|
|
fsha1=
|
|
csha1=
|
|
tsha1=
|
|
|
|
test_expect_success '-A with -d option leaves unreachable objects unpacked' '
|
|
echo content > file1 &&
|
|
git add . &&
|
|
test_tick &&
|
|
git commit -m initial_commit &&
|
|
# create a transient branch with unique content
|
|
git checkout -b transient_branch &&
|
|
echo more content >> file1 &&
|
|
# record the objects created in the database for file, commit, tree
|
|
fsha1=$(git hash-object file1) &&
|
|
test_tick &&
|
|
git commit -a -m more_content &&
|
|
csha1=$(git rev-parse HEAD^{commit}) &&
|
|
tsha1=$(git rev-parse HEAD^{tree}) &&
|
|
git checkout main &&
|
|
echo even more content >> file1 &&
|
|
test_tick &&
|
|
git commit -a -m even_more_content &&
|
|
# delete the transient branch
|
|
git branch -D transient_branch &&
|
|
# pack the repo
|
|
git repack -A -d -l &&
|
|
# verify objects are packed in repository
|
|
test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
|
|
grep -E "^($fsha1|$csha1|$tsha1) " |
|
|
sort | uniq | wc -l) &&
|
|
git show $fsha1 &&
|
|
git show $csha1 &&
|
|
git show $tsha1 &&
|
|
# now expire the reflog, while keeping reachable ones but expiring
|
|
# unreachables immediately
|
|
test_tick &&
|
|
sometimeago=$(( $test_tick - 10000 )) &&
|
|
git reflog expire --expire=$sometimeago --expire-unreachable=$test_tick --all &&
|
|
# and repack
|
|
git repack -A -d -l &&
|
|
# verify objects are retained unpacked
|
|
test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
|
|
grep -E "^($fsha1|$csha1|$tsha1) " |
|
|
sort | uniq | wc -l) &&
|
|
git show $fsha1 &&
|
|
git show $csha1 &&
|
|
git show $tsha1
|
|
'
|
|
|
|
compare_mtimes ()
|
|
{
|
|
read tref &&
|
|
while read t; do
|
|
test "$tref" = "$t" || return 1
|
|
done
|
|
}
|
|
|
|
test_expect_success '-A without -d option leaves unreachable objects packed' '
|
|
fsha1path=$(echo "$fsha1" | sed -e "s|\(..\)|\1/|") &&
|
|
fsha1path=".git/objects/$fsha1path" &&
|
|
csha1path=$(echo "$csha1" | sed -e "s|\(..\)|\1/|") &&
|
|
csha1path=".git/objects/$csha1path" &&
|
|
tsha1path=$(echo "$tsha1" | sed -e "s|\(..\)|\1/|") &&
|
|
tsha1path=".git/objects/$tsha1path" &&
|
|
git branch transient_branch $csha1 &&
|
|
git repack -a -d -l &&
|
|
test ! -f "$fsha1path" &&
|
|
test ! -f "$csha1path" &&
|
|
test ! -f "$tsha1path" &&
|
|
test 1 = $(ls -1 .git/objects/pack/pack-*.pack | wc -l) &&
|
|
packfile=$(ls .git/objects/pack/pack-*.pack) &&
|
|
git branch -D transient_branch &&
|
|
test_tick &&
|
|
git repack -A -l &&
|
|
test ! -f "$fsha1path" &&
|
|
test ! -f "$csha1path" &&
|
|
test ! -f "$tsha1path" &&
|
|
git show $fsha1 &&
|
|
git show $csha1 &&
|
|
git show $tsha1
|
|
'
|
|
|
|
test_expect_success 'unpacked objects receive timestamp of pack file' '
|
|
tmppack=".git/objects/pack/tmp_pack" &&
|
|
ln "$packfile" "$tmppack" &&
|
|
git repack -A -l -d &&
|
|
test-tool chmtime --get "$tmppack" "$fsha1path" "$csha1path" "$tsha1path" \
|
|
> mtimes &&
|
|
compare_mtimes < mtimes
|
|
'
|
|
|
|
test_expect_success 'do not bother loosening old objects' '
|
|
obj1=$(echo one | git hash-object -w --stdin) &&
|
|
obj2=$(echo two | git hash-object -w --stdin) &&
|
|
pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
|
|
pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
|
|
git prune-packed &&
|
|
git cat-file -p $obj1 &&
|
|
git cat-file -p $obj2 &&
|
|
test-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
|
|
git repack -A -d --unpack-unreachable=1.hour.ago &&
|
|
git cat-file -p $obj1 &&
|
|
test_must_fail git cat-file -p $obj2
|
|
'
|
|
|
|
test_expect_success 'gc.recentObjectsHook' '
|
|
obj1=$(echo one | git hash-object -w --stdin) &&
|
|
obj2=$(echo two | git hash-object -w --stdin) &&
|
|
obj3=$(echo three | git hash-object -w --stdin) &&
|
|
pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
|
|
pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
|
|
pack3=$(echo $obj3 | git pack-objects .git/objects/pack/pack) &&
|
|
git prune-packed &&
|
|
|
|
git cat-file -p $obj1 &&
|
|
git cat-file -p $obj2 &&
|
|
git cat-file -p $obj3 &&
|
|
|
|
# make an unreachable annotated tag object to ensure we rescue objects
|
|
# which are reachable from non-pruned unreachable objects
|
|
obj2_tag="$(git mktag <<-EOF
|
|
object $obj2
|
|
type blob
|
|
tag obj2-tag
|
|
tagger T A Gger <tagger@example.com> 1234567890 -0000
|
|
EOF
|
|
)" &&
|
|
|
|
obj2_tag_pack="$(echo $obj2_tag | git pack-objects .git/objects/pack/pack)" &&
|
|
git prune-packed &&
|
|
|
|
write_script precious-objects <<-EOF &&
|
|
echo $obj2_tag
|
|
EOF
|
|
git config gc.recentObjectsHook ./precious-objects &&
|
|
|
|
test-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
|
|
test-tool chmtime =-86400 .git/objects/pack/pack-$pack3.pack &&
|
|
test-tool chmtime =-86400 .git/objects/pack/pack-$obj2_tag_pack.pack &&
|
|
git repack -A -d --unpack-unreachable=1.hour.ago &&
|
|
|
|
git cat-file -p $obj1 &&
|
|
git cat-file -p $obj2 &&
|
|
git cat-file -p $obj2_tag &&
|
|
test_must_fail git cat-file -p $obj3
|
|
'
|
|
|
|
test_expect_success 'keep packed objects found only in index' '
|
|
echo my-unique-content >file &&
|
|
git add file &&
|
|
git commit -m "make it reachable" &&
|
|
git gc &&
|
|
git reset HEAD^ &&
|
|
git reflog expire --expire=now --all &&
|
|
git add file &&
|
|
test-tool chmtime =-86400 .git/objects/pack/* &&
|
|
git gc --prune=1.hour.ago &&
|
|
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_expect_success 'repack -k packs unreachable loose objects' '
|
|
# create loose unreachable object
|
|
sha1=$(echo would-be-deleted-loose | git hash-object -w --stdin) &&
|
|
objpath=.git/objects/$(echo $sha1 | sed "s,..,&/,") &&
|
|
test_path_is_file $objpath &&
|
|
|
|
# and confirm that the loose object goes away, but we can
|
|
# still access it (ergo, it is packed)
|
|
git repack -adk &&
|
|
test_path_is_missing $objpath &&
|
|
git cat-file -p $sha1
|
|
'
|
|
|
|
test_expect_success 'repack -k packs unreachable loose objects without existing packfiles' '
|
|
test_when_finished "rm -rf repo" &&
|
|
git init repo &&
|
|
(
|
|
cd repo &&
|
|
|
|
oid=$(echo would-be-deleted-loose | git hash-object -w --stdin) &&
|
|
objpath=.git/objects/$(echo $sha1 | sed "s,..,&/,") &&
|
|
test_path_is_file $objpath &&
|
|
|
|
git repack -ad --keep-unreachable &&
|
|
test_path_is_missing $objpath &&
|
|
git cat-file -p $oid
|
|
)
|
|
'
|
|
|
|
test_done
|