( client: GitClient, args: string[], input: GitCliInput, )
| 609 | // --------------------------------------------------------------- |
| 610 | |
| 611 | async function runCommit( |
| 612 | client: GitClient, |
| 613 | args: string[], |
| 614 | input: GitCliInput, |
| 615 | ): Promise<GitCliResult> { |
| 616 | // `git commit [-a] -m <msg> [--amend] [--author="Name <email>"]` |
| 617 | // |
| 618 | // Expand a combined short cluster like `-am` into `-a -m` |
| 619 | // first; the generic parser treats `-am` as one unknown short |
| 620 | // option. Only the `-a`/`-m` combination matters here. |
| 621 | const expanded = expandCommitShortCluster(args); |
| 622 | const parsed = parseFlags(expanded, { |
| 623 | message: { kind: "value", alias: ["m"] }, |
| 624 | amend: { kind: "bool" }, |
| 625 | author: { kind: "value" }, |
| 626 | all: { kind: "bool", alias: ["a"] }, |
| 627 | }); |
| 628 | if ("error" in parsed) { |
| 629 | return { stdout: "", stderr: `git commit: ${parsed.error}\n`, exitCode: 129 }; |
| 630 | } |
| 631 | if (parsed.positional.length > 0) { |
| 632 | return { |
| 633 | stdout: "", |
| 634 | stderr: `git commit: unexpected argument '${parsed.positional[0]}'\n`, |
| 635 | exitCode: 129, |
| 636 | }; |
| 637 | } |
| 638 | const message = parsed.flags.message as string | undefined; |
| 639 | if (!message) { |
| 640 | return { |
| 641 | stdout: "", |
| 642 | stderr: "git commit: -m <message> is required\n", |
| 643 | exitCode: 129, |
| 644 | }; |
| 645 | } |
| 646 | let author: { name: string; email: string } | undefined; |
| 647 | if (parsed.flags.author !== undefined) { |
| 648 | author = parseAuthorString(parsed.flags.author as string); |
| 649 | if (!author) { |
| 650 | return { |
| 651 | stdout: "", |
| 652 | stderr: `git commit: malformed --author '${parsed.flags.author}'. Expected 'Name <email>'.\n`, |
| 653 | exitCode: 129, |
| 654 | }; |
| 655 | } |
| 656 | } |
| 657 | const dir = resolveDir(undefined, input.cwd); |
| 658 | // Identity resolution happens inside commitWith via the typed |
| 659 | // surface; mirror the same env shape here. |
| 660 | try { |
| 661 | // `-a` stages tracked modifications and deletions (never |
| 662 | // untracked files) before the commit, matching `git commit |
| 663 | // -a`. A staging failure aborts before the commit runs. |
| 664 | if (parsed.flags.all === true) { |
| 665 | await client.add({ dir, paths: [], all: true, trackedOnly: true }); |
| 666 | } |
| 667 | const { oid } = await client.commit({ |
| 668 | dir, |
no test coverage detected