Fix commit-aware PR resolution across forges

This commit is contained in:
nllptrx
2026-07-18 14:21:10 +02:00
committed by GitHub
parent ffe76a7e57
commit c97f823236
16 changed files with 879 additions and 122 deletions
+56 -8
View File
@@ -7,7 +7,7 @@ import type { Logger } from "pino";
import type { ParsedDiffFile } from "../server/utils/diff-highlighter.js";
import { parseAndHighlightDiff } from "../server/utils/diff-highlighter.js";
import { parseGitHubRepoFromRemote } from "../server/workspace-git-metadata.js";
import { GitHubCommandError, createGitHubService } from "../services/github-service.js";
import { createGitHubService } from "../services/github-service.js";
import type {
CurrentPullRequestStatus,
ForgeAuthState,
@@ -15,7 +15,11 @@ import type {
ForgeSpecificStatusFacts,
PullRequestMergeable,
} from "../services/forge-service.js";
import { ForgeAuthenticationError, ForgeCliMissingError } from "../services/forge-cli-command.js";
import {
ForgeAuthenticationError,
ForgeCliMissingError,
ForgeCommandError,
} from "../services/forge-cli-command.js";
import { parseGitRevParsePath, resolveGitRevParsePath } from "./git-rev-parse-path.js";
import { runGitCommand } from "./run-git-command.js";
import { isPaseoOwnedWorktreeCwd, resolvePaseoWorktreesBaseRoot } from "./worktree.js";
@@ -67,6 +71,7 @@ interface CheckoutReadCacheOptions {
interface PullRequestStatusLookupTarget {
headRef: string;
headSha?: string;
headRepositoryOwner?: string;
}
@@ -116,8 +121,8 @@ function createShortstatCache(ttlMs: number) {
});
}
function getPullRequestStatusCacheKey(cwd: string): string {
return resolve(cwd);
function getPullRequestStatusCacheKey(cwd: string, headSha: string | null): string {
return `${resolve(cwd)}\u0000${headSha ?? ""}`;
}
function rememberPullRequestStatus(cacheKey: string, status: PullRequestStatusResult): void {
@@ -829,6 +834,38 @@ export async function getCurrentBranch(cwd: string): Promise<string | null> {
}
}
async function getCurrentHeadSha(cwd: string, context?: CheckoutContext): Promise<string | null> {
const knownSha = context?.facts?.isGit
? context.facts.pullRequestLookupTarget?.headSha
: undefined;
if (knownSha) {
return knownSha;
}
try {
const { stdout } = await runGitCommand(["rev-parse", "HEAD"], {
cwd,
envOverlay: READ_ONLY_GIT_ENV,
logger: context?.logger,
});
const sha = stdout.trim();
return sha.length > 0 ? sha : null;
} catch {
return null;
}
}
async function addHeadShaToPullRequestLookupTarget(
cwd: string,
target: PullRequestStatusLookupTarget | null,
context?: CheckoutContext,
): Promise<PullRequestStatusLookupTarget | null> {
if (!target) {
return null;
}
const headSha = await getCurrentHeadSha(cwd, context);
return headSha ? { ...target, headSha } : target;
}
async function getRebaseHeadBranch(cwd: string): Promise<string | null> {
const paths = ["rebase-merge/head-name", "rebase-apply/head-name"];
const results = await Promise.all(
@@ -1761,6 +1798,11 @@ export async function getCheckoutSnapshotFacts(
context,
)) ?? pullRequestLookupTarget;
}
pullRequestLookupTarget = await addHeadShaToPullRequestLookupTarget(
cwd,
pullRequestLookupTarget,
context,
);
return {
isGit: true,
@@ -3402,7 +3444,8 @@ export async function getPullRequestStatus(
options?: CheckoutReadCacheOptions,
context?: CheckoutContext,
): Promise<PullRequestStatusResult> {
const cacheKey = getPullRequestStatusCacheKey(cwd);
const headSha = await getCurrentHeadSha(cwd, context);
const cacheKey = getPullRequestStatusCacheKey(cwd, headSha);
if (!options?.force) {
const cached = pullRequestStatusCache.get(cacheKey);
if (cached) {
@@ -3415,14 +3458,14 @@ export async function getPullRequestStatus(
}
}
const lookup = getPullRequestStatusUncached(cwd, forgeService, options, context)
const lookup = getPullRequestStatusUncached(cwd, forgeService, options, context, headSha)
.then((status) => {
pullRequestStatusCache.set(cacheKey, status);
rememberPullRequestStatus(cacheKey, status);
return status;
})
.catch((error) => {
if (!options?.force && error instanceof GitHubCommandError) {
if (!options?.force && error instanceof ForgeCommandError) {
const stale = lastSuccessfulPullRequestStatus.get(cacheKey);
if (stale) {
return stale;
@@ -3443,6 +3486,7 @@ async function getPullRequestStatusUncached(
forgeService: ForgeService,
options?: CheckoutReadCacheOptions,
context?: CheckoutContext,
headSha?: string | null,
): Promise<PullRequestStatusResult> {
if (context?.facts?.isGit === false) {
return buildPullRequestStatusResult(null, "no_remote");
@@ -3455,7 +3499,11 @@ async function getPullRequestStatusUncached(
return buildPullRequestStatusResult(null, "no_remote");
}
try {
const lookupTarget = await resolvePullRequestStatusLookupTarget(cwd, head, context);
const resolvedLookupTarget = await resolvePullRequestStatusLookupTarget(cwd, head, context);
const lookupTarget =
headSha && !resolvedLookupTarget.headSha
? { ...resolvedLookupTarget, headSha }
: resolvedLookupTarget;
let status: CurrentPullRequestStatus | null;
if (options?.force) {
const reason = options.reason;