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; }