mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 08:03:14 +00:00
Show a merged branch PR when the worktree head is one of its own commits
A worktree can end up behind its own merged PR's final head — update-branch
merges or web-committed suggestions land on the remote branch and merge
without a local pull. The merged-implicit visibility rule required exact
head equality, so the Checks panel dropped to 'No pull request found —
Create a pull request…' and the sidebar merged badge vanished right after
a successful merge.
Keep the reused-branch guard, but before hiding a merged branch-matched PR
ask GitHub whether the worktree head is one of the PR's own commits
(commits/{sha}/pulls, bounded immutable-friendly cache, core rate-limit
guard, fail-safe to the old hide). Confirmed heads are stamped on PRInfo/
HostedReviewInfo as confirmedContainedHeadOid so the renderer agreement
points (hosted-review staleness, refresh fallback number, no-PR preserve,
sidebar merged badge) accept the same worktrees without refetch loops.
Old main commits carry no association with the PR, so reused branch names
stay hidden; commits never pushed to GitHub fail the probe and stay hidden.
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -62,6 +62,7 @@ import {
|
||||
type LocalGitExecOptions,
|
||||
type OwnerRepo
|
||||
} from './gh-utils'
|
||||
import { isCommitPartOfMergedPR } from './merged-pr-commit-membership'
|
||||
import { getSshGitProvider } from '../providers/ssh-git-dispatch'
|
||||
import {
|
||||
hasHostedReviewLocalGitOptions,
|
||||
@@ -2808,7 +2809,30 @@ export async function getPRForBranchOutcome(
|
||||
typeof options.currentHeadOid === 'string' && options.currentHeadOid.trim().length > 0
|
||||
? options.currentHeadOid.trim()
|
||||
: null
|
||||
const hideMergedImplicitPR = async (candidate: PullRequestLookupData | null) => {
|
||||
let confirmedContainedHeadOid: string | null = null
|
||||
const mergedPRContainsHead = async (
|
||||
candidate: PullRequestLookupData,
|
||||
candidateRepo: OwnerRepo | null,
|
||||
headOid: string | null
|
||||
): Promise<boolean> => {
|
||||
if (!candidateRepo || !headOid) {
|
||||
return false
|
||||
}
|
||||
const contained = await isCommitPartOfMergedPR({
|
||||
ownerRepo: candidateRepo,
|
||||
prNumber: candidate.number,
|
||||
commitOid: headOid,
|
||||
ghOptions
|
||||
})
|
||||
if (contained) {
|
||||
confirmedContainedHeadOid = headOid
|
||||
}
|
||||
return contained
|
||||
}
|
||||
const hideMergedImplicitPR = async (
|
||||
candidate: PullRequestLookupData | null,
|
||||
candidateRepo: OwnerRepo | null
|
||||
) => {
|
||||
if (!candidate || !isMergedImplicitPR(candidate, linkedPRNumber)) {
|
||||
return false
|
||||
}
|
||||
@@ -2819,7 +2843,18 @@ export async function getPRForBranchOutcome(
|
||||
explicitCurrentHeadOid !== null
|
||||
? explicitCurrentHeadOid
|
||||
: await getCurrentHeadOid(repoPath, connectionId, localGitOptions)
|
||||
return shouldHideMergedImplicitPR(candidate, linkedPRNumber, currentHeadOidForMergedImplicit)
|
||||
if (!shouldHideMergedImplicitPR(candidate, linkedPRNumber, currentHeadOidForMergedImplicit)) {
|
||||
return false
|
||||
}
|
||||
// Why: a worktree can sit behind its own PR's final head (update-branch
|
||||
// merges, web-committed suggestions). A head that is one of the PR's own
|
||||
// commits is the same line of work, not a reused branch name — keep the
|
||||
// merged PR visible instead of offering "create a pull request".
|
||||
return !(await mergedPRContainsHead(
|
||||
candidate,
|
||||
candidateRepo,
|
||||
currentHeadOidForMergedImplicit
|
||||
))
|
||||
}
|
||||
|
||||
if (typeof linkedPRNumber === 'number') {
|
||||
@@ -2886,7 +2921,7 @@ export async function getPRForBranchOutcome(
|
||||
}
|
||||
}
|
||||
let mergedBranchLookupNumber: number | null = null
|
||||
if (await hideMergedImplicitPR(data)) {
|
||||
if (await hideMergedImplicitPR(data, dataRepo)) {
|
||||
mergedBranchLookupNumber = data?.number ?? null
|
||||
data = null
|
||||
dataRepo = null
|
||||
@@ -2913,14 +2948,15 @@ export async function getPRForBranchOutcome(
|
||||
data.number === fallbackPRNumber
|
||||
const explicitHeadHidesMergedImplicitPR =
|
||||
explicitCurrentHeadOid !== null &&
|
||||
shouldHideMergedImplicitPR(data, linkedPRNumber, explicitCurrentHeadOid)
|
||||
shouldHideMergedImplicitPR(data, linkedPRNumber, explicitCurrentHeadOid) &&
|
||||
!(await mergedPRContainsHead(data, dataRepo, explicitCurrentHeadOid))
|
||||
const shouldPreserveMergedFallback =
|
||||
!explicitHeadHidesMergedImplicitPR &&
|
||||
(fallbackConfirmedMergedBranch || options.acceptMergedFallbackPR === true)
|
||||
// Why: a currently visible PR can be merged outside Orca; when the caller
|
||||
// marks the fallback as visible review state, keep its lifecycle fresh even
|
||||
// if GitHub no longer reports it by branch (for example deleted heads).
|
||||
if ((await hideMergedImplicitPR(data)) && !shouldPreserveMergedFallback) {
|
||||
if ((await hideMergedImplicitPR(data, dataRepo)) && !shouldPreserveMergedFallback) {
|
||||
return { kind: 'no-pr', fetchedAt: Date.now() }
|
||||
}
|
||||
|
||||
@@ -2962,6 +2998,7 @@ export async function getPRForBranchOutcome(
|
||||
: {}),
|
||||
...(data.mergeStateStatus !== undefined ? { mergeStateStatus: data.mergeStateStatus } : {}),
|
||||
headSha: data.headRefOid,
|
||||
...(confirmedContainedHeadOid ? { confirmedContainedHeadOid } : {}),
|
||||
...(data.baseRefName ? { baseRefName: data.baseRefName } : {}),
|
||||
prRepo: dataRepo ?? undefined,
|
||||
headRepo: dataHeadRepo ?? undefined,
|
||||
|
||||
Reference in New Issue
Block a user