80 lines · 3.2 KB
import { first } from "../db";
import { notFoundError } from "../errors";
import { loadRepo, repoPermission } from "../repos";
import { diffTreesCached } from "./diff";
import { compareCommits } from "./history";
import { assertVisibleRevision } from "./reachable";
import { listRefs, resolveRevision } from "./refs";
import type { ResolvedRevision } from "./refs";
import { withStore } from "./store";
import type { Comparison } from "./history";
import type { DiffResult, RepoRow, Viewer } from "~/lib/types";

export type BranchComparison = {
  base: ResolvedRevision;
  head: ResolvedRevision;
  headRepo: RepoRow;
  comparison: Comparison;
  diff: DiffResult;
  baseBranches: string[];
  headBranches: string[];
};

/** Resolves an `owner:branch` head to a fork the viewer can read; bare names stay in the base repository. */
async function resolveHead(base: RepoRow, head: string, viewer: Viewer | null) {
  const separator = head.indexOf(":");
  if (separator === -1) return { repo: base, ref: head };
  const owner = head.slice(0, separator);
  const ref = head.slice(separator + 1);
  const forks = await first<{ name: string }>(
    `SELECT r.name FROM repos r JOIN accounts a ON a.id = r.owner_id WHERE a.name = ? AND (r.id = ? OR r.fork_of_id = ? OR r.id = ?)`,
    owner,
    base.id,
    base.id,
    base.fork_of_id ?? "",
  );
  if (!forks) throw notFoundError("No fork found for that owner");
  const repo = await loadRepo(owner, forks.name);
  if (!repo || (await repoPermission(repo, viewer)) === "none") throw notFoundError("Head repository not found");
  return { repo, ref };
}