Repository navigation
fix(refs): read references by exact name instead of revision guessing - #2313
Closed
Keerthana-64 wants to merge 1 commit into
Closed
Keerthana-64 wants to merge 1 commit into
Keerthana-64 wants to merge 1 commit into
Conversation
`SymbolicReference._get_ref_info_helper` resolved a reference that is not a symbolic ref with `git rev-parse --verify --end-of-options <path>`, and `Repo.currently_rebasing_on()` read `REBASE_HEAD` the same way. `rev-parse` applies the revision-guessing rules from `gitrevisions(7)`: when `<path>` itself does not exist it also tries `refs/<path>`, `refs/tags/<path>`, `refs/heads/<path>` and `refs/remotes/<path>`. A reference that does not exist was therefore reported as existing whenever a look-alike name did. `refs/heads/main` is a valid tag name, and any fetch or clone delivers it as `refs/tags/refs/heads/main`. In a repository whose `HEAD` points at the unborn `refs/heads/main`, Git reports an unborn branch (`git rev-parse --verify HEAD` exits 1 and `git show-ref --verify refs/heads/main` fails), while GitPython's CLI backend reported `head.is_valid()` as `True`, `head.commit` as the tag's commit, and `repo.index.commit()` created the first commit with that foreign commit as its parent and then wrote `refs/heads/main` on top of the tag's history. The same lookup through the Gix backend already answered "does not exist", so the two backends disagreed. A tag named `REBASE_HEAD` likewise made `currently_rebasing_on()` return a commit while no rebase was in progress. Read the exact name instead. `git show-ref --verify --hash -- <path>` reads only the named reference and fails for a missing one; it resolves `HEAD`, `ORIG_HEAD`, `FETCH_HEAD`, `MERGE_HEAD`, `refs/stash`, nested tags and a linked worktree's `HEAD` the same way `rev-parse --verify` did, on both files and reftable storage. `currently_rebasing_on()` first runs `git show-ref --exists -- REBASE_HEAD`, which exits 2 for a missing reference, so an absent rebase is still reported as `None` without matching diagnostic text. The `ValueError` raised for a missing reference is unchanged, and the Gix backend's native lookup is unaffected. The `lookalike` case in `tests/fixtures/reference-security.sh` tags the initial commit as `refs/heads/missing` and `REBASE_HEAD` and points `HEAD` at the unborn branch. `test_missing_references_do_not_resolve_to_look_alike_names` checks the lookup, `is_valid()`, tag access and `currently_rebasing_on()` through the native and the CLI reader, and `test_commit_on_unborn_head_ignores_look_alike_tag` checks that the first commit has no parent. Both fail before this change on each backend and pass after it. Validation: the full test suite with Git 2.52.0 on the CLI and Gix backends (a bootstrapped clone, `init-tests-after-clone.sh`), Ruff check and format, mypy, and ShellCheck on the fixture script.
1 task done
Byron
pushed a commit
that referenced
this pull request
Oct 27, 2026
Fetching a valid tag named `refs/heads/unborn` stores it under `refs/tags/refs/heads/unborn`. When `HEAD` names the absent `refs/heads/unborn`, the CLI reference reader used `git rev-parse --verify` and guessed that tag instead. `head.is_valid()` became true and `IndexFile.commit()` gave the local initial commit a foreign parent. A tag named `REBASE_HEAD` also made `Repo.currently_rebasing_on()` report a rebase. Read direct references with `git show-ref --verify --hash` instead of revision parsing. Probe `REBASE_HEAD` with `git show-ref --exists` to distinguish absence from lookup errors, then use `SymbolicReference.dereference_recursive()` and `Repo.commit()` so the rebase reader shares exact lookup and symbolic-reference validation while retaining recursive annotated-tag peeling. Revision APIs intentionally retain Git's name guessing. Existing native Gix reference reads are unchanged; root-reference existence and reftable retain documented CLI fallbacks. Independently written regressions fetch the misleading tags into an unborn repository and cover SHA-1/SHA-256, files/reftable, forced CLI fallback, parentless initial commits, and absent/present/deleted `REBASE_HEAD`. Positive cases compare existing root refs, packed annotated tags, and detached `HEAD` against Git. All 16 original regressions failed on the CLI baseline; 14 failed under Gix, whose files-based initial commits were already protected. All 26 final reference regression cases pass on each backend. Post-commit review found that `SymbolicReference.commit` peels only one annotated tag; four new regressions failed with `TypeError` before switching to the existing recursive commit resolver. Broader validation exposed the existing GIX-17 grammar bug for this work branch: Gix reads `2313-supersede` as blob `23136a1d1dd1f02de7cde0f7b55f49d76ac77d55`, while Git reads the branch's commit. Extend the existing conservative CLI grammar guard to hexadecimal prefixes followed by a dash. A deterministic fixture verifies existing `<blob-prefix>-topic` and missing `<blob-prefix>-missing` names; its test fails before the guard and passes afterward. The GIX-17 ledger retains the upstream bug as open and records the evidence and adapter fallback. An additional exact-name check exposed GIX-33: when the root `REBASE_HEAD` is absent and `refs/tags/REBASE_HEAD` is symbolic, Gix's partial lookup returns that tag and native `symbolic_ref` dispatch follows its target. Git's equivalent `symbolic-ref --quiet --no-recurse` exits 1. Compare the returned native reference name with the requested bytes and retain CLI for a lookalike. Two new SHA-1/SHA-256 regressions fail before the guard and pass afterward, including forced CLI fallback. The ledger keeps this compatibility bug open until Gix exposes a verified exact-name mode. Final caller validation passes 98 CLI reference tests and 200 Gix reference/backend tests, with 24 passing subtests each. The preceding intermediate commit `9ddf778` also passed the full local suites: 1,969 CLI tests and 2,072 Gix tests, with 85 passing subtests each. Earlier targeted revision/installation checks passed 48 CLI tests and 50 Gix tests. Ruff check/format, mypy, basedpyright, ShellCheck, and `git diff --check` pass. Behavioral comparisons use `git version 2.54.0 (Apple Git-157)` and `GixPython 0.3.0`. The Git reference checkout at `6de20f6092dcf9bdb1c8efe03db4b70c82b423dd` documents exact lookup in `Documentation/git-show-ref.adoc`, name guessing in `Documentation/revisions.adoc`, and parentless root commits in `Documentation/git-commit-tree.adoc`. Source report and investigation: Keerthana-64's PR #2313. The replacement does not incorporate that PR's implementation or tests.
Member
|
This PR is violating the contribution guidelines (impersonation) which require the agent to identify themselves, with the PR text clearly being LLM generated. This is why it will be regenerated and superseded. Put yourself into my shoes, I might interact with a human, but there is no use in responding to an agent. Not making clear who is behind the PR forces me to fallback to assume it's all made without human interaction, a bot on a cron job, who has been told to not follow self-disclosure or intentionally impersonate. This isn't acceptable. |
Byron
added a commit
that referenced
this pull request
Oct 27, 2026
<!-- agent --> Fetching a valid tag named `refs/heads/unborn` stores it under `refs/tags/refs/heads/unborn`. When `HEAD` names the absent `refs/heads/unborn`, the CLI reference reader used `git rev-parse --verify` and guessed that tag instead. `head.is_valid()` became true and `IndexFile.commit()` gave the local initial commit a foreign parent. A tag named `REBASE_HEAD` also made `Repo.currently_rebasing_on()` report a rebase. Read direct references with `git show-ref --verify --hash` instead of revision parsing. Probe `REBASE_HEAD` with `git show-ref --exists` to distinguish absence from lookup errors, then use `SymbolicReference.dereference_recursive()` and `Repo.commit()` so the rebase reader shares exact lookup and symbolic-reference validation while retaining recursive annotated-tag peeling. Revision APIs intentionally retain Git's name guessing. Existing native Gix reference reads are unchanged; root-reference existence and reftable retain documented CLI fallbacks. Independently written regressions fetch the misleading tags into an unborn repository and cover SHA-1/SHA-256, files/reftable, forced CLI fallback, parentless initial commits, and absent/present/deleted `REBASE_HEAD`. Positive cases compare existing root refs, packed annotated tags, and detached `HEAD` against Git. All 16 original regressions failed on the CLI baseline; 14 failed under Gix, whose files-based initial commits were already protected. All 26 final reference regression cases pass on each backend. Post-commit review found that `SymbolicReference.commit` peels only one annotated tag; four new regressions failed with `TypeError` before switching to the existing recursive commit resolver. Broader validation exposed the existing GIX-17 grammar bug for this work branch: Gix reads `2313-supersede` as blob `23136a1d1dd1f02de7cde0f7b55f49d76ac77d55`, while Git reads the branch's commit. Extend the existing conservative CLI grammar guard to hexadecimal prefixes followed by a dash. A deterministic fixture verifies existing `<blob-prefix>-topic` and missing `<blob-prefix>-missing` names; its test fails before the guard and passes afterward. The GIX-17 ledger retains the upstream bug as open and records the evidence and adapter fallback. An additional exact-name check exposed GIX-33: when the root `REBASE_HEAD` is absent and `refs/tags/REBASE_HEAD` is symbolic, Gix's partial lookup returns that tag and native `symbolic_ref` dispatch follows its target. Git's equivalent `symbolic-ref --quiet --no-recurse` exits 1. Compare the returned native reference name with the requested bytes and retain CLI for a lookalike. Two new SHA-1/SHA-256 regressions fail before the guard and pass afterward, including forced CLI fallback. The ledger keeps this compatibility bug open until Gix exposes a verified exact-name mode. Final caller validation passes 98 CLI reference tests and 200 Gix reference/backend tests, with 24 passing subtests each. The preceding intermediate commit `9ddf778` also passed the full local suites: 1,969 CLI tests and 2,072 Gix tests, with 85 passing subtests each. Earlier targeted revision/installation checks passed 48 CLI tests and 50 Gix tests. Ruff check/format, mypy, basedpyright, ShellCheck, and `git diff --check` pass. Behavioral comparisons use `git version 2.54.0 (Apple Git-157)` and `GixPython 0.3.0`. The Git reference checkout at `6de20f6092dcf9bdb1c8efe03db4b70c82b423dd` documents exact lookup in `Documentation/git-show-ref.adoc`, name guessing in `Documentation/revisions.adoc`, and parentless root commits in `Documentation/git-commit-tree.adoc`. Source report and investigation: Keerthana-64's PR #2313. The replacement does not incorporate that PR's implementation or tests. Assisted-by: GPT 6.1 Sol Co-authored-by: GPT 6.1 Sol <codex@openai.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Repro: fetch a tag named
refs/heads/main(a valid ref name any remote can advertise, stored asrefs/tags/refs/heads/main) into a repository whoseHEADpoints at the unbornrefs/heads/main. On the Git CLI backendrepo.head.is_valid()isTrue,repo.head.commitis the tag's commit, andrepo.index.commit("initial")records that commit as the parent and writesrefs/heads/mainon top of it, whilegit rev-parse --verify HEADexits 1. A tag namedREBASE_HEADmakesrepo.currently_rebasing_on()return a commit with no rebase in progress.Cause:
SymbolicReference._get_ref_info_helperandRepo.currently_rebasing_on()resolve a non-symbolic name withgit rev-parse --verify, which applies thegitrevisionsguessing rules (refs/<name>,refs/tags/<name>,refs/heads/<name>, ...) when the exact name is missing. The Gix backend reads the exact name and already reports the reference as missing, so the two backends disagreed.Fix: read with
git show-ref --verify --hash -- <path>, and probeREBASE_HEADwithgit show-ref --existsfirst, so only the named reference counts. The missing-referenceValueErroris unchanged. Checked against Git 2.52.0 forHEAD,ORIG_HEAD,FETCH_HEAD,MERGE_HEAD,refs/stash, nested tags, a linked worktree'sHEAD, and both files and reftable storage. The newlookalikefixture tests fail before and pass after on both backends; the full suite, Ruff, mypy and ShellCheck pass.