change!: accept :3:<path> for the 'theirs' index stage, like Git does - #2892
Merged
Sebastian Thiel (Byron) merged 3 commits intoAug 6, 2026
Merged
Conversation
The parser had arms for stages 0, 1 and 2 and let everything else fall through to the catch-all, so `:3:file` was looked up as a path literally named `3:file` at stage 0. `gitrevisions(7)` documents a stage number of 0 to 3, and Git resolves `:3:file` to the blob from the branch being merged. The trait's own docs said stages range from 0 to 2 and labelled them base, ours and theirs. That is off by one: 0 is unconflicted, and 1, 2 and 3 are the common ancestor, the target branch and the branch being merged. The docs came first, in cee04e1, and the arms written twenty minutes later in ea22d3e matched them. This is marked breaking because a `Navigate` implementation written against the old documented range can now be handed a stage it does not expect. The one in `gix` already maps 3 to `Stage::Theirs`, but the one behind `gix revision explain` did not, which the next commit addresses. `:4:` and other numbers stay part of the path, which Git agrees with.
`gix revision explain` panicked on `:3:<path>` through its `unreachable!()` arm now that the parser can emit stage 3. Its stage labels carried the same off-by-one as the trait docs, printing "stage 1 (ours)" where `gitrevisions(7)` has stage 1 as the common ancestor.
Amey Pawar (ameyypawar)
marked this pull request as draft
August 6, 2026 17:12
Sebastian Thiel (Byron)
marked this pull request as ready for review
August 6, 2026 17:53
Sebastian Thiel (Byron)
enabled auto-merge
August 6, 2026 17:53
Member
|
Thanks a lot, great catch! It seems GH CI is currently down, so let's see when this merges. But in any case, there is a lot of work left to be done in |
Contributor
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5a4e97bf27
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
Sebastian Thiel (Byron)
approved these changes
Aug 6, 2026
Sebastian Thiel (Byron)
merged commit Aug 6, 2026
453c17c
into
GitoxideLabs:main
1 of 30 checks passed
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.
Created by Claude Code on behalf of Amey, who reviewed it before submitting. Everything below this line is the agent's writing, not his.
Summary
:3:<path>now resolves to the 'theirs' side of a conflict instead of being read as a path.Navigatedocs said stages run from 0 to 2 and named them base, ours and theirs, which is off by one against Git in both the range and the labels.gix revision explaingets the same correction, and no longer panics when handed stage 3.Commits
:3:<path>for the 'theirs' index stage, like Git does. — the parser arm and the trait docs.gix-revision. —gix revision explain, split out asDEVELOPMENT.mdasks.Git baseline
gitrevisions(7):The parser had arms for
:0:,:1:and:2:, so:3:filefell through to the catch-all and became a lookup for a path literally named3:fileat stage 0. Measured in a repository left in a conflicted merge, with git 2.52.0:git rev-parsegix revision resolve, before:1:conflict.txtdf967b96df967b96:2:conflict.txtb19a1e93b19a1e93:3:conflict.txt950b81b7Couldn't find index '3:conflict.txt' stage 0 (implicit):4:conflict.txt:4:,:5:,:01:and:10:stay part of the path on both sides, which the existinginvalid_index_stage_is_part_of_pathalready pins.The docs are the reason the arms stop at 2. They came first, in cee04e1, and the arms written twenty minutes later in ea22d3e matched them — both in #427.
Why this is marked breaking
A
Navigateimplementation written against the documented 0 to 2 range can now be handed a stage it does not expect. That is not hypothetical: the implementation behindgix revision explainhadso
gix revision explain :3:conflict.txtpanicked as soon as the parser could emit stage 3. Its labels also carried the same off-by-one, printingstage 1 (ours)where stage 1 is the common ancestor. Both are fixed in the second commit, which is split out asDEVELOPMENT.mdasks.The implementation in
gixneeded nothing — it already maps 3 toStage::Theirs, and its ownunreachable!()notes that the driver is what guarantees valid stages.Validation
A differential sweep of 141 revspecs, taking Git's answer via
git rev-parseon the same fixture rather than an expectation, goes from 132 to 133 agreeing;:3:conflict.txtis the row that disappears. Six set-valued specs such as^!and^@are excluded and listed by name, becausegix revision resolveprints the spec form back rather than expanding the range, which is a difference in the command rather than in parsing.Two harness faults were corrected before the numbers above were trusted: every fixture commit initially shared one timestamp, which made
:/!-firstdisagree until the commits were given distinct times, andgit rev-parseexits 0 while echoing an argument it cannot resolve, so acceptance had to mean "produced an object id" rather than a zero exit code.Removing only the new arm makes
various_valid_index_lookups_by_path_and_stagefail with[("3:dir/path", 0)]against[("dir/path", 3)].cargo test— gix-revision 112, gix-refspec 77, gix 417, gitoxide-core 4cargo fmt --check— gix-revision, gitoxide-corecargo clippy -p gix-revision -p gitoxide-core --all-targets— clean for the changed files; the warnings it prints are ingitoxide-core/src/repository/index/entries.rsand the threeunfulfilled_lint_expectationsingix-ref, all present onmainNot addressed here
The same sweep turned up two differences in the relative dates accepted after
@{, both of which belong togix-daterather than to this parser, and one of them touches a function with an open PR against it:@{1.hour.ago}is rejected while@{1 hour ago}is accepted.approxidate_str()skips every byte that is neither a digit nor a letter, so.,-,_and anything else all read the same as a space to Git.special[]table indate.choldsyesterday,noon,midnight,tea,PM,AM,neverandnowin 2.52.0, while this crate understandsnow,todayandyesterday. Measured against a reflog with ordinary dates,@{noon},@{midnight},@{tea},@{AM}and@{PM}resolve in Git and are rejected here.@{today}is not a gap of the same kind: 2.52.0 has notodayin that table, soapproxidate_str()leavestouchedunset and reports an error, whilemasterhas since added{ "today", date_today }— which is the behaviour this crate already has.Happy to follow up on those separately if they are worth having.