| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 43a43cd commit 84d33f3
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -444,6 +444,20 @@ def _checked_abspath( | |||
| 444 | 444 | raise ValueError("Submodule path %r contains a symbolic link" % relative_path) | |
| 445 | 445 | return path | |
| 446 | 446 | ||
| 447 | + @staticmethod | ||
| 448 | + def _renames(source: PathLike, destination: PathLike) -> None: | ||
| 449 | + os.makedirs(osp.dirname(destination), exist_ok=True) | ||
| 450 | + os.rename(source, destination) | ||
| 451 | + # Match renames() cleanup, but stop before directory symlinks: Windows | ||
| 452 | + # rmdir() removes the link even when its target is nonempty. | ||
| 453 | + parent = osp.dirname(source) | ||
| 454 | + while parent and not osp.islink(parent): | ||
| 455 | + try: | ||
| 456 | + os.rmdir(parent) | ||
| 457 | + except OSError: | ||
| 458 | + break | ||
| 459 | + parent = osp.dirname(parent) | ||
| 460 | + | ||
| 447 | 461 | @classmethod | |
| 448 | 462 | def _write_git_file_and_module_config(cls, working_tree_dir: PathLike, module_abspath: PathLike) -> None: | |
| 449 | 463 | """Write a ``.git`` file containing a (preferably) relative path to the actual | |
@@ -1120,7 +1134,7 @@ def move(self, module_path: PathLike, configuration: bool = True, module: bool = | |||
| 1120 | 1134 | # Move the module into place if possible. | |
| 1121 | 1135 | renamed_module = False | |
| 1122 | 1136 | if module and osp.exists(cur_path): | |
| 1123 | - os.renames(cur_path, module_checkout_abspath) | ||
| 1137 | + self._renames(cur_path, module_checkout_abspath) | ||
| 1124 | 1138 | renamed_module = True | |
| 1125 | 1139 | ||
| 1126 | 1140 | if osp.isfile(osp.join(module_checkout_abspath, ".git")): | |
@@ -1151,7 +1165,7 @@ def move(self, module_path: PathLike, configuration: bool = True, module: bool = | |||
| 1151 | 1165 | # END handle configuration flag | |
| 1152 | 1166 | except Exception: | |
| 1153 | 1167 | if renamed_module: | |
| 1154 | - os.renames(module_checkout_abspath, cur_path) | ||
| 1168 | + self._renames(module_checkout_abspath, cur_path) | ||
| 1155 | 1169 | # END undo module renaming | |
| 1156 | 1170 | raise | |
| 1157 | 1171 | # END handle undo rename | |
@@ -1504,10 +1518,10 @@ def rename(self, new_name: str) -> "Submodule": | |||
| 1504 | 1518 | # Let's be sure the submodule name is not so obviously tied to a directory. | |
| 1505 | 1519 | if str(destination_module_abspath).startswith(str(mod.git_dir)): | |
| 1506 | 1520 | tmp_dir = self._module_abspath(self.repo, self.path, str(uuid.uuid4())) | |
| 1507 | - os.renames(source_dir, tmp_dir) | ||
| 1521 | + self._renames(source_dir, tmp_dir) | ||
| 1508 | 1522 | source_dir = tmp_dir | |
| 1509 | 1523 | # END handle self-containment | |
| 1510 | - os.renames(source_dir, destination_module_abspath) | ||
| 1524 | + self._renames(source_dir, destination_module_abspath) | ||
| 1511 | 1525 | if mod.working_tree_dir: | |
| 1512 | 1526 | self._write_git_file_and_module_config(mod.working_tree_dir, destination_module_abspath) | |
| 1513 | 1527 | # END move separate git repository | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -252,14 +252,29 @@ def test_submodule_rejects_checkout_and_gitmodules_symlinks(movable_submodule, t | |||
| 252 | 252 | assert (root / "moved").is_dir() | |
| 253 | 253 | ||
| 254 | 254 | ||
| 255 | - def test_submodule_allows_symlink_above_worktree(movable_submodule, tmp_path): | ||
| 255 | + @pytest.fixture | ||
| 256 | + def windows_directory_symlink_removal(monkeypatch): | ||
| 257 | + """Exercise Windows rmdir semantics on POSIX, where rmdir rejects symlinks.""" | ||
| 258 | + if sys.platform != "win32": | ||
| 259 | + original_rmdir = os.rmdir | ||
| 260 | + | ||
| 261 | + def rmdir(path, *args, **kwargs): | ||
| 262 | + if osp.islink(path): | ||
| 263 | + return os.unlink(path, *args, **kwargs) | ||
| 264 | + return original_rmdir(path, *args, **kwargs) | ||
| 265 | + | ||
| 266 | + monkeypatch.setattr(os, "rmdir", rmdir) | ||
| 267 | + | ||
| 268 | + | ||
| 269 | + def test_submodule_allows_symlink_above_worktree(movable_submodule, tmp_path, windows_directory_symlink_removal): | ||
| 256 | 270 | """Allow adding and moving submodules when the parent is opened through a symlink.""" | |
| 257 | 271 | sm = movable_submodule | |
| 258 | 272 | alias = tmp_path / "alias" | |
| 259 | 273 | alias.symlink_to(sm.repo.working_tree_dir, target_is_directory=True) | |
| 260 | 274 | with git.Repo(alias) as parent: | |
| 261 | 275 | added = Submodule.add(parent, "new", "new", sm.url) | |
| 262 | 276 | added.move("moved") | |
| 277 | + assert alias.is_symlink() | ||
| 263 | 278 | with added.module() as module: | |
| 264 | 279 | assert Path(module.git.rev_parse("--show-toplevel")).resolve() == Path(added.abspath).resolve() | |
| 265 | 280 | assert Path(added.abspath, "file").read_text() == "content" | |
@@ -296,7 +311,9 @@ def test_submodule_allows_metadata_destination_symlinks(movable_submodule, tmp_p | |||
| 296 | 311 | ||
| 297 | 312 | @pytest.mark.parametrize("kind", ["modules", "intermediate", "leaf", "gitfile", "config", "alias"]) | |
| 298 | 313 | @pytest.mark.parametrize("operation", ["update", "move", "rename", "remove"]) | |
| 299 | - def test_submodule_allows_existing_metadata_symlinks(movable_submodule, tmp_path, kind, operation): | ||
| 314 | + def test_submodule_allows_existing_metadata_symlinks( | ||
| 315 | + movable_submodule, tmp_path, kind, operation, windows_directory_symlink_removal | ||
| 316 | + ): | ||
| 300 | 317 | """Keep submodule operations compatible with existing symlinks in Git metadata. | |
| 301 | 318 | ||
| 302 | 319 | Cover linked metadata directories, gitfiles, configs, and internal aliases. | |
@@ -336,6 +353,12 @@ def test_submodule_allows_existing_metadata_symlinks(movable_submodule, tmp_path | |||
| 336 | 353 | sm.move("moved") | |
| 337 | 354 | else: | |
| 338 | 355 | sm.rename("renamed") | |
| 356 | + if kind == "modules" or (operation == "rename" and kind in ("intermediate", "alias")): | ||
| 357 | + assert link.is_symlink() | ||
| 358 | + assert link.is_dir() | ||
| 359 | + if operation == "rename" and kind == "leaf": | ||
| 360 | + assert (modules / "renamed").is_symlink() | ||
| 361 | + assert target.is_dir() | ||
| 339 | 362 | with sm.module() as module: | |
| 340 | 363 | assert Path(module.git.rev_parse("--show-toplevel")).resolve() == Path(sm.abspath).resolve() | |
| 341 | 364 | assert Path(sm.abspath, "file").read_text() == "content" | |
| Back | FazBrowse Home | New Git URL |
0 commit comments