import { run } from "./db"; import { withStore } from "./git/store"; import { compareCommits } from "./git/history"; import { diffTreesCached } from "./git/diff"; import { badRequest, notFoundError } from "./errors"; import type { PullRow } from "./pulls"; import type { DiffResult, RepoRow } from "~/lib/types"; const MAX_PICKER_COMMITS = 100; export type DiffRange = { commit?: string; since?: string }; export type PullDiff = { diff: DiffResult; commits: Array<{ sha: string; shortSha: string; subject: string }>; range: { kind: "all" } | { kind: "commit"; sha: string } | { kind: "since"; sha: string }; }; /** * Diff for the Files tab: the whole pull request (merge base to head), a single commit, or the * changes since a reviewed commit ("changes since last review"). */ export async function pullDiff(pull: PullRow, repo: RepoRow, headRepo: RepoRow, range: DiffRange): Promise { return withStore(repo.artifacts_name, (baseStore) => withStore(headRepo.artifacts_name, async (headStore) => { const comparison = await compareCommits( baseStore, pull.merged_at && pull.merge_base_sha ? pull.merge_base_sha : pull.base_sha, headStore, pull.head_sha, ); const commits = comparison.commits .slice(0, MAX_PICKER_COMMITS) .map((commit) => ({ sha: commit.sha, shortSha: commit.shortSha, subject: commit.subject })); const head = await headStore.commit(pull.head_sha); if (!head) throw notFoundError("Head commit missing"); if (range.commit) { const commit = comparison.commits.find((candidate) => candidate.sha === range.commit); if (!commit) throw badRequest("That commit is not part of this pull request"); const parent = commit.parents[0] ? await headStore.commit(commit.parents[0]) : null; return { diff: await diffTreesCached(headStore, parent?.treeSha ?? null, commit.treeSha), commits, range: { kind: "commit", sha: commit.sha }, }; } if (range.since && range.since !== pull.head_sha) { const since = await headStore.commit(range.since); if (!since) throw badRequest("The reviewed commit is no longer available"); return { diff: await diffTreesCached(headStore, since.treeSha, head.treeSha), commits, range: { kind: "since", sha: since.sha }, }; } const mergeBaseSha = comparison.mergeBase ?? pull.merge_base_sha; if (mergeBaseSha && mergeBaseSha !== pull.merge_base_sha && !pull.merged_at) { await run("UPDATE pull_requests SET merge_base_sha = ? WHERE issue_id = ?", mergeBaseSha, pull.issue_id); } const mergeBase = mergeBaseSha ? await headStore.commit(mergeBaseSha) : null; return { diff: await diffTreesCached(headStore, mergeBase?.treeSha ?? null, head.treeSha), commits, range: { kind: "all" }, }; }), ); }