| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent da58f39 commit 308d371
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -576,8 +576,10 @@ def _all_items(section: str) -> List[Tuple[str, str]]: | |||
| 576 | 576 | value = match.group(2).strip() | |
| 577 | 577 | ||
| 578 | 578 | if keyword in ["gitdir", "gitdir/i"]: | |
| 579 | - value = osp.expanduser(value).replace("\\", "/") | ||
| 580 | - git_dir = os.fspath(self._repo.git_dir).replace("\\", "/") if self._repo.git_dir else None | ||
| 579 | + value = osp.expanduser(value) | ||
| 580 | + git_dir = os.fspath(self._repo.git_dir) if self._repo.git_dir else None | ||
| 581 | + if sys.platform == "win32": | ||
| 582 | + git_dir = git_dir.replace("\\", "/") if git_dir else None | ||
| 581 | 583 | ||
| 582 | 584 | drive, _tail = osp.splitdrive(value) | |
| 583 | 585 | if not drive and not any(value.startswith(s) for s in ["./", "/"]): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -316,17 +316,20 @@ def join_path_native(a: PathLike, *p: PathLike) -> PathLike: | |||
| 316 | 316 | ||
| 317 | 317 | ||
| 318 | 318 | def _is_path_rooted(path: PathLike) -> bool: | |
| 319 | - r"""Whether ``path`` has a root component after any drive. | ||
| 319 | + r"""Whether ``path`` has a root, including one encoded in a UNC drive. | ||
| 320 | 320 | ||
| 321 | 321 | On Windows, ``\directory`` is rooted on the current drive without being | |
| 322 | 322 | absolute, while ``C:\directory`` has both a drive and a root. In contrast, | |
| 323 | 323 | ``directory`` and the drive-relative ``C:directory`` have no root. | |
| 324 | + UNC paths are rooted: ``\\server\share`` stores the share in the drive | ||
| 325 | + returned by :func:`os.path.splitdrive`, while ``\\server\share\directory`` | ||
| 326 | + additionally has a rooted tail. | ||
| 324 | 327 | On POSIX, which has no drive concept, this simply distinguishes absolute | |
| 325 | 328 | paths such as ``/directory`` from relative paths such as ``directory``. | |
| 326 | 329 | """ | |
| 327 | - _drive, tail = osp.splitdrive(os.fspath(path)) | ||
| 330 | + drive, tail = osp.splitdrive(os.fspath(path)) | ||
| 328 | 331 | separators = (os.sep,) if os.altsep is None else (os.sep, os.altsep) | |
| 329 | - return tail.startswith(separators) | ||
| 332 | + return tail.startswith(separators) or drive.startswith(separators) | ||
| 330 | 333 | ||
| 331 | 334 | ||
| 332 | 335 | def _to_relative_path(root: PathLike, path: PathLike) -> str: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,6 +7,7 @@ | |||
| 7 | 7 | import io | |
| 8 | 8 | import os | |
| 9 | 9 | import os.path as osp | |
| 10 | + import sys | ||
| 10 | 11 | from unittest import mock | |
| 11 | 12 | ||
| 12 | 13 | import pytest | |
@@ -430,6 +431,7 @@ def test_multiple_include_paths_with_same_key(self, rw_dir): | |||
| 430 | 431 | def test_conditional_includes_from_git_dir(self, rw_dir): | |
| 431 | 432 | # Initiate repository path. | |
| 432 | 433 | git_dir = osp.join(rw_dir, "target1", "repo1") | |
| 434 | + git_dir_pattern = git_dir.replace("\\", "/") | ||
| 433 | 435 | os.makedirs(git_dir) | |
| 434 | 436 | ||
| 435 | 437 | # Initiate mocked repository. | |
@@ -441,37 +443,42 @@ def test_conditional_includes_from_git_dir(self, rw_dir): | |||
| 441 | 443 | template = '[includeIf "{}:{}"]\n path={}\n' | |
| 442 | 444 | ||
| 443 | 445 | with open(path1, "w") as stream: | |
| 446 | + # on Windows, this writes a backslash pattern. | ||
| 444 | 447 | stream.write(template.format("gitdir", git_dir, path2)) | |
| 445 | 448 | ||
| 446 | 449 | # Ensure that config is ignored if no repo is set. | |
| 447 | 450 | with GitConfigParser(path1) as config: | |
| 448 | 451 | assert not config._has_includes() | |
| 449 | 452 | assert config._included_paths() == [] | |
| 450 | 453 | ||
| 451 | - # Ensure that config is included if path is matching git_dir. | ||
| 452 | - with GitConfigParser(path1, repo=repo) as config: | ||
| 453 | - assert config._has_includes() | ||
| 454 | - assert config._included_paths() == [("path", path2)] | ||
| 454 | + # Git uses forward slashes in gitdir patterns on every platform: | ||
| 455 | + # backslashes escape the next pattern character rather than separate | ||
| 456 | + # path components. On Windows, GitPython therefore normalizes git_dir | ||
| 457 | + # to forward slashes but leaves this backslash pattern unchanged, so | ||
| 458 | + # the two do not match and no path is included. | ||
| 459 | + with GitConfigParser(path1, repo=repo, merge_includes=False) as config: | ||
| 460 | + expected_paths = [] if sys.platform == "win32" else [("path", path2)] | ||
| 461 | + assert config._included_paths() == expected_paths | ||
| 455 | 462 | ||
| 456 | 463 | # Ensure that Git's forward-slash syntax matches native Windows paths. | |
| 457 | 464 | with open(path1, "w") as stream: | |
| 458 | - stream.write(template.format("gitdir", git_dir.replace("\\", "/"), path2)) | ||
| 465 | + stream.write(template.format("gitdir", git_dir_pattern, path2)) | ||
| 459 | 466 | ||
| 460 | 467 | with GitConfigParser(path1, repo=repo) as config: | |
| 461 | 468 | assert config._has_includes() | |
| 462 | 469 | assert config._included_paths() == [("path", path2)] | |
| 463 | 470 | ||
| 464 | 471 | # Ensure that config is ignored if case is incorrect. | |
| 465 | 472 | with open(path1, "w") as stream: | |
| 466 | - stream.write(template.format("gitdir", git_dir.upper(), path2)) | ||
| 473 | + stream.write(template.format("gitdir", git_dir_pattern.upper(), path2)) | ||
| 467 | 474 | ||
| 468 | 475 | with GitConfigParser(path1, repo=repo) as config: | |
| 469 | 476 | assert not config._has_includes() | |
| 470 | 477 | assert config._included_paths() == [] | |
| 471 | 478 | ||
| 472 | 479 | # Ensure that config is included if case is ignored. | |
| 473 | 480 | with open(path1, "w") as stream: | |
| 474 | - stream.write(template.format("gitdir/i", git_dir.upper(), path2)) | ||
| 481 | + stream.write(template.format("gitdir/i", git_dir_pattern.upper(), path2)) | ||
| 475 | 482 | ||
| 476 | 483 | with GitConfigParser(path1, repo=repo) as config: | |
| 477 | 484 | assert config._has_includes() | |
@@ -501,6 +508,20 @@ def test_conditional_includes_from_git_dir(self, rw_dir): | |||
| 501 | 508 | assert config._has_includes() | |
| 502 | 509 | assert config._included_paths() == [("path", path2)] | |
| 503 | 510 | ||
| 511 | + @with_rw_directory | ||
| 512 | + def test_conditional_includes_do_not_treat_backslashes_as_separators(self, rw_dir): | ||
| 513 | + git_dir = osp.join(rw_dir, "target", "repo") | ||
| 514 | + repo = mock.Mock(git_dir=git_dir) | ||
| 515 | + config_path = osp.join(rw_dir, "config") | ||
| 516 | + included_path = osp.join(rw_dir, "included") | ||
| 517 | + pattern = git_dir.replace("\\", "/").replace("/target/repo", R"/target\repo") | ||
| 518 | + | ||
| 519 | + with open(config_path, "w") as stream: | ||
| 520 | + stream.write(f'[includeIf "gitdir:{pattern}"]\n path={included_path}\n') | ||
| 521 | + | ||
| 522 | + with GitConfigParser(config_path, repo=repo, merge_includes=False) as config: | ||
| 523 | + assert config._included_paths() == [] | ||
| 524 | + | ||
| 504 | 525 | @with_rw_directory | |
| 505 | 526 | def test_conditional_includes_from_branch_name(self, rw_dir): | |
| 506 | 527 | # Initiate mocked branch. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1090,6 +1090,15 @@ class Mocked: | |||
| 1090 | 1090 | actual_path = index._to_relative_path(path) | |
| 1091 | 1091 | self.assertEqual(expected_path, actual_path) | |
| 1092 | 1092 | ||
| 1093 | + @pytest.mark.skipif(sys.platform != "win32", reason="Specifically for Windows.") | ||
| 1094 | + def test__to_relative_path_windows_unc_share_root(self): | ||
| 1095 | + for repo_root in [R"\\server\share", R"\\?\UNC\server\share"]: | ||
| 1096 | + with self.subTest(repo_root=repo_root): | ||
| 1097 | + repo = mock.Mock(bare=False, git_dir=repo_root, working_tree_dir=repo_root) | ||
| 1098 | + index = IndexFile(repo) | ||
| 1099 | + | ||
| 1100 | + self.assertEqual(index._to_relative_path(repo_root), ".") | ||
| 1101 | + | ||
| 1093 | 1102 | @pytest.mark.skipif(sys.platform != "win32", reason="Specifically for Windows.") | |
| 1094 | 1103 | @with_rw_directory | |
| 1095 | 1104 | def test__to_relative_path_windows_path_kinds(self, rw_dir): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments