fix: Preserve submodule paths and release checkout handles on Windows · gitpython-developers/GitPython@2bfd829 · GitHub
Skip to content

Commit 2bfd829

Browse files
Byroncodex
andcommitted
fix: Preserve submodule paths and release checkout handles on Windows
A quick rubber-stamp, admittedly. V4 will probably review all tests and make it more proper, if there can be such a thing in python anyway. <!-- agent --> Both Windows failures came from os.renames() pruning a directory symlink above the source after the rename succeeded. Unlike POSIX, Windows rmdir() can remove a directory symlink even when its target is nonempty. Moving a checkout through a worktree alias therefore deleted the alias and broke configuration updates and rollback. Renaming metadata through a linked .git/modules directory deleted that link and broke config.lock creation. Route checkout moves, rollback, and metadata renames through one helper. It creates destination parents and renames the source, then prunes empty source parents only until it reaches a symlink or a directory it cannot remove. This keeps ordinary empty-directory cleanup while preserving parent links and their targets, including targets that become empty. Leaf symlinks continue to move as links rather than moving their targets. Exercise Windows directory-symlink removal semantics on POSIX in the existing compatibility tests, and use native behavior on Windows. Both reported failures reproduced locally before the fix. Strengthen assertions that worktree and metadata parent aliases survive, their targets remain directories, and leaf metadata symlinks move without moving their targets. A further Windows run exposed a separate sharing violation during the checkout move. Submodule.add() read HEAD through its temporary Repo but left that Repo's persistent cat-file processes open. Those processes can hold the checkout as their current directory and prevent its rename. Close the owned Repo with a context manager when reading HEAD, including on read failure, instead of waiting for garbage collection. Add a regression that observes the real cat-file processes started for the new checkout and requires them to have exited before add() returns. It failed before the fix and now passes, along with the immediate move. The remaining metadata failures also reproduce with Python 3.7's Windows path semantics: ntpath.realpath is an alias of abspath and does not resolve symlinks. Relative core.worktree values were calculated from the metadata alias instead of the repository directory Git actually opens. This broke add/reconnect HEAD reads and made moves and renames point at nonexistent worktrees. The SHA/dubious-ownership message was a secondary read failure. Use pathlib.Path.resolve(), which resolves Windows symlinks on Python 3.7, for both endpoints of gitfile/config rewrites and for metadata removal. Run metadata and worktree-alias tests with native and simulated Windows 3.7 realpath behavior. The simulation reproduced all eight reported failures plus a leaf-symlink removal failure before this change. Reference: https://github.com/python/cpython/blob/3.7/Lib/ntpath.py and https://github.com/python/cpython/blob/3.7/Lib/pathlib.py. Validation: 183 passed, 3 skipped, and 1 expected failure across the submodule and diff suites plus the commit-message hook success test on macOS. Ruff lint and formatting, mypy for the changed module, and git diff --check passed. Native Windows validation remains for CI. Assisted-by: GPT 6.0 Co-authored-by: GPT 6.0 <codex@openai.com>
1 parent 43a43cd commit 2bfd829

2 files changed

Lines changed: 95 additions & 11 deletions

File tree

git/objects/submodule/base.py

Lines changed: 26 additions & 8 deletions

test/test_submodule.py

Lines changed: 69 additions & 3 deletions

0 commit comments

Comments
 (0)