Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Branches and Tags pages, each file's last commit, and the branch menu on files | 1 | /** |
| 2 | * A repository's branches as its pages show them: each one's head commit, | |
| 3 | * how far it has moved from the default branch, the pull request open on | |
| 4 | * it with its checks, and its preview. The overview shows the newest few; | |
| 5 | * the Branches page shows them all. | |
| 6 | */ | |
| 7 | import type { Branch, Pull, RepoPath, Viewer } from "@g1t/contracts"; | |
| 8 | ||
| 9 | import type { ActiveBranch } from "../components/branches"; | |
| 10 | import { drift } from "./branches"; | |
| 11 | import { repos } from "./services.server"; | |
| 12 | ||
| 13 | /** How far back each branch's history, and the default branch's, is read to count ahead and behind. */ | |
| 14 | export const BRANCH_DEPTH = 40; | |
| 15 | export const MAIN_DEPTH = 120; | |
| 16 | ||
| 17 | type Preview = { branch?: string | null; number?: number | null; url: string }; | |
| 18 | ||
| 19 | /** | |
| 20 | * The branches other than the default, at most `read` of them read (those | |
| 21 | * with an open pull request first), newest commit first. `main` is the | |
| 22 | * default branch's head commit with its first line. | |
| 23 | */ | |
| 24 | export async function readBranches( | |
| 25 | path: RepoPath, | |
| 26 | viewer: Viewer, | |
| 27 | input: { defaultBranch: string; branches: Branch[]; pulls: Pull[]; previews: Preview[] }, | |
| 28 | read: number, | |
| 29 | ): Promise<{ main: string; total: number; shown: ActiveBranch[]; head: ActiveBranch["commit"] }> { | |
| 30 | const soft = <T,>(promise: Promise<T>): Promise<T | null> => promise.catch(() => null); | |
| 31 | const main = input.defaultBranch; | |
| 32 | const pullOn = new Map(input.pulls.filter((pull) => pull.branch).map((pull) => [pull.branch as string, pull])); | |
| 33 | const others = input.branches.filter((branch) => branch.name !== main); | |
| 34 | const reading = [...others.filter((b) => pullOn.has(b.name)), ...others.filter((b) => !pullOn.has(b.name))].slice(0, read); | |
| 35 | // By commit hash, not name: history from a commit never changes, so | |
| 36 | // repos keeps it (services/repos/src/store.rs) and only new heads cost a walk. | |
| 37 | const mainHead = input.branches.find((branch) => branch.name === main)?.hash ?? main; | |
| 38 | const [mainLog, ...logs] = await Promise.all([ | |
| 39 | soft(repos.log(path, viewer, mainHead, MAIN_DEPTH)), | |
| 40 | ...reading.map((branch) => soft(repos.log(path, viewer, branch.hash || branch.name, BRANCH_DEPTH))), | |
| 41 | ]); | |
| 42 | const mainHistory = mainLog?.ok ? mainLog.value : []; | |
| 43 | const mainHashes = mainHistory.map((c) => c.hash); | |
| 44 | const summary = (commit: (typeof mainHistory)[number] | undefined): ActiveBranch["commit"] => | |
| 45 | commit ? { hash: commit.hash, message: commit.message.split("\n")[0] ?? "", author: commit.author.name, at: commit.authoredAt } : null; | |
| 46 | const shown = reading | |
| 47 | .map((branch, index): ActiveBranch => { | |
| 48 | const history = logs[index]?.ok ? logs[index].value : []; | |
| 49 | const pull = pullOn.get(branch.name); | |
| 50 | return { | |
| 51 | name: branch.name, | |
| 52 | commit: summary(history[0]), | |
| 53 | ...drift(history.map((c) => c.hash), mainHashes, BRANCH_DEPTH), | |
| 54 | pull: pull ? { number: pull.number, title: pull.title, checkStatus: pull.checkStatus, draft: pull.status === "draft" } : null, | |
| 55 | preview: input.previews.find((app) => app.branch === branch.name || (pull != null && app.number === pull.number))?.url ?? null, | |
| 56 | }; | |
| 57 | }) | |
| 58 | .sort((a, b) => Date.parse(b.commit?.at ?? "0") - Date.parse(a.commit?.at ?? "0")); | |
| 59 | return { main, total: others.length, shown, head: summary(mainHistory[0]) }; | |
| 60 | } |