@@ -11,6 +11,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
1111import {
1212 classifyGitAuthFailure ,
1313 GitError ,
14+ getGitHistory ,
1415 getGitStatus ,
1516 runGit ,
1617 runGitFetch ,
@@ -71,7 +72,7 @@ describe("runGit", () => {
7172
7273 const status = await getGitStatus ( testDir ) ;
7374
74- expect ( status . deleted ) . toEqual ( [ { path : "docs/验收报告/phase 1/a b.txt" } ] ) ;
75+ expect ( status . deleted ) . toEqual ( [ { path : "docs/验收报告/phase 1/a b.txt" , status : "deleted" } ] ) ;
7576 await expect (
7677 execFileAsync ( "git" , [ "add" , "--" , status . deleted [ 0 ] ?. path ?? "" ] , { cwd : testDir } )
7778 ) . resolves . toBeDefined ( ) ;
@@ -638,8 +639,8 @@ describe("getGitStatus", () => {
638639 const status = await getGitStatus ( testDir ) ;
639640
640641 expect ( status . untracked ) . toEqual ( [
641- { path : "docs/help/README.md" } ,
642- { path : "docs/help/assets/logo.png" } ,
642+ { path : "docs/help/README.md" , status : "untracked" } ,
643+ { path : "docs/help/assets/logo.png" , status : "untracked" } ,
643644 ] ) ;
644645
645646 await rm ( testDir , { recursive : true , force : true } ) ;
@@ -681,6 +682,82 @@ describe("getGitStatus", () => {
681682 } ) ;
682683} ) ;
683684
685+ describe ( "getGitHistory" , ( ) => {
686+ it ( "returns recent commits in reverse chronological order with author and timestamp" , async ( ) => {
687+ const testDir = await mkdtemp ( join ( tmpdir ( ) , "git-history-" ) ) ;
688+ await execFileAsync ( "git" , [ "init" ] , { cwd : testDir } ) ;
689+ await execFileAsync ( "git" , [ "config" , "user.name" , "Test" ] , { cwd : testDir } ) ;
690+ await execFileAsync ( "git" , [ "config" , "user.email" , "test@example.com" ] , { cwd : testDir } ) ;
691+
692+ await writeFile ( join ( testDir , "file.txt" ) , "one\n" ) ;
693+ await execFileAsync ( "git" , [ "add" , "." ] , { cwd : testDir } ) ;
694+ await execFileAsync ( "git" , [ "commit" , "-m" , "first commit" ] , { cwd : testDir } ) ;
695+
696+ await writeFile ( join ( testDir , "file.txt" ) , "two\n" ) ;
697+ await execFileAsync ( "git" , [ "add" , "." ] , { cwd : testDir } ) ;
698+ await execFileAsync ( "git" , [ "commit" , "-m" , "second commit" ] , { cwd : testDir } ) ;
699+
700+ const history = await getGitHistory ( testDir , 5 ) ;
701+
702+ expect ( history ) . toHaveLength ( 2 ) ;
703+ expect ( history [ 0 ] ) . toEqual (
704+ expect . objectContaining ( {
705+ subject : "second commit" ,
706+ authorName : "Test" ,
707+ } )
708+ ) ;
709+ expect ( history [ 0 ] ?. sha ) . toHaveLength ( 40 ) ;
710+ expect ( history [ 0 ] ?. shortSha ) . toHaveLength ( 7 ) ;
711+ expect ( history [ 0 ] ?. authoredAt ) . toBeTypeOf ( "number" ) ;
712+ expect ( history [ 0 ] ! . authoredAt ) . toBeGreaterThanOrEqual ( history [ 1 ] ! . authoredAt ) ;
713+ expect ( history [ 1 ] ) . toEqual (
714+ expect . objectContaining ( {
715+ subject : "first commit" ,
716+ authorName : "Test" ,
717+ } )
718+ ) ;
719+
720+ await rm ( testDir , { recursive : true , force : true } ) ;
721+ } ) ;
722+
723+ it ( "returns an empty list for repositories without commits" , async ( ) => {
724+ const testDir = await mkdtemp ( join ( tmpdir ( ) , "git-history-empty-" ) ) ;
725+ await execFileAsync ( "git" , [ "init" ] , { cwd : testDir } ) ;
726+
727+ await expect ( getGitHistory ( testDir , 5 ) ) . resolves . toEqual ( [ ] ) ;
728+
729+ await rm ( testDir , { recursive : true , force : true } ) ;
730+ } ) ;
731+ } ) ;
732+
733+ describe ( "getGitCommitDiff" , ( ) => {
734+ it ( "returns the requested commit patch with metadata" , async ( ) => {
735+ const testDir = await mkdtemp ( join ( tmpdir ( ) , "git-show-" ) ) ;
736+ await execFileAsync ( "git" , [ "init" ] , { cwd : testDir } ) ;
737+ await execFileAsync ( "git" , [ "config" , "user.name" , "Test" ] , { cwd : testDir } ) ;
738+ await execFileAsync ( "git" , [ "config" , "user.email" , "test@example.com" ] , { cwd : testDir } ) ;
739+
740+ await writeFile ( join ( testDir , "file.txt" ) , "one\n" ) ;
741+ await execFileAsync ( "git" , [ "add" , "." ] , { cwd : testDir } ) ;
742+ await execFileAsync ( "git" , [ "commit" , "-m" , "first commit" ] , { cwd : testDir } ) ;
743+
744+ await writeFile ( join ( testDir , "file.txt" ) , "two\n" ) ;
745+ await execFileAsync ( "git" , [ "add" , "." ] , { cwd : testDir } ) ;
746+ await execFileAsync ( "git" , [ "commit" , "-m" , "second commit" ] , { cwd : testDir } ) ;
747+
748+ const { stdout } = await execFileAsync ( "git" , [ "rev-parse" , "HEAD" ] , { cwd : testDir } ) ;
749+ const { getGitCommitDiff } = await import ( "../../git/cli.js" ) ;
750+ const diff = await getGitCommitDiff ( testDir , stdout . trim ( ) ) ;
751+
752+ expect ( diff ) . toContain ( "second commit" ) ;
753+ expect ( diff ) . toContain ( "diff --git a/file.txt b/file.txt" ) ;
754+ expect ( diff ) . toContain ( "-one" ) ;
755+ expect ( diff ) . toContain ( "+two" ) ;
756+
757+ await rm ( testDir , { recursive : true , force : true } ) ;
758+ } ) ;
759+ } ) ;
760+
684761describe ( "runGitCheckout" , ( ) => {
685762 let testDir : string ;
686763 let remoteDir : string ;
0 commit comments