@@ -39,7 +39,7 @@ vi.mock('fs/promises', () => ({
3939} ) ) ;
4040
4141vi . mock ( 'istextorbinary' , ( ) => ( {
42- isBinary : ( ) => false ,
42+ isBinary : vi . fn ( ( ) => false ) ,
4343} ) ) ;
4444
4545const fs = await import ( 'fs/promises' ) ;
@@ -168,3 +168,89 @@ describe('WorktreeManager - getUpdatedFiles symlink filtering', () => {
168168 expect ( paths ) . not . toContain ( 'resources/linux' ) ;
169169 } ) ;
170170} ) ;
171+
172+ describe ( 'WorktreeManager - getUpdatedFiles no HEAD (no commits)' , ( ) => {
173+ let worktreeManager : WorktreeManager ;
174+ const testPath = '/test/worktree' ;
175+
176+ beforeEach ( ( ) => {
177+ vi . clearAllMocks ( ) ;
178+ worktreeManager = new WorktreeManager ( ) ;
179+ } ) ;
180+
181+ it ( 'should return staged files when there are no commits (non-worktree mode)' , async ( ) => {
182+ const diffContent =
183+ 'diff --git a/src/main.ts b/src/main.ts\nnew file mode 100644\n--- /dev/null\n+++ b/src/main.ts\n@@ -0,0 +1,3 @@\n+line1\n+line2\n+line3' ;
184+
185+ useMockSeq ( createMockSeq ( [ new Error ( 'HEAD not found' ) , { stdout : '3\t0\tsrc/main.ts\0' , stderr : '' } , { stdout : diffContent , stderr : '' } ] ) ) ;
186+ ( lstatSync as Mock ) . mockReturnValue ( { isSymbolicLink : ( ) => false } ) ;
187+ ( fs . default . access as Mock ) . mockResolvedValue ( undefined ) ;
188+ ( fs . default . readFile as Mock ) . mockResolvedValue ( Buffer . from ( 'hello' ) ) ;
189+
190+ const result = await worktreeManager . getUpdatedFiles ( testPath ) ;
191+
192+ expect ( result ) . toHaveLength ( 1 ) ;
193+ expect ( result [ 0 ] . path ) . toBe ( 'src/main.ts' ) ;
194+ expect ( result [ 0 ] . additions ) . toBe ( 3 ) ;
195+ expect ( result [ 0 ] . deletions ) . toBe ( 0 ) ;
196+ expect ( result [ 0 ] . diff ) . toBe ( diffContent ) ;
197+ } ) ;
198+
199+ it ( 'should filter symlink paths in no-HEAD mode' , async ( ) => {
200+ useMockSeq (
201+ createMockSeq ( [
202+ new Error ( 'HEAD not found' ) ,
203+ { stdout : '3\t0\tsrc/main.ts\0-\t-\tresources/linux\0' , stderr : '' } ,
204+ { stdout : 'diff content' , stderr : '' } ,
205+ ] ) ,
206+ ) ;
207+ ( lstatSync as Mock ) . mockImplementation ( ( p : string ) => ( {
208+ isSymbolicLink : ( ) => p . includes ( 'resources/linux' ) ,
209+ } ) ) ;
210+ ( fs . default . access as Mock ) . mockResolvedValue ( undefined ) ;
211+ ( fs . default . readFile as Mock ) . mockResolvedValue ( Buffer . from ( 'hello' ) ) ;
212+
213+ const result = await worktreeManager . getUpdatedFiles ( testPath ) ;
214+
215+ expect ( result ) . toHaveLength ( 1 ) ;
216+ expect ( result [ 0 ] . path ) . toBe ( 'src/main.ts' ) ;
217+ } ) ;
218+
219+ it ( 'should handle binary files in no-HEAD mode' , async ( ) => {
220+ // @ts -expect-error istextorbinary library does not provide TypeScript definitions
221+ const { isBinary } = await import ( 'istextorbinary' ) ;
222+ ( isBinary as Mock ) . mockReturnValue ( true ) ;
223+
224+ useMockSeq ( createMockSeq ( [ new Error ( 'HEAD not found' ) , { stdout : '-\t-\tassets/image.png\0' , stderr : '' } ] ) ) ;
225+ ( lstatSync as Mock ) . mockReturnValue ( { isSymbolicLink : ( ) => false } ) ;
226+ ( fs . default . access as Mock ) . mockResolvedValue ( undefined ) ;
227+ ( fs . default . readFile as Mock ) . mockResolvedValue ( Buffer . from ( [ 0x89 , 0x50 , 0x4e , 0x47 ] ) ) ;
228+
229+ const result = await worktreeManager . getUpdatedFiles ( testPath ) ;
230+
231+ expect ( result ) . toHaveLength ( 1 ) ;
232+ expect ( result [ 0 ] . path ) . toBe ( 'assets/image.png' ) ;
233+ expect ( result [ 0 ] . diff ) . toBe ( '' ) ;
234+
235+ ( isBinary as Mock ) . mockReturnValue ( false ) ;
236+ } ) ;
237+
238+ it ( 'should return staged files in worktree mode when no HEAD exists' , async ( ) => {
239+ const mainBranch = 'main' ;
240+ const diffContent = 'diff --git a/src/app.ts b/src/app.ts\nnew file mode 100644\n--- /dev/null\n+++ b/src/app.ts\n@@ -0,0 +1,2 @@\n+hello\n+world' ;
241+
242+ useMockSeq (
243+ createMockSeq ( [ new Error ( 'log failed' ) , new Error ( 'HEAD not found' ) , { stdout : '2\t0\tsrc/app.ts\0' , stderr : '' } , { stdout : diffContent , stderr : '' } ] ) ,
244+ ) ;
245+ ( lstatSync as Mock ) . mockReturnValue ( { isSymbolicLink : ( ) => false } ) ;
246+ ( fs . default . access as Mock ) . mockResolvedValue ( undefined ) ;
247+ ( fs . default . readFile as Mock ) . mockResolvedValue ( Buffer . from ( 'hello' ) ) ;
248+
249+ const result = await worktreeManager . getUpdatedFiles ( testPath , 'worktree' , mainBranch ) ;
250+
251+ expect ( result ) . toHaveLength ( 1 ) ;
252+ expect ( result [ 0 ] . path ) . toBe ( 'src/app.ts' ) ;
253+ expect ( result [ 0 ] . additions ) . toBe ( 2 ) ;
254+ expect ( result [ 0 ] . diff ) . toBe ( diffContent ) ;
255+ } ) ;
256+ } ) ;
0 commit comments