71 lines · 2.9 KB
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<PullDiff> {
  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");