Skip to content
65 linesCodeBlameRaw
1/**
2 * Active branches as the overview and the Branches page show them, from
3 * the repos service's `branch_drift` answer. The counting itself is done
4 * there (services/repos/src/drift.rs), kept by the pair of head commits.
5 */
6import type { Branch, BranchDrifts, Commit, Pull } from "@g1t/contracts";
7
8import type { ActiveBranch } from "../components/branches";
9
10/**
11 * How far a branch has moved from the default branch: commits it has that
12 * the default branch does not (ahead), and commits the default branch has
13 * that it does not (behind), the way `git rev-list --left-right --count`
14 * says it.
15 */
16export type Drift = { ahead: number; behind: number };
17
18type Preview = { branch?: string | null; number?: number | null; url: string };
19
20/** A commit as a branch row shows it: its first line. */
21export const summary = (commit: Commit | null | undefined): ActiveBranch["commit"] =>
22 commit ? { hash: commit.hash, message: commit.message.split("\n")[0] ?? "", author: commit.author.name, at: commit.authoredAt } : null;
23
24/**
25 * The branches other than the default that are read (at most `read`, those
26 * with an open pull request first), how many there are, and the default
27 * branch's head commit.
28 */
29export function branchesToRead(
30 input: { defaultBranch: string; branches: Branch[]; pulls: Pick<Pull, "branch">[] },
31 read: number,
32): { reading: Branch[]; total: number; mainHead: string | null } {
33 const pullOn = new Set(input.pulls.flatMap((pull) => (pull.branch ? [pull.branch] : [])));
34 const others = input.branches.filter((branch) => branch.name !== input.defaultBranch);
35 const reading = [...others.filter((b) => pullOn.has(b.name)), ...others.filter((b) => !pullOn.has(b.name))].slice(0, read);
36 const mainHead = input.branches.find((branch) => branch.name === input.defaultBranch)?.hash || null;
37 return { reading, total: others.length, mainHead };
38}
39
40/**
41 * Each branch read, newest commit first, with what repos measured (null
42 * when that could not be had: no commits, no counts), its pull request and
43 * its preview.
44 */
45export function activeBranches(
46 reading: Branch[],
47 measured: BranchDrifts | null,
48 input: { pulls: Pick<Pull, "branch" | "number" | "title" | "checkStatus" | "status">[]; previews: Preview[] },
49): ActiveBranch[] {
50 const pullOn = new Map(input.pulls.flatMap((pull) => (pull.branch ? [[pull.branch, pull] as const] : [])));
51 const byHead = new Map((measured?.branches ?? []).map((found) => [found.head, found]));
52 return reading
53 .map((branch): ActiveBranch => {
54 const pull = pullOn.get(branch.name);
55 const found = byHead.get(branch.hash);
56 return {
57 name: branch.name,
58 commit: summary(found?.commit),
59 drift: found?.drift ?? null,
60 pull: pull ? { number: pull.number, title: pull.title, checkStatus: pull.checkStatus, draft: pull.status === "draft" } : null,
61 preview: input.previews.find((app) => app.branch === branch.name || (pull != null && app.number === pull.number))?.url ?? null,
62 };
63 })
64 .sort((a, b) => Date.parse(b.commit?.at ?? "0") - Date.parse(a.commit?.at ?? "0"));
65}