fix(github): keep merged PRs as branch history instead of hiding them
Branch status resolves an open PR across the whole fork network first, so a merged fork PR can never hide an open upstream PR for the same head. Only when no target has an open PR does the branch's newest closed/merged PR come back, as history. The panel shows that history as a compact note and offers creating the next PR below it, instead of either sticking on a terminal PR or going blank after a merge. Terminal associations stay persisted for reload continuity but are never treated as authority: they revalidate on the discovery cadence and on focus. History is looked up only for the branch's own remote and name, and remembered per repo+branch, so the extra lookup cannot exhaust the route's resolve budget. The checks summary and merge-permission lookup are skipped for a closed or merged PR, where neither is actionable.
This commit is contained in:
@@ -574,10 +574,17 @@ export function registerGitHubRoutes(app) {
|
||||
return res.json({ connected: true, repo: searchRepo, branch, pr: null, checks: null, canMerge: false });
|
||||
}
|
||||
|
||||
const isMerged = Boolean(prData.merged || prData.merged_at);
|
||||
const prState = isMerged ? 'merged' : (prData.state === 'closed' ? 'closed' : 'open');
|
||||
// A closed/merged PR is a historical record for this branch: its checks
|
||||
// are no longer actionable and it can never be merged from here, so skip
|
||||
// the extra GitHub calls those two fields would cost.
|
||||
const isHistorical = prState !== 'open';
|
||||
|
||||
// Checks summary: prefer check-runs (Actions), fallback to classic statuses.
|
||||
let checks = null;
|
||||
const sha = prData.head?.sha;
|
||||
if (sha) {
|
||||
if (sha && !isHistorical) {
|
||||
try {
|
||||
const runs = await octokit.rest.checks.listForRef({
|
||||
owner: searchRepo.owner,
|
||||
@@ -610,38 +617,37 @@ export function registerGitHubRoutes(app) {
|
||||
|
||||
// Permission check (best-effort)
|
||||
let canMerge = false;
|
||||
try {
|
||||
const auth = getGitHubAuth();
|
||||
// gh-CLI tokens have no persisted user record; resolve the login from
|
||||
// the API once (memoized) so permissions still resolve for them.
|
||||
let username = auth?.user?.login;
|
||||
if (!username) {
|
||||
if (!resolvedAuthLoginPromise) {
|
||||
resolvedAuthLoginPromise = octokit.rest.users.getAuthenticated()
|
||||
.then((resp) => resp?.data?.login || null)
|
||||
.catch(() => {
|
||||
resolvedAuthLoginPromise = null;
|
||||
return null;
|
||||
});
|
||||
if (!isHistorical) {
|
||||
try {
|
||||
const auth = getGitHubAuth();
|
||||
// gh-CLI tokens have no persisted user record; resolve the login from
|
||||
// the API once (memoized) so permissions still resolve for them.
|
||||
let username = auth?.user?.login;
|
||||
if (!username) {
|
||||
if (!resolvedAuthLoginPromise) {
|
||||
resolvedAuthLoginPromise = octokit.rest.users.getAuthenticated()
|
||||
.then((resp) => resp?.data?.login || null)
|
||||
.catch(() => {
|
||||
resolvedAuthLoginPromise = null;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
username = await resolvedAuthLoginPromise;
|
||||
}
|
||||
username = await resolvedAuthLoginPromise;
|
||||
if (username) {
|
||||
const perm = await octokit.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: searchRepo.owner,
|
||||
repo: searchRepo.repo,
|
||||
username,
|
||||
});
|
||||
const level = perm?.data?.permission;
|
||||
canMerge = level === 'admin' || level === 'maintain' || level === 'write';
|
||||
}
|
||||
} catch {
|
||||
canMerge = false;
|
||||
}
|
||||
if (username) {
|
||||
const perm = await octokit.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: searchRepo.owner,
|
||||
repo: searchRepo.repo,
|
||||
username,
|
||||
});
|
||||
const level = perm?.data?.permission;
|
||||
canMerge = level === 'admin' || level === 'maintain' || level === 'write';
|
||||
}
|
||||
} catch {
|
||||
canMerge = false;
|
||||
}
|
||||
|
||||
const isMerged = Boolean(prData.merged || prData.merged_at);
|
||||
const mergedState = isMerged ? 'merged' : (prData.state === 'closed' ? 'closed' : 'open');
|
||||
|
||||
return res.json({
|
||||
connected: true,
|
||||
repo: searchRepo,
|
||||
@@ -651,7 +657,7 @@ export function registerGitHubRoutes(app) {
|
||||
title: prData.title,
|
||||
body: prData.body || '',
|
||||
url: prData.html_url,
|
||||
state: mergedState,
|
||||
state: prState,
|
||||
draft: Boolean(prData.draft),
|
||||
base: prData.base?.ref,
|
||||
head: prData.head?.ref,
|
||||
|
||||
Reference in New Issue
Block a user