Skip to content

Commit 0af0672

Browse files
derrickstoleegitster
authored andcommitted
builtin/repack.c: only repack .packs that exist
In 73320e4 (builtin/repack.c: only collect fully-formed packs, 2023-06-07), we switched the check for which packs to collect by starting at the .idx files and looking for matching .pack files. This avoids trying to repack pack-files that have not had their pack-indexes installed yet. However, it does cause maintenance to halt if we find the (problematic, but not insurmountable) case of a .idx file without a corresponding .pack file. In an environment where packfile maintenance is a critical function, such a hard stop is costly and requires human intervention to resolve (by deleting the .idx file). This was not the case before. We successfully repacked through this scenario until the recent change to scan for .idx files. Further, if we are actually in a case where objects are missing, we detect this at a different point during the reachability walk. In other cases, Git prepares its list of packfiles by scanning .idx files and then only adds it to the packfile list if the corresponding .pack file exists. It even does so without a warning! (See add_packed_git() in packfile.c for details.) This case is much less likely to occur than the failures seen before 73320e4. Packfiles are "installed" by writing the .pack file before the .idx and that process can be interrupted. Packfiles _should_ be deleted by deleting the .idx first, followed by the .pack file, but unlink_pack_path() does not do this: it deletes the .pack _first_, allowing a window where this process could be interrupted. We leave the consideration of changing this order as a separate concern. Knowing that this condition is possible from interrupted Git processes and not other tools lends some weight that Git should be more flexible around this scenario. Add a check to see if the .pack file exists before adding it to the list for repacking. This will stop a number of maintenance failures seen in production but fixed by deleting the .idx files. This brings us closer to the case before 73320e4 in that 'git repack' will not fail when there is an orphaned .idx file, at least, not due to the way we scan for packfiles. In the case that the .pack file was erroneously deleted without copies of its objects in other installed packfiles, then 'git repack' will fail due to the reachable object walk. This does resolve the case where automated repacks will no longer be halted on this case. The tests in t7700 show both these successful scenarios and the case of failing if the .pack was truly required. Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Taylor Blau <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent aa9166b commit 0af0672

File tree

2 files changed

+38
-1
lines changed

2 files changed

+38
-1
lines changed

builtin/repack.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ static void collect_pack_filenames(struct string_list *fname_nonkept_list,
125125
strbuf_add(&buf, e->d_name, len);
126126
strbuf_addstr(&buf, ".pack");
127127

128+
if (!file_exists(mkpath("%s/%s", packdir, buf.buf)))
129+
continue;
130+
128131
for (i = 0; i < extra_keep->nr; i++)
129132
if (!fspathcmp(buf.buf, extra_keep->items[i].string))
130133
break;

t/t7700-repack.sh

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ test_expect_success 'repack --keep-pack' '
213213
test_create_repo keep-pack &&
214214
(
215215
cd keep-pack &&
216-
# avoid producing difference packs to delta/base choices
216+
# avoid producing different packs due to delta/base choices
217217
git config pack.window 0 &&
218218
P1=$(commit_and_pack 1) &&
219219
P2=$(commit_and_pack 2) &&
@@ -239,6 +239,10 @@ test_expect_success 'repack --keep-pack' '
239239
mv "$from" "$to" || return 1
240240
done &&
241241
242+
# A .idx file without a .pack should not stop us from
243+
# repacking what we can.
244+
touch .git/objects/pack/pack-does-not-exist.idx &&
245+
242246
git repack --cruft -d --keep-pack $P1 --keep-pack $P4 &&
243247
244248
ls .git/objects/pack/*.pack >newer-counts &&
@@ -247,6 +251,36 @@ test_expect_success 'repack --keep-pack' '
247251
)
248252
'
249253

254+
test_expect_success 'repacking fails when missing .pack actually means missing objects' '
255+
test_create_repo idx-without-pack &&
256+
(
257+
cd idx-without-pack &&
258+
259+
# Avoid producing different packs due to delta/base choices
260+
git config pack.window 0 &&
261+
P1=$(commit_and_pack 1) &&
262+
P2=$(commit_and_pack 2) &&
263+
P3=$(commit_and_pack 3) &&
264+
P4=$(commit_and_pack 4) &&
265+
ls .git/objects/pack/*.pack >old-counts &&
266+
test_line_count = 4 old-counts &&
267+
268+
# Remove one .pack file
269+
rm .git/objects/pack/$P2 &&
270+
271+
ls .git/objects/pack/*.pack >before-pack-dir &&
272+
273+
test_must_fail git fsck &&
274+
test_must_fail git repack --cruft -d 2>err &&
275+
grep "bad object" err &&
276+
277+
# Before failing, the repack did not modify the
278+
# pack directory.
279+
ls .git/objects/pack/*.pack >after-pack-dir &&
280+
test_cmp before-pack-dir after-pack-dir
281+
)
282+
'
283+
250284
test_expect_success 'bitmaps are created by default in bare repos' '
251285
git clone --bare .git bare.git &&
252286
rm -f bare.git/objects/pack/*.bitmap &&

0 commit comments

Comments
 (0)