Skip to content
File

Blob: src/worker/git/core/commitParse.ts

typescript66 lines
1/**
2 * Author/committer signature extracted from a Git commit object.
3 *
4 * Format reference
5 * - `Name <email> <unix-seconds> <tz>`
6 * Example: `John Doe <john@example.com> 1694025600 +0000`
7 */
8export interface Signature {
9 name: string;
10 email: string;
11 when: number;
12 tz: string;
13}
14 
15export interface CommitParsed {
16 tree: string;
17 parents: string[];
18 author?: Signature;
19 committer?: Signature;
20 message: string;
21}
22 
23/**
24 * Parse a Git signature line (author or committer).
25 *
26 * @param sig - A single line like `Name <email> 1694025600 +0000`
27 * @returns A normalized `Signature` or `undefined` if the format does not match
28 */
29export function parseSignature(sig: string): Signature | undefined {
30 // Format: Name <email> 1694025600 +0000
31 const m = sig.match(/^(.*) <([^>]+)>\s+(\d+)\s+([+-]\d{4})$/);
32 if (!m) return undefined;
33 const name = m[1];
34 const email = m[2];
35 const when = parseInt(m[3], 10);
36 const tz = m[4];
37 return { name, email, when, tz };
38}
39 
40/**
41 * Parse a commit payload string (without the Git object header).
42 *
43 * Expected input
44 * - The raw text after inflating a `commit` Git object and removing the
45 * `<type> <len>\0` header. Contains RFC-2822-ish lines and a blank line before
46 * the message body.
47 *
48 * Extracted fields
49 * - `tree`: the root tree OID the commit points to
50 * - `parents`: zero or more parent OIDs (first entry is the first-parent)
51 * - `author`, `committer`: parsed signatures if present
52 * - `message`: text after the first blank line (may be empty)
53 */
54export function parseCommitText(text: string): CommitParsed {
55 const mTree = text.match(/^tree ([0-9a-f]{40})/m);
56 const tree = mTree ? mTree[1] : "";
57 const parents = [...text.matchAll(/^parent ([0-9a-f]{40})/gm)].map((x) => x[1]);
58 const authorLine = (text.match(/^author (.+)$/m) || [])[1];
59 const committerLine = (text.match(/^committer (.+)$/m) || [])[1];
60 const author = authorLine ? parseSignature(authorLine) : undefined;
61 const committer = committerLine ? parseSignature(committerLine) : undefined;
62 const msgIdx = text.indexOf("\n\n");
63 const message = msgIdx >= 0 ? text.slice(msgIdx + 2) : "";
64 return { tree, parents, author, committer, message };
65}