Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
101 linesCodeBlameRaw

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.

Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971/**
2 * What the Files page's About, the Contributors, Releases and Activity
3 * pages say, worked out without the server: labels, the language bar,
4 * and an activity line for each event worth one.
5 */
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)6import type { G1tEvent, LanguageShare, License, Project, WeekCommits } from "@g1t/contracts";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb977
8/**
9 * The address the About links to: the project's homepage (its own, or its
10 * repository's website it follows), else production when it runs
11 * elsewhere. Null leaves the repository's website.
12 */
13export function projectHomepage(project: Pick<Project, "links" | "runs" | "productionUrl"> | null): string | null {
14 if (!project) return null;
15 return project.links?.homepage ?? (project.runs === "elsewhere" ? project.productionUrl : null) ?? null;
16}
17
18/** "MIT license", as the About names it; "View license" for one not recognized. */
19export function licenseLabel(license: License): string {
20 return license.spdxId ? `${license.spdxId} license` : "View license";
21}
22
23/** "12", "1.2k", "15k". */
24export function compact(n: number): string {
25 return n >= 10_000 ? `${Math.round(n / 1000)}k` : n >= 1000 ? `${(n / 1000).toFixed(1).replace(/\.0$/, "")}k` : String(n);
26}
27
28/** "1 star", "1.2k stars". */
29export function count(n: number, one: string, many = `${one}s`): string {
30 return `${compact(n)} ${n === 1 ? one : many}`;
31}
32
33/** The language bar's pieces: the largest, then the rest under 1% as Other. */
The repository sidebar reads like it should. TSX counts as TypeScript, as JSX already counted as JavaScript, so one language is not split in two (stats version 3, so every repository is read again); the languages list every one down to a tenth of a percent, the largest eight, rather than folding MDX, CSS and Shell into Other; up to five contributors are listed by name, each with their commits and card, more as avatars; and with no release published, Releases says how many tags there are, linked to them.34export function languageBar(languages: LanguageShare[], least = 0.1, most = 8): LanguageShare[] {
35 // Every language down to a tenth of a percent, the largest `most` of them; the rest as Other.
36 const sorted = [...languages].sort((a, b) => b.percent - a.percent);
37 const shown = sorted.filter((language, index) => language.percent >= least && index < most);
38 const rest = sorted.filter((language) => !shown.includes(language));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9739 if (rest.length === 0) return shown;
40 const bytes = rest.reduce((sum, language) => sum + language.bytes, 0);
41 const percent = Math.round(rest.reduce((sum, language) => sum + language.percent, 0) * 10) / 10;
42 if (percent === 0 && shown.length > 0) return shown;
43 return [...shown, { name: "Other", color: null, bytes, percent }];
44}
45
46
47/** The tallest week, for a chart's scale: never zero. */
48export function peak(weeks: WeekCommits[]): number {
49 return Math.max(1, ...weeks.map((week) => week.commits));
50}
51
52/** A contributor's weeks laid over the repository's, missing weeks as none. */
53export function alignWeeks(all: WeekCommits[], own: WeekCommits[]): number[] {
54 const byWeek = new Map(own.map((week) => [week.week, week.commits]));
55 return all.map((week) => byWeek.get(week.week) ?? 0);
56}
57
58/** An Activity line: what happened, and the branch, tag or pull request it was to. */
59export type ActivityLine =
60 | { kind: "push"; branch: string; commit: string; before: string | null; created: boolean; defaultBranch: boolean }
61 | { kind: "tag"; tag: string; commit: string }
62 | { kind: "merge"; number: number; commit: string }
63 | { kind: "renamed"; from: string; to: string }
64 | { kind: "default"; from: string; to: string };
65
66/** The events the Activity page lists. */
67export const ACTIVITY_TYPES = ["git.push", "pull.merged", "branch.renamed", "repo.default_branch_changed"] as const;
68
69/** One event as an Activity line, or null for one it does not list. */
70export function activityLine(event: G1tEvent): ActivityLine | null {
71 switch (event.type) {
72 case "git.push": {
73 const data = event.data as { ref: string; before?: string | null; after: string; defaultBranch: boolean };
74 if (data.ref.startsWith("refs/tags/")) return { kind: "tag", tag: data.ref.slice("refs/tags/".length), commit: data.after };
75 if (!data.ref.startsWith("refs/heads/")) return null;
76 return {
77 kind: "push",
78 branch: data.ref.slice("refs/heads/".length),
79 commit: data.after,
80 before: data.before ?? null,
81 created: !data.before,
82 defaultBranch: data.defaultBranch,
83 };
84 }
85 case "pull.merged": {
86 const data = event.data as { number: number; commit: string };
87 return { kind: "merge", number: data.number, commit: data.commit };
88 }
89 case "branch.renamed": {
90 const data = event.data as { from: string; to: string };
91 return { kind: "renamed", from: data.from, to: data.to };
92 }
93 case "repo.default_branch_changed": {
94 const data = event.data as { from: string; to: string; renamed: boolean };
95 // A rename of the default branch says so once, as a rename.
96 return data.renamed ? null : { kind: "default", from: data.from, to: data.to };
97 }
98 default:
99 return null;
100 }
101}

This file's history is long; its oldest lines are credited to the oldest commit read.