import type { CommitSummary } from "~/lib/types";
export type PatchKind = "diff" | "patch";
/** RFC 2822 date as git writes it. Author time zones are not kept, so dates are given in UTC. */
function mailDate(timestamp: number): string {
return new Date(timestamp).toUTCString().replace("GMT", "+0000");
}
function withTrailingNewline(text: string): string {
return text === "" || text.endsWith("\n") ? text : `${text}\n`;
}
/** Splits a commit message into the subject line and the body after the first blank line, like git does. */
function splitMessage(message: string): { subject: string; body: string } {
const lines = message.replace(/\r\n/g, "\n").split("\n");
const blank = lines.findIndex((line) => line.trim() === "");
const subjectLines = blank === -1 ? lines : lines.slice(0, blank);
const body =
blank === -1
? ""
: lines
.slice(blank + 1)
.join("\n")
.trim();
return { subject: subjectLines.join(" ").trim(), body };
}
/** One `git format-patch` mbox message per commit, numbered `[PATCH i/n]` when there is more than one. */
export function formatPatchSeries(
entries: Array<{
commit: Pick<CommitSummary, "sha" | "message" | "authorName" | "authorEmail" | "authoredAt">;
patch: string;
}>,
): string {
const total = entries.length;
return entries
.map(({ commit, patch }, index) => {
const { subject, body } = splitMessage(commit.message);
const prefix = total > 1 ? `[PATCH ${index + 1}/${total}]` : "[PATCH]";