Skip to content

Commit 6abb17c

Browse files
pks-tdscho
authored andcommitted
object-file: fix race in object collision check
One of the tests in t5616 asserts that git-fetch(1) with `--refetch` triggers repository maintenance with the correct set of arguments. This test is flaky and causes us to fail sometimes: ++ git -c protocol.version=0 -c gc.autoPackLimit=0 -c maintenance.incremental-repack.auto=1234 -C pc1 fetch --refetch origin error: unable to open .git/objects/pack/pack-029d08823bd8a8eab510ad6ac75c823cfd3ed31e.pack: No such file or directory fatal: unable to rename temporary file to '.git/objects/pack/pack-029d08823bd8a8eab510ad6ac75c823cfd3ed31e.pack' fatal: could not finish pack-objects to repack local links fatal: index-pack failed error: last command exited with $?=128 The error message is quite confusing as it talks about trying to rename a temporary packfile. A first hunch would thus be that this packfile gets written by git-fetch(1), but removed by git-maintenance(1) while it hasn't yet been finalized, which shouldn't ever happen. And indeed, when looking closer one notices that the file that is supposedly of temporary nature does not have the typical `tmp_pack_` prefix. As it turns out, the "unable to rename temporary file" fatal error is a red herring and the real error is "unable to open". That error is raised by `check_collision()`, which is called by `finalize_object_file()` when moving the new packfile into place. Because t5616 re-fetches objects, we end up with the exact same pack as we already have in the repository. So when the concurrent git-maintenance(1) process rewrites the preexisting pack and unlinks it exactly at the point in time where git-fetch(1) wants to check the old and new packfiles for equality we will see ENOENT and thus `check_collision()` returns an error, which gets bubbled up by `finalize_object_file()` and is then handled by `rename_tmp_packfile()`. That function does not know about the exact root cause of the error and instead just claims that the rename has failed. This race is thus caused by b1b8dfd (finalize_object_file(): implement collision check, 2024-09-26), where we have newly introduced the collision check. By definition, two files cannot collide with each other when one of them has been removed. We can thus trivially fix the issue by ignoring ENOENT when opening either of the files we're about to check for collision. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 5cc2d6c commit 6abb17c

File tree

1 file changed

+4
-2
lines changed

1 file changed

+4
-2
lines changed

object-file.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,13 +1978,15 @@ static int check_collision(const char *filename_a, const char *filename_b)
19781978

19791979
fd_a = open(filename_a, O_RDONLY);
19801980
if (fd_a < 0) {
1981-
ret = error_errno(_("unable to open %s"), filename_a);
1981+
if (errno != ENOENT)
1982+
ret = error_errno(_("unable to open %s"), filename_a);
19821983
goto out;
19831984
}
19841985

19851986
fd_b = open(filename_b, O_RDONLY);
19861987
if (fd_b < 0) {
1987-
ret = error_errno(_("unable to open %s"), filename_b);
1988+
if (errno != ENOENT)
1989+
ret = error_errno(_("unable to open %s"), filename_b);
19881990
goto out;
19891991
}
19901992

0 commit comments

Comments
 (0)