Skip to content

Commit

Mission control: a busy week's earlier pushes count again

The week chart read a project's last 40 push events, which include every branch, so in a week of agent branches Monday's and Tuesday's pushes to main had already fallen out (the chart showed nothing on a day with 15 commits). It now pages back through two weeks of pushes, reads the default branch's history once, and places each push's commits at its time, instead of one history read of 60 commits per push, which also cut large pushes short.

syntaqxcommitted Parent7469247Browse files
3 files+68−170/3 viewed
+17−0
33
44 import {
55 type Merged,
6+ placePushes,
67 change,
78 checksFact,
89 confidenceAsk,
370371 assert.match(why, /self-hosted runner/);
371372 assert.doesNotMatch(why, /agent/);
372373 });
374+
375+test("each push to the default branch places the commits it brought, at its time, from one read of the history", () => {
376+ const c = (hash: string, parents = ["p"], author = "Chase Pierce") => ({ hash, parents, author: { name: author } });
377+ // Newest first: a Wednesday push of two, a Monday push of one, a merge, and g1t's own commit.
378+ const history = [c("w2"), c("w1"), c("m1"), c("merge", ["a", "b"]), c("bot", ["p"], "g1t"), c("old")];
379+ const pushes = [
380+ { time: "2026-10-07T12:00:00Z", data: { after: "w2", before: "m1" } },
381+ { time: "2026-10-05T12:00:00Z", data: { after: "m1", before: "old" } },
382+ { time: "2026-10-01T12:00:00Z", data: { after: "gone", before: "older" } },
383+ ];
384+ assert.deepEqual(placePushes(history, pushes), [
385+ { hash: "m1", at: "2026-10-05T12:00:00Z" },
386+ { hash: "w2", at: "2026-10-07T12:00:00Z" },
387+ { hash: "w1", at: "2026-10-07T12:00:00Z" },
388+ ]);
389+});
+23−0
448448 return brought.filter((commit) => commit.parents.length <= 1 && !isAgent(commit.author.name));
449449 }
450450
451+/**
452+ * Each commit of `history` (newest first) a push in `pushes` (newest
453+ * first) brought, at that push's time. A commit pushed twice (after a
454+ * force push, say) counts once, at its first landing; a push whose `after`
455+ * is no longer in the history (rewritten) brings nothing.
456+ */
457+export function placePushes<C extends { hash: string; parents: string[]; author: { name: string } }>(
458+ history: C[],
459+ pushes: { time: string; data: { after: string; before?: string } }[],
460+): { hash: string; at: string }[] {
461+ const index = new Map(history.map((commit, i) => [commit.hash, i]));
462+ const seen = new Map<string, string>();
463+ for (const push of [...pushes].reverse()) {
464+ const start = index.get(push.data.after);
465+ if (start === undefined) continue;
466+ const end = push.data.before ? index.get(push.data.before) : undefined;
467+ for (const commit of pushedCommits(history.slice(start, end ?? history.length), undefined)) {
468+ if (!seen.has(commit.hash)) seen.set(commit.hash, push.time);
469+ }
470+ }
471+ return [...seen].map(([hash, at]) => ({ hash, at }));
472+}
473+
451474 export type Merged = {
452475 repo: RepoPath;
453476 number: number;
+28−17
3939 import {
4040 type Fact,
4141 type Merged,
42− pushedCommits,
42+ placePushes,
4343 type NeedRow,
4444 type QuickAction,
4545 RUN_LABEL,
152152 return data<DelegateResult>({ error: null, notStarted: refused });
153153 }
154154
155−/** Pushes to the default branch read for the week, per project, and commits read back from each. */
156−const PUSHES_READ = 40;
157−const PUSH_DEPTH = 60;
155+/**
156+ * Push events read per project, a page at a time, back two weeks. Pushes
157+ * to every branch are in the log, so a fixed count (it was 40) lost a busy
158+ * week's earlier days to agents' branches.
159+ */
160+const PUSH_PAGE = 200;
161+const PUSH_PAGES = 5;
162+/** Commits of the default branch read once, to place each push's commits. */
163+const HISTORY_READ = 1000;
158164
159165 /**
160166 * The commits people pushed straight to a project's default branch in the
163169 */
164170 async function directCommits(repo: Repo, viewer: Viewer): Promise<{ hash: string; at: string }[]> {
165171 const since = Date.now() - 14 * TIME.DAY;
166− const pushes = (await eventLog.list({ repoId: repo.id, types: ["git.push"], limit: PUSHES_READ })).filter(
167− (event): event is G1tEvent<"git.push"> => event.type === "git.push" && event.data.defaultBranch && Date.parse(event.time) >= since,
168− );
172+ const pushes: G1tEvent<"git.push">[] = [];
173+ let before: string | undefined;
174+ for (let page = 0; page < PUSH_PAGES; page++) {
175+ const batch = await eventLog.list({ repoId: repo.id, types: ["git.push"], limit: PUSH_PAGE, ...(before ? { before } : {}) });
176+ for (const event of batch) {
177+ if (event.type === "git.push" && event.data.defaultBranch && Date.parse(event.time) >= since) pushes.push(event as G1tEvent<"git.push">);
178+ }
179+ const oldest = batch.at(-1);
180+ if (batch.length < PUSH_PAGE || !oldest || Date.parse(oldest.time) < since) break;
181+ before = oldest.id;
182+ }
183+ if (pushes.length === 0) return [];
184+ // One read of the branch from the newest push back; each push brought
185+ // what lies between its `after` and its `before` in that history.
169186 const path = { namespace: repo.namespace, name: repo.name };
170− const read = await Promise.all(
171− pushes.map(async (push) => {
172− const history = await reposApi.log(path, viewer, push.data.after, PUSH_DEPTH).catch(() => null);
173− return history?.ok ? pushedCommits(history.value, push.data.before).map((commit) => ({ hash: commit.hash, at: push.time })) : [];
174− }),
175− );
176− // A commit pushed twice (after a force push, say) counts once, at its first landing.
177− const seen = new Map<string, string>();
178− for (const commit of read.flat().reverse()) if (!seen.has(commit.hash)) seen.set(commit.hash, commit.at);
179− return [...seen].map(([hash, at]) => ({ hash, at }));
187+ const history = await reposApi.log(path, viewer, pushes[0].data.after, HISTORY_READ).catch(() => null);
188+ if (!history?.ok) return [];
189+ return placePushes(history.value, pushes);
180190 }
181191
192+
182193 export async function loader({ context, request }: Route.LoaderArgs) {
183194 const viewer = getViewer(context);
184195 if (!viewer) {