Commit 2bfd829
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
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
444 | 445 | | |
445 | 446 | | |
446 | 447 | | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
447 | 462 | | |
448 | 463 | | |
449 | 464 | | |
| |||
468 | 483 | | |
469 | 484 | | |
470 | 485 | | |
471 | | - | |
472 | | - | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
473 | 489 | | |
474 | 490 | | |
475 | 491 | | |
| |||
685 | 701 | | |
686 | 702 | | |
687 | 703 | | |
688 | | - | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
689 | 707 | | |
690 | 708 | | |
691 | 709 | | |
| |||
1120 | 1138 | | |
1121 | 1139 | | |
1122 | 1140 | | |
1123 | | - | |
| 1141 | + | |
1124 | 1142 | | |
1125 | 1143 | | |
1126 | 1144 | | |
| |||
1151 | 1169 | | |
1152 | 1170 | | |
1153 | 1171 | | |
1154 | | - | |
| 1172 | + | |
1155 | 1173 | | |
1156 | 1174 | | |
1157 | 1175 | | |
| |||
1239 | 1257 | | |
1240 | 1258 | | |
1241 | 1259 | | |
1242 | | - | |
| 1260 | + | |
1243 | 1261 | | |
1244 | 1262 | | |
1245 | 1263 | | |
| |||
1504 | 1522 | | |
1505 | 1523 | | |
1506 | 1524 | | |
1507 | | - | |
| 1525 | + | |
1508 | 1526 | | |
1509 | 1527 | | |
1510 | | - | |
| 1528 | + | |
1511 | 1529 | | |
1512 | 1530 | | |
1513 | 1531 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
252 | 252 | | |
253 | 253 | | |
254 | 254 | | |
255 | | - | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
256 | 302 | | |
257 | 303 | | |
258 | 304 | | |
259 | 305 | | |
260 | 306 | | |
261 | 307 | | |
262 | 308 | | |
| 309 | + | |
263 | 310 | | |
264 | 311 | | |
265 | 312 | | |
266 | 313 | | |
267 | 314 | | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
268 | 326 | | |
269 | | - | |
| 327 | + | |
270 | 328 | | |
271 | 329 | | |
272 | 330 | | |
| |||
296 | 354 | | |
297 | 355 | | |
298 | 356 | | |
299 | | - | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
300 | 360 | | |
301 | 361 | | |
302 | 362 | | |
| |||
336 | 396 | | |
337 | 397 | | |
338 | 398 | | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
339 | 405 | | |
340 | 406 | | |
341 | 407 | | |
| |||
0 commit comments