| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ea24b47 commit f6779db
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -172,7 +172,7 @@ jobs: | |||
| 172 | 172 | ||
| 173 | 173 | - name: Test with pytest | |
| 174 | 174 | run: | | |
| 175 | - pytest --color=yes -p no:sugar --instafail -vv | ||
| 175 | + pytest --color=yes -p no:sugar --instafail -vv --durations=30 | ||
| 176 | 176 | continue-on-error: false | |
| 177 | 177 | ||
| 178 | 178 | - name: Documentation | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -458,6 +458,57 @@ their focused tests. No full-suite rerun or fresh timed benchmark is claimed | |||
| 458 | 458 | for this follow-up. Logs, backend reports and the count comparison are under | |
| 459 | 459 | `.cache/gix-capability-followup/` (`15-final-*` and `16-benchmark-*`). | |
| 460 | 460 | ||
| 461 | + ### Windows CI process overhead | ||
| 462 | + | ||
| 463 | + The Python 3.12 jobs in [PR #2274's Python package run](https://github.com/gitpython-developers/GitPython/actions/runs/37628358853) | ||
| 464 | + spent 57m 25s in pytest on Windows and 5m 2s on Ubuntu. Their process reports | ||
| 465 | + counted 85,777 and 82,683 `Git.execute` launches respectively. The Windows | ||
| 466 | + submodule test modules accounted for approximately 36 minutes, based on the | ||
| 467 | + timestamps of their test results; the delay was spread across many operations. | ||
| 468 | + | ||
| 469 | + A local Windows profile of `test_file_handle_leaks` and | ||
| 470 | + `test_update_no_fetch_is_recursive[root-no-fetch]`, with CI's coverage and | ||
| 471 | + pytest options, counted 2,447 launches. About 65 of 77 seconds were spent in | ||
| 472 | + Git command execution, including 44 seconds in repository construction; | ||
| 473 | + forced garbage collection accounted for about three seconds. Combining | ||
| 474 | + `--show-ref-format`, `--show-object-format`, `--is-bare-repository` and | ||
| 475 | + `--git-common-dir` in one `rev-parse` invocation reduced the same selection to | ||
| 476 | + 2,009 launches: three saved for each of 146 repository opens. Git still | ||
| 477 | + computes all four values. The common-directory path is last and split only | ||
| 478 | + after the three scalar fields, preserving paths containing newlines. | ||
| 479 | + | ||
| 480 | + Three paired measurements of 20 ordinary repository opens on Windows gave | ||
| 481 | + median times of 300.74 ms before and 221.18 ms after, with matching metadata | ||
| 482 | + and 11 versus eight launches per open. These use CPython 3.12.13 and Git | ||
| 483 | + 2.55.0.windows.3; timing is observational, while the launch reduction is | ||
| 484 | + checked by the regression tests. | ||
| 485 | + | ||
| 486 | + A 50-call `git version` probe on the same host measured median times of | ||
| 487 | + 17.49 ms per call on Windows and 1.27 ms in Ubuntu WSL, which uses CPython | ||
| 488 | + 3.14.4 and Git 2.53.0. Even trivial Git commands carry appreciable startup | ||
| 489 | + cost on Windows. | ||
| 490 | + | ||
| 491 | + This optimizes the CLI backend's construction path. Supported Gix discovery | ||
| 492 | + still uses its native metadata and the separate Git format-validation query; | ||
| 493 | + the Gix launch ceilings and open compatibility bugs remain unchanged. | ||
| 494 | + CI now includes `--durations=30` so subsequent slow tests are visible directly. | ||
| 495 | + Local Windows reproduction must retain CI's `core.autocrlf=true` setting and | ||
| 496 | + place pytest's temporary directories outside a Git checkout. Isolating all | ||
| 497 | + Git configuration without restoring that setting changes the newline test; | ||
| 498 | + placing `--basetemp` under this checkout lets empty-directory Git probes | ||
| 499 | + discover the parent repository instead. | ||
| 500 | + | ||
| 501 | + The full local baseline at `105114db` recorded 86,032 launches in 3,990.28s; | ||
| 502 | + the two submodule modules accounted for 2,421.59s. It had 1,652 passing tests | ||
| 503 | + and the two harness failures described above, both of which passed unchanged | ||
| 504 | + after correcting the setup. The patched run exited successfully with 1,662 | ||
| 505 | + passed, 60 skipped, nine expected failures, two unexpected passes and 40 | ||
| 506 | + passing subtests. It recorded 77,732 launches in 3,715.16s. These runs | ||
| 507 | + overlapped, so their wall times are not a controlled comparison. The paired | ||
| 508 | + repository-open benchmark above measures the affected operation separately. | ||
| 509 | + Logs, profiles, JUnit results and backend reports are retained locally under | ||
| 510 | + `.cache/ci-performance/`. | ||
| 511 | + | ||
| 461 | 512 | ### Test-suite setup measurements | |
| 462 | 513 | ||
| 463 | 514 | The fixture optimizations below are implemented as separate commits, each | |
@@ -619,7 +670,7 @@ identify the historical fixture where needed. | |||
| 619 | 670 | | GIX-11 | Unverified | Inexact and ambiguous rename pairing/scores have not been established as Git-compatible. This is a conservative guard, not a claimed native bug. | Verified pairing, scoring, tie-breaking and option parity. Exact unambiguous renames are native. | | |
| 620 | 671 | | GIX-12 | Missing API | Generic config bindings lack standalone parsing, ordered section/key enumeration, multivars, unset/remove-section and source-scoped queries. Dedicated `.gitmodules` parsing is available and now used for supported submodule reads. | Those operations for `GitConfigParser` and remote/branch configuration. Native merged getters cannot replace repository-only config readers. | | |
| 621 | 672 | | GIX-13 | Bug; missing API | Native index writing normalizes v4 to v2, offers no version setter, and expands split indexes. | Version and split-index preservation. Tests now assert that a split index remains split after an update. | | |
| 622 | - | GIX-14 | Bug; missing API | For a linked worktree of a bare main repository, `is_bare()` returns true while `git rev-parse --is-bare-repository` returns false; `workdir()` does expose the worktree. Initial gitfile opening can retain a noncanonical target and use an arbitrary gitfile's location as the worktree, unlike Git. Discovery accepts undecodable HEADs until explicit `head()` decoding and ignores dangling `commondir` symlinks that Git rejects. Bindings flatten failure kinds into `gix.Error`. | Provide Git-compatible classification, canonical gitfile locations and layout validation, plus typed errors. The adapter combines Gix metadata, reopens through Gix and forces HEAD decoding, retaining CLI for remaining validation/diagnostic gaps. Those workarounds do not close these compatibility bugs. A Git-strict mode must cover these cases; strict config and trust settings alone do not. | | ||
| 673 | + | GIX-14 | Bug; missing API | For a linked worktree of a bare main repository, `is_bare()` returns true while `git rev-parse --is-bare-repository` returns false; `workdir()` does expose the worktree. Initial gitfile opening can retain a noncanonical target and use an arbitrary gitfile's location as the worktree, unlike Git. Discovery accepts undecodable HEADs until explicit `head()` decoding and ignores dangling `commondir` symlinks that Git rejects. Bindings flatten failure kinds into `gix.Error`. Windows also exposes adapter path-formatting differences and a worker panic on an undecodable `commondir`; see the Windows reproduction below. | Provide Git-compatible classification, canonical gitfile locations and layout validation, plus typed errors instead of panics. Format native locations compatibly with Git's command output. The adapter combines Gix metadata, reopens through Gix and forces HEAD decoding, retaining CLI for remaining validation/diagnostic gaps. Those workarounds do not close these compatibility bugs. A Git-strict mode must cover these cases; strict config and trust settings alone do not. | | ||
| 623 | 674 | | GIX-15 | Bug | Native blame disagrees with Git even with Myers and rewrite tracking selected. In the fixture's `README.md`, lines 150 and 158 are attributed to the opposite commits. Incremental order also differs. | Attribution and incremental-output parity before replacing `Repo.blame` / `blame_incremental`. | | |
| 624 | 675 | | GIX-16 | Bug; missing API | Native archive streaming takes a tree rather than a commit. Its TAR omits Git's global PAX commit comment, leaves `export-subst` placeholders literal, and writes ordinary modes as `0644` where Git uses `0664`. | Commit-aware export substitution, metadata and permission parity. Both engines respected `export-ignore` in the probe. | | |
| 625 | 676 | | GIX-17 | Bug | Revision parsing accepts abbreviated IDs with `-dirty`, prefers the OID suffix over an exact describe-shaped tag, and treats escaped braces in message searches differently. | Git-compatible revision grammar and regex semantics. Existing `test_rev_parse.py` cases reproduce all three. | | |
@@ -632,6 +683,29 @@ identify the historical fixture where needed. | |||
| 632 | 683 | | GIX-24 | Bug | In both object formats, `new_commit_as()` accepts a blob as the tree or a parent where `git commit-tree` rejects it. Tree-editor `upsert()`/`write()` accepts object IDs whose actual kinds disagree with blob/tree/gitlink modes; `git mktree --missing` rejects all three tested mismatches. `edit_references_as()` accepts a blob target under `refs/heads/`, rejected by `git update-ref`. | Provide checked commit/tree construction and reference edits, at least in Git-strict mode. Validate kinds and branch targets, including symbolic aliases, before writing and under the required ref locks. Existing `_commit_tree`, `_write_tree` and `_update_ref` guards query Gix headers and select CLI on invalid inputs; they do not replace Gix writes with Python. | | |
| 633 | 684 | | GIX-25 | Bug; missing API | After `git symbolic-ref refs/heads/dangling refs/heads/missing`, `Repository.references().all()` enumerates the dangling name, while `git for-each-ref --format=%(refname)` omits it. Valid symbolic aliases must remain present. Both SHA-1 and SHA-256 probes reproduce this difference. | Expose Git-compatible enumeration with the same dangling-reference and diagnostic behavior, retaining the raw iterator for callers that need it. `_for_each_ref` forces Gix to resolve symbolic targets and falls back to Git on failure; keep that fallback until a compatible Gix API/mode is verified. | | |
| 634 | 685 | ||
| 686 | + Windows reproduction for GIX-14 uses CPython 3.12.13, Git | ||
| 687 | + 2.55.0.windows.3 and the released GixPython 0.1.0 source distribution with | ||
| 688 | + the same pinned Gitoxide revision above. Both `105114db` and the CLI metadata | ||
| 689 | + batching change reproduce the following existing failures: | ||
| 690 | + | ||
| 691 | + - `test_native_command_queries_match_cli` and | ||
| 692 | + `test_native_worktree_inventory_includes_bare_main` return backslashes in | ||
| 693 | + the adapter's native path text where Git returns forward slashes, in both | ||
| 694 | + object formats. The locations agree; the command-output formatting does not. | ||
| 695 | + - The `commondir=b"\xff"` subtest of | ||
| 696 | + `test_repo_discovery_rejects_invalid_metadata` raises | ||
| 697 | + `RuntimeError: native worker panicked` instead of the CLI backend's | ||
| 698 | + `InvalidGitRepositoryError`. The binding must reject this input through its | ||
| 699 | + normal native error contract so discovery can take the existing fallback. | ||
| 700 | + | ||
| 701 | + These remain open compatibility bugs. The optional Windows selection also | ||
| 702 | + has tests that fail before a native/Git comparison: newline-containing | ||
| 703 | + filenames, a raw backslash gitfile operand, and a POSIX-specific symlink | ||
| 704 | + canonicalization assertion. Its symbolic-alias blob assertion is not evidence | ||
| 705 | + of a Gix mismatch on this Git version: direct Git controls in both object | ||
| 706 | + formats also accept the alias update and leave the branch pointing to a blob. | ||
| 707 | + This does not invalidate GIX-24's separate direct-branch validation comparison. | ||
| 708 | + | ||
| 635 | 709 | For GIX-8, at fixture commit `44e0a8ec55c42559dfcdf5117710b26261a7c937`, | |
| 636 | 710 | compare `git rev-list HEAD` with | |
| 637 | 711 | `repo.rev_walk([repo.head_id()]).sorting("newest_first").all()`. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -369,7 +369,21 @@ def __init__( | |||
| 369 | 369 | self.git_dir = git_dir | |
| 370 | 370 | probe.update_environment(GIT_DIR=git_dir) | |
| 371 | 371 | try: | |
| 372 | - self.ref_format = probe._call_process_safe("rev_parse", "--show-ref-format") | ||
| 372 | + if native is not NotImplemented: | ||
| 373 | + self.ref_format = probe._call_process_safe("rev_parse", "--show-ref-format") | ||
| 374 | + else: | ||
| 375 | + # Query the fixed scalar fields together, leaving the path last so | ||
| 376 | + # embedded newlines in the common directory remain unambiguous. | ||
| 377 | + metadata = probe._call_process_safe( | ||
| 378 | + "rev_parse", | ||
| 379 | + "--show-ref-format", | ||
| 380 | + "--show-object-format", | ||
| 381 | + "--is-bare-repository", | ||
| 382 | + "--path-format=absolute", | ||
| 383 | + "--git-common-dir", | ||
| 384 | + ) | ||
| 385 | + self.ref_format, self.object_format, bare, self._common_dir = metadata.split("\n", 3) | ||
| 386 | + self._bare = bare == "true" | ||
| 373 | 387 | except GitCommandError as exc: | |
| 374 | 388 | raise InvalidGitRepositoryError(epath) from exc | |
| 375 | 389 | if native is not NotImplemented: | |
@@ -379,13 +393,6 @@ def __init__( | |||
| 379 | 393 | # Gix's configured bare flag also applies to linked worktrees. | |
| 380 | 394 | self._bare = native.is_bare() and self._working_tree_dir is None | |
| 381 | 395 | else: | |
| 382 | - try: | ||
| 383 | - self._common_dir = probe._call_process_safe("rev_parse", "--path-format=absolute", "--git-common-dir") | ||
| 384 | - self._bare = probe._call_process_safe("rev_parse", "--is-bare-repository") == "true" | ||
| 385 | - self.object_format = probe._call_process_safe("rev_parse", "--show-object-format") | ||
| 386 | - except GitCommandError as exc: | ||
| 387 | - raise InvalidGitRepositoryError(epath) from exc | ||
| 388 | - | ||
| 389 | 396 | self._working_tree_dir = environment.get("GIT_WORK_TREE") | |
| 390 | 397 | if self._working_tree_dir is None and not self._bare and environment.get("GIT_COMMON_DIR") is None: | |
| 391 | 398 | try: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1700,6 +1700,41 @@ def test_ignored_raises_error_w_symlink(self): | |||
| 1700 | 1700 | temp_repo.ignored(tmp_dir / "symlink/file.txt") | |
| 1701 | 1701 | ||
| 1702 | 1702 | ||
| 1703 | + @pytest.mark.parametrize("object_format", ["sha1", "sha256"]) | ||
| 1704 | + @pytest.mark.parametrize("ref_format", ["files", "reftable"]) | ||
| 1705 | + @pytest.mark.parametrize("bare", [False, True]) | ||
| 1706 | + @pytest.mark.parametrize( | ||
| 1707 | + "dirname", | ||
| 1708 | + [ | ||
| 1709 | + "repo with spaces", | ||
| 1710 | + pytest.param( | ||
| 1711 | + "repo\nwith\nnewlines", | ||
| 1712 | + marks=pytest.mark.skipif(sys.platform == "win32", reason="Windows paths cannot contain newlines"), | ||
| 1713 | + ), | ||
| 1714 | + ], | ||
| 1715 | + ) | ||
| 1716 | + def test_repo_batches_cli_metadata_queries(tmp_path, monkeypatch, object_format, ref_format, bare, dirname): | ||
| 1717 | + from git import _backend | ||
| 1718 | + | ||
| 1719 | + monkeypatch.setattr(_backend, "gix", None) | ||
| 1720 | + with Repo.init(tmp_path / dirname, object_format=object_format, ref_format=ref_format, bare=bare) as original: | ||
| 1721 | + with mock.patch.object(Git, "execute", autospec=True, side_effect=Git.execute) as execute: | ||
| 1722 | + with Repo(original.git_dir) as reopened: | ||
| 1723 | + assert reopened.ref_format == ref_format | ||
| 1724 | + assert reopened.object_format == object_format | ||
| 1725 | + assert reopened.bare == bare | ||
| 1726 | + assert reopened.common_dir == original.common_dir | ||
| 1727 | + assert reopened.git_dir == original.git_dir | ||
| 1728 | + assert reopened.working_tree_dir == original.working_tree_dir | ||
| 1729 | + | ||
| 1730 | + metadata_options = {"--show-ref-format", "--show-object-format", "--is-bare-repository", "--git-common-dir"} | ||
| 1731 | + metadata_commands = [ | ||
| 1732 | + call.args[1] for call in execute.call_args_list if metadata_options.intersection(call.args[1]) | ||
| 1733 | + ] | ||
| 1734 | + assert len(metadata_commands) == 1 | ||
| 1735 | + assert metadata_options.issubset(metadata_commands[0]) | ||
| 1736 | + | ||
| 1737 | + | ||
| 1703 | 1738 | @pytest.mark.parametrize("allow_unsafe_options", (False, True)) | |
| 1704 | 1739 | @pytest.mark.parametrize( | |
| 1705 | 1740 | "kwargs", | |
| Back | FazBrowse Home | New Git URL |
0 commit comments