| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 2212c63 commit da58f39
17 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -895,22 +895,6 @@ | |||
| 895 | 895 | "lineCount": 1 | |
| 896 | 896 | } | |
| 897 | 897 | }, | |
| 898 | - { | ||
| 899 | - "code": "reportPossiblyUnboundVariable", | ||
| 900 | - "range": { | ||
| 901 | - "startColumn": 22, | ||
| 902 | - "endColumn": 37, | ||
| 903 | - "lineCount": 1 | ||
| 904 | - } | ||
| 905 | - }, | ||
| 906 | - { | ||
| 907 | - "code": "reportPossiblyUnboundVariable", | ||
| 908 | - "range": { | ||
| 909 | - "startColumn": 22, | ||
| 910 | - "endColumn": 37, | ||
| 911 | - "lineCount": 1 | ||
| 912 | - } | ||
| 913 | - }, | ||
| 914 | 898 | { | |
| 915 | 899 | "code": "reportReturnType", | |
| 916 | 900 | "range": { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,16 +25,6 @@ jobs: | |||
| 25 | 25 | python-version: "3.14t" | |
| 26 | 26 | - os-type: macos | |
| 27 | 27 | python-version: "3.15t" | |
| 28 | - - os-type: windows | ||
| 29 | - python-version: "3.13" # FIXME: Fix and enable Python 3.13-3.15 on Windows (#1955). | ||
| 30 | - - os-type: windows | ||
| 31 | - python-version: "3.14" | ||
| 32 | - - os-type: windows | ||
| 33 | - python-version: "3.14t" | ||
| 34 | - - os-type: windows | ||
| 35 | - python-version: "3.15" | ||
| 36 | - - os-type: windows | ||
| 37 | - python-version: "3.15t" | ||
| 38 | 28 | include: | |
| 39 | 29 | - os-ver: latest | |
| 40 | 30 | - os-type: ubuntu | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -576,9 +576,11 @@ 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) | ||
| 579 | + value = osp.expanduser(value).replace("\\", "/") | ||
| 580 | + git_dir = os.fspath(self._repo.git_dir).replace("\\", "/") if self._repo.git_dir else None | ||
| 580 | 581 | ||
| 581 | - if not any(value.startswith(s) for s in ["./", "/"]): | ||
| 582 | + drive, _tail = osp.splitdrive(value) | ||
| 583 | + if not drive and not any(value.startswith(s) for s in ["./", "/"]): | ||
| 582 | 584 | value = "**/" + value | |
| 583 | 585 | if value.endswith("/"): | |
| 584 | 586 | value += "**" | |
@@ -590,9 +592,8 @@ def _all_items(section: str) -> List[Tuple[str, str]]: | |||
| 590 | 592 | lambda m: f"[{m.group().lower()!r}{m.group().upper()!r}]", | |
| 591 | 593 | value, | |
| 592 | 594 | ) | |
| 593 | - if self._repo.git_dir: | ||
| 594 | - if fnmatch.fnmatchcase(os.fspath(self._repo.git_dir), value): | ||
| 595 | - paths += _all_items(section) | ||
| 595 | + if git_dir and fnmatch.fnmatchcase(git_dir, value): | ||
| 596 | + paths += _all_items(section) | ||
| 596 | 597 | ||
| 597 | 598 | elif keyword == "onbranch": | |
| 598 | 599 | try: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -34,6 +34,8 @@ | |||
| 34 | 34 | LockedFD, | |
| 35 | 35 | join_path_native, | |
| 36 | 36 | file_contents_ro, | |
| 37 | + _is_path_rooted, | ||
| 38 | + _to_relative_path, | ||
| 37 | 39 | to_native_path_linux, | |
| 38 | 40 | unbare_repo, | |
| 39 | 41 | to_bin_sha, | |
@@ -58,6 +60,7 @@ | |||
| 58 | 60 | Any, | |
| 59 | 61 | BinaryIO, | |
| 60 | 62 | Callable, | |
| 63 | + cast, | ||
| 61 | 64 | Dict, | |
| 62 | 65 | Generator, | |
| 63 | 66 | IO, | |
@@ -655,16 +658,12 @@ def _to_relative_path(self, path: PathLike) -> PathLike: | |||
| 655 | 658 | ||
| 656 | 659 | :raise ValueError: | |
| 657 | 660 | """ | |
| 658 | - if not osp.isabs(path): | ||
| 659 | - return path | ||
| 660 | 661 | if self.repo.bare: | |
| 661 | - raise InvalidGitRepositoryError("require non-bare repository") | ||
| 662 | - if not osp.normpath(path).startswith(str(self.repo.working_tree_dir)): | ||
| 663 | - raise ValueError("Absolute path %r is not in git repository at %r" % (path, self.repo.working_tree_dir)) | ||
| 664 | - result = os.path.relpath(path, self.repo.working_tree_dir) | ||
| 665 | - if os.fspath(path).endswith(os.sep) and not result.endswith(os.sep): | ||
| 666 | - result += os.sep | ||
| 667 | - return result | ||
| 662 | + drive, _tail = osp.splitdrive(os.fspath(path)) | ||
| 663 | + if drive or _is_path_rooted(path): | ||
| 664 | + raise InvalidGitRepositoryError("paths with a drive or root require a non-bare repository") | ||
| 665 | + return path | ||
| 666 | + return _to_relative_path(cast(PathLike, self.repo.working_tree_dir), path) | ||
| 668 | 667 | ||
| 669 | 668 | def _preprocess_add_items( | |
| 670 | 669 | self, items: Union[PathLike, Sequence[Union[PathLike, Blob, BaseIndexEntry, "Submodule"]]] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -29,6 +29,7 @@ | |||
| 29 | 29 | from git.util import ( | |
| 30 | 30 | IterableList, | |
| 31 | 31 | RemoteProgress, | |
| 32 | + _to_relative_path, | ||
| 32 | 33 | join_path_native, | |
| 33 | 34 | rmtree, | |
| 34 | 35 | to_native_path_linux, | |
@@ -391,23 +392,14 @@ def _to_relative_path(cls, parent_repo: "Repo", path: PathLike) -> PathLike: | |||
| 391 | 392 | :raise ValueError: | |
| 392 | 393 | If path is not contained in the parent repository's working tree. | |
| 393 | 394 | """ | |
| 394 | - path = to_native_path_linux(path) | ||
| 395 | + if parent_repo.working_tree_dir: | ||
| 396 | + path = _to_relative_path(parent_repo.working_tree_dir, path) | ||
| 397 | + else: | ||
| 398 | + path = to_native_path_linux(path) | ||
| 395 | 399 | if path.endswith("/"): | |
| 396 | 400 | path = path[:-1] | |
| 397 | - # END handle trailing slash | ||
| 398 | - | ||
| 399 | - if osp.isabs(path) and parent_repo.working_tree_dir: | ||
| 400 | - working_tree_linux = to_native_path_linux(parent_repo.working_tree_dir) | ||
| 401 | - if not path.startswith(working_tree_linux): | ||
| 402 | - raise ValueError( | ||
| 403 | - "Submodule checkout path '%s' needs to be within the parents repository at '%s'" | ||
| 404 | - % (working_tree_linux, path) | ||
| 405 | - ) | ||
| 406 | - path = path[len(working_tree_linux.rstrip("/")) + 1 :] | ||
| 407 | - if not path: | ||
| 408 | - raise ValueError("Absolute submodule path '%s' didn't yield a valid relative path" % path) | ||
| 409 | - # END verify converted relative path makes sense | ||
| 410 | - # END convert to a relative path | ||
| 401 | + if not path or path == ".": | ||
| 402 | + raise ValueError("Submodule checkout path must not be the repository root") | ||
| 411 | 403 | ||
| 412 | 404 | return path | |
| 413 | 405 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -119,7 +119,7 @@ def _get_validated_path(base: PathLike, path: PathLike) -> str: | |||
| 119 | 119 | common_path = os.path.commonpath([base_path, abs_path]) | |
| 120 | 120 | except ValueError as e: | |
| 121 | 121 | raise ValueError("Reference path %r escapes the repository" % path) from e | |
| 122 | - if os.path.normcase(common_path) != os.path.normcase(base_path): | ||
| 122 | + if common_path != base_path: | ||
| 123 | 123 | raise ValueError("Reference path %r escapes the repository" % path) | |
| 124 | 124 | return abs_path | |
| 125 | 125 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -961,8 +961,7 @@ def _get_alternates(self) -> List[str]: | |||
| 961 | 961 | :return: | |
| 962 | 962 | List of strings being pathnames of alternates | |
| 963 | 963 | """ | |
| 964 | - if self.git_dir: | ||
| 965 | - alternates_path = osp.join(self.git_dir, "objects", "info", "alternates") | ||
| 964 | + alternates_path = osp.join(self.common_dir, "objects", "info", "alternates") | ||
| 966 | 965 | ||
| 967 | 966 | if osp.exists(alternates_path): | |
| 968 | 967 | with open(alternates_path, "rb") as f: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -315,6 +315,65 @@ def join_path_native(a: PathLike, *p: PathLike) -> PathLike: | |||
| 315 | 315 | return to_native_path(join_path(a, *p)) | |
| 316 | 316 | ||
| 317 | 317 | ||
| 318 | + def _is_path_rooted(path: PathLike) -> bool: | ||
| 319 | + r"""Whether ``path`` has a root component after any drive. | ||
| 320 | + | ||
| 321 | + On Windows, ``\directory`` is rooted on the current drive without being | ||
| 322 | + absolute, while ``C:\directory`` has both a drive and a root. In contrast, | ||
| 323 | + ``directory`` and the drive-relative ``C:directory`` have no root. | ||
| 324 | + On POSIX, which has no drive concept, this simply distinguishes absolute | ||
| 325 | + paths such as ``/directory`` from relative paths such as ``directory``. | ||
| 326 | + """ | ||
| 327 | + _drive, tail = osp.splitdrive(os.fspath(path)) | ||
| 328 | + separators = (os.sep,) if os.altsep is None else (os.sep, os.altsep) | ||
| 329 | + return tail.startswith(separators) | ||
| 330 | + | ||
| 331 | + | ||
| 332 | + def _to_relative_path(root: PathLike, path: PathLike) -> str: | ||
| 333 | + r"""Return a normalized Git-style path confined to ``root``. | ||
| 334 | + | ||
| 335 | + A Windows path such as ``\directory`` is rooted but not absolute. Resolve it | ||
| 336 | + against the drive of ``root`` rather than treating it as relative to ``root``. | ||
| 337 | + Drive-relative paths such as ``C:directory`` are rejected because their meaning | ||
| 338 | + depends on process-global per-drive state. | ||
| 339 | + | ||
| 340 | + For example, with ``root`` set to ``C:\repo`` on Windows: | ||
| 341 | + | ||
| 342 | + * ``directory\file`` -> ``directory/file`` | ||
| 343 | + * ``directory\`` -> ``directory/`` | ||
| 344 | + * ``C:\repo\directory\file`` -> ``directory/file`` | ||
| 345 | + * ``\repo\directory\file`` -> ``directory/file`` | ||
| 346 | + * ``C:directory\file`` -> :exc:`ValueError` | ||
| 347 | + * ``C:\other\file`` -> :exc:`ValueError` | ||
| 348 | + | ||
| 349 | + On POSIX, ``/repo/directory/file`` under ``/repo`` similarly becomes | ||
| 350 | + ``directory/file``. A trailing separator is preserved as a Git-style ``/``. | ||
| 351 | + """ | ||
| 352 | + path_str = os.fspath(path) | ||
| 353 | + if not path_str: | ||
| 354 | + return path_str | ||
| 355 | + | ||
| 356 | + drive, _tail = osp.splitdrive(path_str) | ||
| 357 | + rooted = _is_path_rooted(path_str) | ||
| 358 | + if drive and not rooted: | ||
| 359 | + raise ValueError("Drive-relative path %r is not supported" % path_str) | ||
| 360 | + | ||
| 361 | + root_abs = osp.abspath(os.fspath(root)) | ||
| 362 | + path_abs = osp.abspath(osp.join(root_abs, path_str)) | ||
| 363 | + try: | ||
| 364 | + common_path = osp.commonpath([root_abs, path_abs]) | ||
| 365 | + except ValueError as e: | ||
| 366 | + raise ValueError("Path %r is not in repository at %r" % (path_str, root_abs)) from e | ||
| 367 | + if common_path != root_abs: | ||
| 368 | + raise ValueError("Path %r is not in repository at %r" % (path_str, root_abs)) | ||
| 369 | + | ||
| 370 | + relative_path = to_native_path_linux(osp.relpath(path_abs, root_abs)) | ||
| 371 | + separators = (os.sep,) if os.altsep is None else (os.sep, os.altsep) | ||
| 372 | + if path_str.endswith(separators) and relative_path != "." and not relative_path.endswith("/"): | ||
| 373 | + relative_path += "/" | ||
| 374 | + return relative_path | ||
| 375 | + | ||
| 376 | + | ||
| 318 | 377 | def assure_directory_exists(path: PathLike, is_file: bool = False) -> bool: | |
| 319 | 378 | """Make sure that the directory pointed to by path exists. | |
| 320 | 379 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -276,14 +276,14 @@ def test_iteration(self): | |||
| 276 | 276 | assert ltd_commits and len(ltd_commits) < len(all_commits) | |
| 277 | 277 | ||
| 278 | 278 | # Show commits of multiple paths, resulting in a union of commits. | |
| 279 | - less_ltd_commits = list(Commit.iter_items(self.rorepo, "master", paths=("CHANGES", "AUTHORS"))) | ||
| 279 | + less_ltd_commits = list(Commit.iter_items(self.rorepo, "HEAD", paths=("CHANGES", "AUTHORS"))) | ||
| 280 | 280 | assert len(ltd_commits) < len(less_ltd_commits) | |
| 281 | 281 | ||
| 282 | 282 | class Child(Commit): | |
| 283 | 283 | def __init__(self, *args, **kwargs): | |
| 284 | 284 | super().__init__(*args, **kwargs) | |
| 285 | 285 | ||
| 286 | - child_commits = list(Child.iter_items(self.rorepo, "master", paths=("CHANGES", "AUTHORS"))) | ||
| 286 | + child_commits = list(Child.iter_items(self.rorepo, "HEAD", paths=("CHANGES", "AUTHORS"))) | ||
| 287 | 287 | assert type(child_commits[0]) is Child | |
| 288 | 288 | ||
| 289 | 289 | def test_iter_items(self): | |
@@ -536,7 +536,7 @@ def test_trailers(self): | |||
| 536 | 536 | ), | |
| 537 | 537 | ] | |
| 538 | 538 | for msg in msgs: | |
| 539 | - commit = copy.copy(self.rorepo.commit("master")) | ||
| 539 | + commit = copy.copy(self.rorepo.commit("HEAD")) | ||
| 540 | 540 | commit.message = msg | |
| 541 | 541 | assert commit.trailers_list == [ | |
| 542 | 542 | (KEY_1, VALUE_1_1), | |
@@ -559,13 +559,13 @@ def test_trailers(self): | |||
| 559 | 559 | ] | |
| 560 | 560 | ||
| 561 | 561 | for msg in msgs: | |
| 562 | - commit = copy.copy(self.rorepo.commit("master")) | ||
| 562 | + commit = copy.copy(self.rorepo.commit("HEAD")) | ||
| 563 | 563 | commit.message = msg | |
| 564 | 564 | assert commit.trailers_list == [] | |
| 565 | 565 | assert commit.trailers_dict == {} | |
| 566 | 566 | ||
| 567 | 567 | # Check that only the last key value paragraph is evaluated. | |
| 568 | - commit = copy.copy(self.rorepo.commit("master")) | ||
| 568 | + commit = copy.copy(self.rorepo.commit("HEAD")) | ||
| 569 | 569 | commit.message = f"Subject\n\nMultiline\nBody\n\n{KEY_1}: {VALUE_1_1}\n\n{KEY_2}: {VALUE_2}\n" | |
| 570 | 570 | assert commit.trailers_list == [(KEY_2, VALUE_2)] | |
| 571 | 571 | assert commit.trailers_dict == {KEY_2: [VALUE_2]} | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,14 +7,13 @@ | |||
| 7 | 7 | import io | |
| 8 | 8 | import os | |
| 9 | 9 | import os.path as osp | |
| 10 | - import sys | ||
| 11 | 10 | from unittest import mock | |
| 12 | 11 | ||
| 13 | 12 | import pytest | |
| 14 | 13 | ||
| 15 | 14 | from git import GitConfigParser | |
| 16 | 15 | from git.config import _OMD, cp | |
| 17 | - from git.util import rmfile | ||
| 16 | + from git.util import cwd, rmfile | ||
| 18 | 17 | ||
| 19 | 18 | from test.lib import SkipTest, TestCase, fixture_path, with_rw_directory | |
| 20 | 19 | ||
@@ -374,6 +373,22 @@ def test_config_relative_path_include(self, rw_dir): | |||
| 374 | 373 | with GitConfigParser(relative_config_path, read_only=True) as cr: | |
| 375 | 374 | assert cr.get_value("included", "value") == "included" | |
| 376 | 375 | ||
| 376 | + @pytest.mark.skipif(os.name != "nt", reason="Specifically for Windows drive-rooted paths.") | ||
| 377 | + @with_rw_directory | ||
| 378 | + def test_config_drive_rooted_path_include(self, rw_dir): | ||
| 379 | + with cwd(rw_dir): | ||
| 380 | + included_path = osp.join(rw_dir, "included") | ||
| 381 | + with GitConfigParser(included_path, read_only=False) as cw: | ||
| 382 | + cw.set_value("included", "value", "included") | ||
| 383 | + | ||
| 384 | + _drive, rooted_included_path = osp.splitdrive(included_path) | ||
| 385 | + config_path = osp.join(rw_dir, "config") | ||
| 386 | + with GitConfigParser(config_path, read_only=False) as cw: | ||
| 387 | + cw.set_value("include", "path", rooted_included_path) | ||
| 388 | + | ||
| 389 | + with GitConfigParser(config_path, read_only=True) as cr: | ||
| 390 | + assert cr.get_value("included", "value") == "included" | ||
| 391 | + | ||
| 377 | 392 | @with_rw_directory | |
| 378 | 393 | def test_multiple_include_paths_with_same_key(self, rw_dir): | |
| 379 | 394 | """Test that multiple 'path' entries under [include] are all respected. | |
@@ -411,11 +426,6 @@ def test_multiple_include_paths_with_same_key(self, rw_dir): | |||
| 411 | 426 | assert cr.get_value("user", "name") == "from-inc1" | |
| 412 | 427 | assert cr.get_value("core", "bar") == "from-inc2" | |
| 413 | 428 | ||
| 414 | - @pytest.mark.xfail( | ||
| 415 | - sys.platform == "win32", | ||
| 416 | - reason='Second config._has_includes() assertion fails (for "config is included if path is matching git_dir")', | ||
| 417 | - raises=AssertionError, | ||
| 418 | - ) | ||
| 419 | 429 | @with_rw_directory | |
| 420 | 430 | def test_conditional_includes_from_git_dir(self, rw_dir): | |
| 421 | 431 | # Initiate repository path. | |
@@ -443,6 +453,14 @@ def test_conditional_includes_from_git_dir(self, rw_dir): | |||
| 443 | 453 | assert config._has_includes() | |
| 444 | 454 | assert config._included_paths() == [("path", path2)] | |
| 445 | 455 | ||
| 456 | + # Ensure that Git's forward-slash syntax matches native Windows paths. | ||
| 457 | + with open(path1, "w") as stream: | ||
| 458 | + stream.write(template.format("gitdir", git_dir.replace("\\", "/"), path2)) | ||
| 459 | + | ||
| 460 | + with GitConfigParser(path1, repo=repo) as config: | ||
| 461 | + assert config._has_includes() | ||
| 462 | + assert config._included_paths() == [("path", path2)] | ||
| 463 | + | ||
| 446 | 464 | # Ensure that config is ignored if case is incorrect. | |
| 447 | 465 | with open(path1, "w") as stream: | |
| 448 | 466 | stream.write(template.format("gitdir", git_dir.upper(), path2)) | |
| Back | FazBrowse Home | New Git URL |
0 commit comments