Skip to content

fix(server): stop attaching a base branch's PR to branches cut from it - #8210

Open
AyushKaithwas wants to merge 4 commits into
pingdotgg:mainfrom
AyushKaithwas:fix/status-pr-upstream-base
Open

fix(server): stop attaching a base branch's PR to branches cut from it#8210
AyushKaithwas wants to merge 4 commits into
pingdotgg:mainfrom
AyushKaithwas:fix/status-pr-upstream-base

Conversation

@AyushKaithwas

@AyushKaithwas AyushKaithwas commented Aug 25, 2026

Copy link
Copy Markdown

Fixes #8209.

A branch created with git checkout -b feature origin/dev tracks its base, and prLookupCache reads the upstream ref as the branch's published head. The guard against that only fired when the upstream was the repository default branch, so in a repo that integrates through a non-default branch every thread cut from dev showed dev's newest release PR, kept showing it after checkouts because that PR is merged, and settled on it.

The upstream of a branch whose name differs from it is the branch's base. resolveBaseBranch already reads the same config that way when opening a PR, so this makes the status lookup agree with the create path instead of contradicting it.

One same-repo case really does track a differently named head: a tracking checkout of a remote whose own name contains a slash, where git checkout --track my-org/upstream/effect-atom cannot name the local branch effect-atom and leaves upstream/effect-atom. Those keep their lookup by matching the local name against the tail of the upstream ref, and the existing test for them still passes. Cross-repo head contexts are untouched.

Also drops the default branch from the PR lookup cache key, since nothing in the lookup reads it now.

No UI change beyond the wrong badge no longer appearing.

Verification: vp test run apps/server/src/git/GitManager.test.ts (87 passed, including the new case, which fails on main), plus vp lint and tsgo --noEmit for the touched scope.

Written by Claude Opus 5 (1M context) in T3 Code.


Note

Medium Risk
Changes git status PR resolution for many tracking setups; legitimate alias branches depend on the new suffix rules, but behavior now aligns with PR creation base resolution.

Overview
Fixes incorrect PR badges on feature branches that track an integration or release branch (e.g. feature/from-dev on origin/dev) by tightening when GitManager runs GitHub PR lookup during status.

prLookupCache no longer keys on the default branch. It skips lookup unless the local branch is treated as the same published head as its upstream—either the names match, or the git tracking-alias case applies (local ends with /<head> and upstream ends with /<local>). Otherwise the upstream is treated as the branch’s base, so status.pr stays null and gh pr list is not called.

Three new tests cover non-default upstreams, hierarchical ref tails (v2 vs release/v2), and suffix collisions (feature/dev vs dev).

Reviewed by Cursor Bugbot for commit 3ea8882. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Stop inheriting PR metadata from base branches in GitManager.prLookupCache

  • Reworks the PR lookup skip logic in the prLookupCache loader so branches tracking a differently-named upstream ref (e.g. feature/from-dev tracking origin/dev) no longer inherit the upstream's PR.
  • Introduces localBranchIsAliasOfHead, which is true when the local branch name equals the resolved head branch, or when the local branch ends with /<headBranch> and the upstream ref ends with /<localBranch>. Only alias branches still perform PR lookup.
  • Removes defaultBranch from the prLookupCacheKey composition; cache entries are now keyed on [cwd, branch, upstreamRef, epoch] and shared across different default-branch values.
  • Adds three tests in GitManager.test.ts covering non-default upstream, hierarchical-base tail match, and base-as-tail scenarios.
  • Behavioral Change: branches that previously received a PR from their upstream (when the upstream head was the default branch) will now show status.pr as null; review the localBranchIsAliasOfHead condition in GitManager.ts if legitimate alias branches stop resolving PRs.

Macroscope summarized 3ea8882.

A branch created with `git checkout -b feature origin/dev` tracks its base,
and the status PR lookup reads the upstream ref as the branch's published
head. The guard against that only fired when the upstream was the repository
default branch, so a repo that integrates through a non-default branch showed
that branch's newest release PR on every thread cut from it.

Treat any upstream whose name differs from the local branch as the base, which
is how `resolveBaseBranch` already reads it when opening a PR. Branches that
track a remote ref under a git-mangled local name (a remote whose own name
contains a slash) keep their lookup.
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 92857433-44c5-4aed-80c7-fc48a3ce98e5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 25, 2026
Comment thread apps/server/src/git/GitManager.ts Outdated
// cannot name it "effect-atom", so it keeps "upstream/effect-atom". Its
// upstream is still its published head.
const localBranchTracksUpstreamHead =
details.upstreamRef !== null && details.upstreamRef.endsWith(`/${details.branch}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium git/GitManager.ts:953

Status suppresses a real PR badge after git branch -m old-name new-name because the renamed branch still tracks origin/old-name, causing resolveBranchHeadContext to return { latest: null }. Conversely, endsWith('/feature') misclassifies origin/team/feature as the published head for local feature, so status can display an unrelated PR from the base branch. Replace this heuristic with logic that distinguishes renamed branches from base-tracking branches and compares the remote name separately from the slash-containing remote branch name.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/git/GitManager.ts around line 953:

Status suppresses a real PR badge after `git branch -m old-name new-name` because the renamed branch still tracks `origin/old-name`, causing `resolveBranchHeadContext` to return `{ latest: null }`. Conversely, `endsWith('/feature')` misclassifies `origin/team/feature` as the published head for local `feature`, so status can display an unrelated PR from the base branch. Replace this heuristic with logic that distinguishes renamed branches from base-tracking branches and compares the remote name separately from the slash-containing remote branch name.

Evidence trail:
Commit 16d4313: apps/server/src/git/GitManager.ts:939-973, 1187-1258, 1311-1379; apps/server/src/vcs/GitVcsDriverCore.ts:992-1011, 1485-1531; apps/server/src/git/remoteRefs.ts:44-72. Tests: apps/server/src/git/GitManager.test.ts:1273-1387, 1460-1536. Git documentation: https://git-scm.com/docs/git-branch. Commands: git diff MERGE_BASE REVIEWED_COMMIT -- apps/server/src/git/GitManager.ts; git show 16d4313.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second half (origin/team/feature misclassified as the published head of local feature) was real and is fixed in 1d1ca06: the suffix comparison now runs against the parsed headContext.headBranch in the alias direction only, with a regression test.

The rename half is inherent ambiguity, not something this heuristic can resolve: after git branch -m old new, branch.new.merge is refs/heads/old, byte-identical to a checkout cut from old. No config or commit-graph signal distinguishes them (a fresh cut equals its base exactly; a renamed branch with local work is ahead of its upstream, and so is a stale base). Given the tie, hiding the badge until the next push is the safer failure mode: attaching the wrong PR auto-settles the thread and survives checkout switches because merged PRs are retained, which is the bug this PR fixes. The badge comes back on git push, since push updates old on the remote and that head is the PR's. On main today the rename case only worked when the upstream was non-default; it was already suppressed for default-branch upstreams by the previous guard.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

Comment thread apps/server/src/git/GitManager.ts
@macroscopeapp

macroscopeapp Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a narrowly scoped fix to prevent inherited pull-request metadata, with targeted regression coverage. The updated branch heuristic still has an unresolved tradeoff where renamed branches may lose a legitimate PR badge, so the behavior warrants human review.

Not approved because:

  • 1 blocking correctness issue found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

`upstreamRef.endsWith("/" + branch)` also matched a branch cut from a
hierarchical base whose tail equals the branch name (local v2 tracking
origin/release/v2), so those kept inheriting the base's PR. The alias
suffix runs the other way: the git-mangled local name ends with the
parsed head branch. Compare against headContext.headBranch directly.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1d1ca06. Configure here.

Comment thread apps/server/src/git/GitManager.ts Outdated
AyushKaithwas and others added 2 commits August 26, 2026 03:12
… ref

A branch ending in its head's name was still counted as an alias, so
feature/dev cut from origin/dev kept inheriting dev's PR. A git-mangled
alias satisfies both suffix directions: the local name ends with the
parsed head and the upstream ref ends with the local name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Thread shows the base branch's PR when the branch tracks a non-default base

1 participant