]> git.ipfire.org Git - thirdparty/git.git/commitdiff
maintenance(geometric): do release the `.idx` files before repacking
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Thu, 7 May 2026 12:51:13 +0000 (12:51 +0000)
committerJunio C Hamano <gitster@pobox.com>
Fri, 8 May 2026 00:53:12 +0000 (09:53 +0900)
As is done for all the other maintenance tasks, let's release the ODB
also before starting the geometric repacking. That way, the `.idx` files
won't be `mmap()`ed when they are to be deleted (which does not work on
Windows because you cannot delete files on that platform as long as they
are kept open by a process).

This regression was introduced by 9bc151850c1c (builtin/maintenance:
introduce "geometric-repack" task, 2025-10-24), but was only noticed
once geometric repacking was made the default in 452b12c2e0fe (builtin/
maintenance: use "geometric" strategy by default, 2026-02-24).

The fix recapitulates my work from df76ee7b77f0 (run-command: offer to
close the object store before running, 2021-09-09) & friends.

To guard against future regressions of this kind, add a check to
`run_and_verify_geometric_pack()` in `t7900` that detects orphaned
`.idx` files left behind after repacking. Contrary to interactive
calls, the `git maintenance` call in that test case would _not_ block on
Windows, asking whether to retry deleting that file, which is the reason
why this bug was not caught earlier.

Furthermore, since the default behavior of `DeleteFileW()` was changed
at some point between Windows 10 Build 17134.1304 and Build 18363.657
to use POSIX semantics (see https://stackoverflow.com/a/60512798),
the added orphaned-`.idx` check would be insufficient to catch this
regression on modern Windows without emulating legacy delete semantics
via `GIT_TEST_LEGACY_DELETE=1`.

This fixes https://github.com/git-for-windows/git/issues/6210.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/gc.c
t/t7900-maintenance.sh

index 3a71e314c975afea15cddd7dfe51ae6c21297357..84a66d32404e4dbdba5e554d63a8cae55cce5f97 100644 (file)
@@ -1590,6 +1590,7 @@ static int maintenance_task_geometric_repack(struct maintenance_run_opts *opts,
        pack_geometry_split(&geometry);
 
        child.git_cmd = 1;
+       child.odb_to_close = the_repository->objects;
 
        strvec_pushl(&child.args, "repack", "-d", "-l", NULL);
        if (geometry.split < geometry.pack_nr)
index 4700beacc18281413fc8d88ff35ea6ce392b0208..f497f51b2348c8b8e20dd91a98524cb5705a17ba 100755 (executable)
@@ -532,7 +532,16 @@ run_and_verify_geometric_pack () {
 
        # And verify that there are no loose objects anymore.
        git count-objects -v >count &&
-       test_grep '^count: 0$' count
+       test_grep '^count: 0$' count &&
+
+       # Verify that no orphaned .idx files were left behind. On
+       # Windows, a missing odb_to_close causes the parent to hold
+       # mmap handles on .idx files, silently preventing their
+       # deletion by the child git-repack process.
+       ls .git/objects/pack/pack-*.idx .git/objects/pack/pack-*.pack |
+       sed "s/\.pack$/.idx/" |
+       sort | uniq -u >orphaned-idx &&
+       test_must_be_empty orphaned-idx
 }
 
 test_expect_success 'geometric repacking task' '
@@ -580,8 +589,19 @@ test_expect_success 'geometric repacking task' '
 
                # And these two small packs should now be merged via the
                # geometric repack. The large packfile should remain intact.
+               cp -R .git/objects .git/objects.save &&
                run_and_verify_geometric_pack 2 &&
 
+               # On Windows, verify the same with legacy delete semantics
+               # that reject deletion of mmap-held .idx files.
+               if test_have_prereq MINGW
+               then
+                       rm -rf .git/objects &&
+                       mv .git/objects.save .git/objects &&
+                       test_env GIT_TEST_LEGACY_DELETE=1 \
+                               run_and_verify_geometric_pack 2
+               fi &&
+
                # If we now add two more objects and repack twice we should
                # then see another all-into-one repack. This time around
                # though, as we have unreachable objects, we should also see a