Vectrum
Sign In
39 lines · 2.0 KB · TypeScript
import "server-only";
import { commitExists, isAncestor, listBranches, revParse, type Branch } from "./git";
import { listPulls } from "./pulls";
import { listForks, type RepoRecord } from "./repos";

/** Branches pushed to this recently, without a pull request, get a suggestion to open one. */
const RECENT_MS = 2 * 86400e3;

export type RecentPush = { branch: Branch; fork: RepoRecord | null; compareHref: string };

/**
 * Branches the viewer pushed to lately that could become pull requests into the repository's
 * default branch: its own (for people with push access) and those of the viewer's forks of it.
 * Branches whose commits the default branch already has are left out.
 */
export async function recentPushes(repo: RepoRecord & { defaultBranch: string | null }, viewerId: string | null, canWrite: boolean, limit = 3) {
  if (!viewerId || !repo.defaultBranch) return [];
  const base = `/${repo.owner}/${repo.name}`;
  const baseSha = await revParse(repo.dir, `refs/heads/${repo.defaultBranch}`);
  if (!baseSha) return [];
  const open = (await listPulls(repo.id, { state: "open", perPage: 200 })).pulls;
  const since = Date.now() - RECENT_MS;
  const out: RecentPush[] = [];

  const sources = [...(canWrite ? [null] : []), ...(await listForks(repo.id, viewerId)).filter((f) => f.ownerId === viewerId)];
  for (const fork of sources) {
    const dir = fork?.dir ?? repo.dir;
    const taken = new Set(open.filter((p) => (fork ? p.headRepo?.id === fork.id : !p.headRepo)).map((p) => p.head));
    for (const b of await listBranches(dir)) {
      if (out.length >= limit) return out;
      if (b.date <= since || taken.has(b.name) || (!fork && b.name === repo.defaultBranch)) continue;
      // Already in the default branch: nothing to propose.
      if ((await commitExists(repo.dir, b.sha)) && (await isAncestor(repo.dir, b.sha, baseSha))) continue;
      const query = new URLSearchParams({ ...(fork ? { head_repo: `${fork.owner}/${fork.name}` } : {}), head: b.name });
      out.push({ branch: b, fork, compareHref: `${base}/compare?${query}` });
    }
  }
  return out;
}