Repository navigation
fix(git): find the PR and the tracking remote when a tag or branch has the same short name - #774
Merged
Conversation
git's short ref names add a prefix when the short name is ambiguous.
With a tag named like the current branch, `git symbolic-ref --short
HEAD` prints "heads/<branch>". Git PR and Git CI Status pass that name
to the lookup pinned to origin (`gh pr view --repo <origin> -- <branch>`,
and the same for glab), which then asks for a PR on a branch called
"heads/<branch>" and finds none. In a fork, where gh's own resolution
picks the upstream repository, that pinned lookup is the one that finds
the PR, so the widgets showed "(no PR)" for a branch that has one.
The tracking-remote fallback of the Git Upstream widgets has the same
flaw: `git rev-parse --abbrev-ref --symbolic-full-name @{upstream}`
prints "remotes/origin/<branch>" when a local branch or tag is named
"origin/<branch>", which matches no remote, so the widgets showed
"no upstream".
Both now read the full ref and strip "refs/heads/" or "refs/remotes/",
as Git Branch already does for the branch name.
sirmalloc
pushed a commit
that referenced
this pull request
Nov 5, 2026
…791) With a tag named like the current branch, `git symbolic-ref --short HEAD` prints "heads/<branch>". The refresh child keys the PR cache by the full ref with "refs/heads/" stripped since #774, but the render's lookup through the cached runGit (#639) still used --short. The two then hash to different cache files: the child writes the PR to one, Git PR and Git CI Status read the other, and the widgets show "(no PR)" on every render while a refresh runs again each time its lock goes stale. The child also never releases the lock it was handed, since that lock belongs to the other cache file. The render's lookup now reads `symbolic-ref HEAD` and strips the prefix the same way, which is also the command Git Branch already runs, so the cached runGit answers both. Co-authored-by: effectustasi <216187407+effectustasi@users.noreply.github.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.
What
Git PR/MR and Git CI Status now find the PR for a branch that has a tag of the same name, for example a release branch
release-1.4next to the tagrelease-1.4. Before, when the lookup had to fall back to origin, as in a fork where gh resolves to the upstream repository, they showed(no PR)(and CI-) for a branch with an open PR. GitLab merge requests through glab had the same problem.The Git Upstream widgets (Owner, Repo, Owner/Repo) have the same flaw in their tracking-branch fallback, so it is fixed here too. In a clone with no
upstreamremote, they showedno upstreamwhen a local branch or tag was namedorigin/<branch>.Why
When a short ref name is ambiguous, git's short form adds a prefix. Git Branch hit this in #679. Two other places read the short form.
Git PR/MR and Git CI Status.
getCurrentBranchingit-review-cache.tsrangit symbolic-ref --short HEAD. The first lookup is the CLI's owngh pr view(orglab mr view). If that finds nothing, a second lookup is pinned to origin:gh pr view --repo <origin> -- <branch>(orglab mr view --repo <origin> -- <branch>), with the branch fromgetCurrentBranch. With a tag named like the branch, git printsheads/<branch>, so the pinned lookup asks for a PR on a branch calledheads/<branch>and finds none.In a scratch repo with git 2.50.1, on branch
featurewith a tagfeature:The Git PR/MR demo setup below, piped through
main's built CLI under Bun and Node, with a stand-in gh that logs its calls, gives this. Each call runs twice: with a CI widget on the line, gh is asked again without the CI fields after any failure, a retry this change doesn't touch.Git Upstream.
getTrackingRemoteNameingit-remote.tsrangit rev-parse --abbrev-ref --symbolic-full-name @{upstream}and matched the result against the remote names (origin/…). With a local branch or tag namedorigin/feature, git printsremotes/origin/feature, which matches no remote:The Git Upstream demo repository below has only an
originremote. Piped throughmain, it renders⎇ feature | me/projwithout the localorigin/featurebranch, and⎇ feature | no upstreamwith it. A tag namedorigin/featuregivesno upstreamtoo.No other call under
src/reads a short ref name.rev-parse --short HEADgives a commit hash.How
getCurrentBranchreads the full ref withgit symbolic-ref HEADand stripsrefs/heads/, the way Git Branch does since fix(git): detached HEAD, tag-shadowed branches, submodule worktrees and wide PR titles #679. The pinned gh and glab lookups now pass the plain branch name. The PR cache entry is keyed by that name too, so a(no PR)cached under the oldheads/name is not reused.getTrackingRemoteNamereadsgit rev-parse --symbolic-full-name @{upstream}. It returns no remote unless the ref starts withrefs/remotes/, and strips that prefix before matching remote names as before. A branch that tracks a local branch (refs/heads/…) has no tracking remote.git-review-cache.test.tsharness answerssymbolic-refas git does: the full ref, or with--shortthe name, which becomesheads/<name>once a tag of that name exists (addTag).git-remote.test.ts, the three tracking-remote tests now return full refs, as the new command does. A new test checks the command and thatrefs/remotes/origin/featureresolves toorigin.Demo
Git PR/MR on a tag-shadowed branch. A scratch fork with origin
me/projand upstreamacme/proj. The checked-out branchrelease-1.4has a tag of the same name. A stand-in gh comes first onPATH, so nothing reaches GitHub. Its own repo resolution picks upstream and finds no PR. Only--repo https://github.com/me/proj -- release-1.4answers, with PR#42. Render 1 starts the background refresh. The helper then waits for the refresh and lists the gh calls it made, and render 2 shows the cached result. The(x2)is the retry without the CI fields described above.Git Upstream with a ref named like the tracking branch. A clone with only origin (
https://github.com/me/proj.git). Branchfeaturetracksorigin/feature, and a local branch is also namedorigin/feature. The demo prints both forms of the tracking ref, then renders Git Branch and Git Upstream Owner/Repo.Powerline
Git PR/MR, before:
Git PR/MR, after:
Git Upstream, before:
Git Upstream, after:
Plain
Git PR/MR, before:
Git PR/MR, after:
Git Upstream, before:
Git Upstream, after:
Testing
mainas of [low] perf(claude-status): hold a lock while the status page fetch is in flight #635 and [medium] perf(git-review): key the PR cache through the cached runGit #639 (62a371b): merges cleanly,bun run lintis clean, andbun testgives 3706 pass, 0 fail. The built CLI under Bun 1.4.4 and Node 26.13.0 matchesmainin plain and Powerline mode with the baseline configs, and the TUI opens and exits cleanly.bun test: 3171 pass, 0 fail across 178 files (main: 3168 pass, 0 fail). There are 3 new tests.main's source, run one at a time, 5 tests fail: the 3 new ones, and 2 of the 3 existing tracking-remote tests, whose mocked git output is now a full ref. The third (returns null when the tracking remote cannot be resolved) passes either way. Running both files whole againstmain's source fails 15 tests: those 5 plus 10 later tests ingit-remote.test.ts, because each failing test leaves its queued mock outputs to the tests after it.bun run lint: clean.git-review-cache.test.tsandgit-remote.test.tsthrough vitest: 81 pass (main: 78 pass).runtime-check.shwith the Git PR/MR demo config and a payload in the demo repo, with the stand-in gh first onPATH:main: both show(no PR), because a render returns at once on a cache miss and starts the lookup in the background.main, as intended: they pick up that background lookup's result. On this branch they showPR #42 OPEN Backport the login fix ✓2, and onmainthey show(no PR).dist):mainshows(no PR)andno upstream.PR #42 OPEN Backport the login fix ✓2andme/proj.