Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions builtin/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
47 changes: 45 additions & 2 deletions compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,20 +449,63 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
return wbuf;
}

/*
* Use SetFileInformationByHandle(FileDispositionInfo) to force legacy
* (non-POSIX) delete semantics. On Windows 11, DeleteFileW() uses POSIX
* delete semantics internally, allowing deletion even with active
* MapViewOfFile views. This helper simulates Windows 10 behavior where
* deletion fails if a file mapping exists.
*
* Returns nonzero on success (like DeleteFileW), 0 on failure.
*/
static int legacy_delete_file(const wchar_t *wpathname)
{
FILE_DISPOSITION_INFO fdi = { TRUE };
DWORD gle;
HANDLE h = CreateFileW(wpathname, DELETE,
FILE_SHARE_READ | FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (h == INVALID_HANDLE_VALUE)
return 0;

if (SetFileInformationByHandle(h, FileDispositionInfo,
&fdi, sizeof(fdi))) {
CloseHandle(h);
return 1;
}
gle = GetLastError();
CloseHandle(h);
SetLastError(gle);
return 0;
}

static int try_delete_file(const wchar_t *wpathname, int use_legacy)
{
if (use_legacy)
return legacy_delete_file(wpathname);
return DeleteFileW(wpathname);
}

int mingw_unlink(const char *pathname, int handle_in_use_error)
{
static int use_legacy_delete = -1;
int tries = 0;
wchar_t wpathname[MAX_PATH];
if (xutftowcs_path(wpathname, pathname) < 0)
return -1;

if (DeleteFileW(wpathname))
if (use_legacy_delete < 0)
use_legacy_delete = !!getenv("GIT_TEST_LEGACY_DELETE");

if (try_delete_file(wpathname, use_legacy_delete))
return 0;

do {
/* read-only files cannot be removed */
_wchmod(wpathname, 0666);
if (!_wunlink(wpathname))
if (try_delete_file(wpathname, use_legacy_delete))
return 0;
if (!is_file_in_use_error(GetLastError()))
break;
Expand Down
22 changes: 21 additions & 1 deletion t/t7900-maintenance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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' '
Expand Down Expand Up @@ -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
Expand Down
Loading