@@ -367,43 +367,120 @@ it.effect("checkpoint capture refuses a truncated nested repository listing", ()
367367 } ) . pipe ( Effect . scoped , Effect . provide ( layerGitContract ) ) ,
368368) ;
369369
370- it . effect ( "checkpoint capture skips an ignored project while keeping its allowed sibling" , ( ) =>
371- Effect . gen ( function * ( ) {
372- const fs = yield * FileSystem . FileSystem ;
373- const path = yield * Path . Path ;
374- const driver = yield * GitVcsDriver . makeVcsDriverShape ( ) ;
375- const parent = yield * fs . makeTempDirectoryScoped ( { prefix : "t3-checkpoint-ignored-" } ) ;
376- const { git } = yield * makeCheckpointFixture ( driver , parent ) ;
377- yield * fs . writeFileString (
378- path . join ( parent , ".gitignore" ) ,
379- "*\n!.gitignore\n!file.txt\n!keep/\n!keep/**\n" ,
380- ) ;
381- const ignoredCwd = path . join ( parent , "ignored" ) ;
382- const keepCwd = path . join ( parent , "keep" ) ;
383- yield * fs . makeDirectory ( ignoredCwd ) ;
384- yield * fs . makeDirectory ( keepCwd ) ;
385- yield * fs . writeFileString ( path . join ( ignoredCwd , "secret.txt" ) , "private\n" ) ;
386- yield * fs . writeFileString ( path . join ( keepCwd , "new.txt" ) , "new\n" ) ;
387- const originalIndex = yield * fs . readFile ( path . join ( parent , ".git" , "index" ) ) ;
388- const ignoredRef = CheckpointRef . make ( "refs/t3/checkpoints/ignored" ) ;
389- const keepRef = CheckpointRef . make ( "refs/t3/checkpoints/keep" ) ;
390-
391- const ignored = yield * Effect . result (
392- driver . checkpoints . captureCheckpoint ( { cwd : ignoredCwd , checkpointRef : ignoredRef } ) ,
393- ) ;
394- yield * driver . checkpoints . captureCheckpoint ( { cwd : keepCwd , checkpointRef : keepRef } ) ;
370+ it . effect . each ( [
371+ { cwdName : "." , source : "root" , patterns : "a.txt\r\n\r\nb.txt\r\n" } ,
372+ { cwdName : "." , source : "root" , patterns : " \n" } ,
373+ { cwdName : "." , source : "info" , patterns : "\r\n" } ,
374+ { cwdName : "." , source : "global" , patterns : "\r\n" } ,
375+ { cwdName : "sub" , source : "root" , patterns : "\r\n" } ,
376+ { cwdName : "sub" , source : "workspace" , patterns : "\r\n" } ,
377+ { cwdName : "sub" , source : "root" , patterns : "sub/\n\r\n!sub/\n" } ,
378+ { cwdName : "sub" , source : "root" , patterns : "sub/\n!sub/\n\r\n" } ,
379+ ] ) (
380+ "checkpoint captures and restores untracked files with blank $source ignore patterns in $cwdName: $patterns" ,
381+ ( { cwdName, source, patterns } ) =>
382+ Effect . gen ( function * ( ) {
383+ const fs = yield * FileSystem . FileSystem ;
384+ const path = yield * Path . Path ;
385+ const driver = yield * GitVcsDriver . makeVcsDriverShape ( ) ;
386+ const sandbox = yield * fs . makeTempDirectoryScoped ( { prefix : "t3-checkpoint-blank-ignore-" } ) ;
387+ const repo = path . join ( sandbox , "repo" ) ;
388+ yield * fs . makeDirectory ( repo ) ;
389+ const { git, checkpointRef } = yield * makeCheckpointFixture ( driver , repo ) ;
390+ const cwd = path . join ( repo , cwdName ) ;
391+ yield * fs . makeDirectory ( cwd , { recursive : true } ) ;
392+ const prefix = cwdName === "." ? "" : `${ cwdName } /` ;
393+ yield * fs . writeFileString ( path . join ( cwd , "staged.txt" ) , "staged\n" ) ;
394+ yield * git ( [ "add" , `${ prefix } staged.txt` ] ) ;
395+ yield * fs . writeFileString ( path . join ( cwd , "staged.txt" ) , "working\n" ) ;
396+ yield * fs . writeFileString ( path . join ( cwd , "notes.md" ) , "before turn\n" ) ;
397+ yield * fs . writeFileString ( path . join ( cwd , ".gitignore" ) , "ignored.txt\n" ) ;
398+ const ignoreFile =
399+ source === "info"
400+ ? path . join ( repo , ".git" , "info" , "exclude" )
401+ : source === "global"
402+ ? path . join ( sandbox , "excludes" )
403+ : path . join ( source === "root" ? repo : cwd , ".gitignore" ) ;
404+ yield * fs . writeFileString ( ignoreFile , `${ patterns } ignored.txt\n` ) ;
405+ if ( source === "global" ) yield * git ( [ "config" , "core.excludesFile" , ignoreFile ] ) ;
406+ yield * fs . writeFileString ( path . join ( cwd , "ignored.txt" ) , "private\n" ) ;
407+ const originalIndex = yield * fs . readFile ( path . join ( repo , ".git" , "index" ) ) ;
395408
396- assert . strictEqual ( ( yield * git ( [ "show" , `${ keepRef } :keep/new.txt` ] ) ) . stdout , "new\n" ) ;
397- assert . strictEqual ( ( yield * git ( [ "ls-tree" , "-r" , keepRef , "--" , "ignored" ] ) ) . stdout , "" ) ;
398- assert . isFalse (
399- yield * driver . checkpoints . hasCheckpointRef ( { cwd : parent , checkpointRef : ignoredRef } ) ,
400- ) ;
401- assert . deepEqual ( yield * fs . readFile ( path . join ( parent , ".git" , "index" ) ) , originalIndex ) ;
402- assert . strictEqual ( ignored . _tag , "Failure" ) ;
403- if ( ignored . _tag === "Failure" ) {
404- assert . strictEqual ( ignored . failure . _tag , "VcsUnsupportedOperationError" ) ;
405- }
406- } ) . pipe ( Effect . scoped , Effect . provide ( layerGitContract ) ) ,
409+ yield * driver . checkpoints . captureCheckpoint ( { cwd, checkpointRef } ) ;
410+
411+ assert . include (
412+ ( yield * git ( [ "ls-tree" , "-r" , "--name-only" , checkpointRef ] ) ) . stdout . split ( "\n" ) ,
413+ `${ prefix } notes.md` ,
414+ ) ;
415+ assert . strictEqual (
416+ ( yield * git ( [ "show" , `${ checkpointRef } :${ prefix } notes.md` ] ) ) . stdout ,
417+ "before turn\n" ,
418+ ) ;
419+ assert . strictEqual (
420+ ( yield * git ( [ "show" , `${ checkpointRef } :${ prefix } staged.txt` ] ) ) . stdout ,
421+ "working\n" ,
422+ ) ;
423+ yield * fs . writeFileString ( path . join ( cwd , "notes.md" ) , "after turn\n" ) ;
424+ yield * fs . writeFileString ( path . join ( cwd , "new.txt" ) , "created during turn\n" ) ;
425+ const afterRef = CheckpointRef . make ( "refs/t3/checkpoints/after" ) ;
426+ yield * driver . checkpoints . captureCheckpoint ( { cwd, checkpointRef : afterRef } ) ;
427+ assert . strictEqual (
428+ ( yield * git ( [ "show" , `${ afterRef } :${ prefix } new.txt` ] ) ) . stdout ,
429+ "created during turn\n" ,
430+ ) ;
431+ assert . strictEqual (
432+ ( yield * git ( [ "ls-tree" , "-r" , afterRef , "--" , `${ prefix } ignored.txt` ] ) ) . stdout ,
433+ "" ,
434+ ) ;
435+ assert . deepEqual ( yield * fs . readFile ( path . join ( repo , ".git" , "index" ) ) , originalIndex ) ;
436+
437+ assert . isTrue ( yield * driver . checkpoints . restoreCheckpoint ( { cwd, checkpointRef } ) ) ;
438+ assert . strictEqual ( yield * fs . readFileString ( path . join ( cwd , "notes.md" ) ) , "before turn\n" ) ;
439+ assert . strictEqual ( yield * fs . readFileString ( path . join ( cwd , "staged.txt" ) ) , "working\n" ) ;
440+ assert . isFalse ( yield * fs . exists ( path . join ( cwd , "new.txt" ) ) ) ;
441+ assert . strictEqual ( yield * fs . readFileString ( path . join ( cwd , "ignored.txt" ) ) , "private\n" ) ;
442+ } ) . pipe ( Effect . scoped , Effect . provide ( layerGitContract ) ) ,
443+ ) ;
444+
445+ it . effect . each ( [ "" , "\r\n" , " \n" ] ) (
446+ "checkpoint capture skips an ignored project while keeping its allowed sibling with blank pattern %j" ,
447+ ( blank ) =>
448+ Effect . gen ( function * ( ) {
449+ const fs = yield * FileSystem . FileSystem ;
450+ const path = yield * Path . Path ;
451+ const driver = yield * GitVcsDriver . makeVcsDriverShape ( ) ;
452+ const parent = yield * fs . makeTempDirectoryScoped ( { prefix : "t3-checkpoint-ignored-" } ) ;
453+ const { git } = yield * makeCheckpointFixture ( driver , parent ) ;
454+ yield * fs . writeFileString (
455+ path . join ( parent , ".gitignore" ) ,
456+ `*\n!.gitignore\n!file.txt\n!keep/\n!keep/**\n${ blank } ` ,
457+ ) ;
458+ const ignoredCwd = path . join ( parent , "ignored" ) ;
459+ const keepCwd = path . join ( parent , "keep" ) ;
460+ yield * fs . makeDirectory ( ignoredCwd ) ;
461+ yield * fs . makeDirectory ( keepCwd ) ;
462+ yield * fs . writeFileString ( path . join ( ignoredCwd , "secret.txt" ) , "private\n" ) ;
463+ yield * fs . writeFileString ( path . join ( keepCwd , "new.txt" ) , "new\n" ) ;
464+ const originalIndex = yield * fs . readFile ( path . join ( parent , ".git" , "index" ) ) ;
465+ const ignoredRef = CheckpointRef . make ( "refs/t3/checkpoints/ignored" ) ;
466+ const keepRef = CheckpointRef . make ( "refs/t3/checkpoints/keep" ) ;
467+
468+ const ignored = yield * Effect . result (
469+ driver . checkpoints . captureCheckpoint ( { cwd : ignoredCwd , checkpointRef : ignoredRef } ) ,
470+ ) ;
471+ yield * driver . checkpoints . captureCheckpoint ( { cwd : keepCwd , checkpointRef : keepRef } ) ;
472+
473+ assert . strictEqual ( ( yield * git ( [ "show" , `${ keepRef } :keep/new.txt` ] ) ) . stdout , "new\n" ) ;
474+ assert . strictEqual ( ( yield * git ( [ "ls-tree" , "-r" , keepRef , "--" , "ignored" ] ) ) . stdout , "" ) ;
475+ assert . isFalse (
476+ yield * driver . checkpoints . hasCheckpointRef ( { cwd : parent , checkpointRef : ignoredRef } ) ,
477+ ) ;
478+ assert . deepEqual ( yield * fs . readFile ( path . join ( parent , ".git" , "index" ) ) , originalIndex ) ;
479+ assert . strictEqual ( ignored . _tag , "Failure" ) ;
480+ if ( ignored . _tag === "Failure" ) {
481+ assert . strictEqual ( ignored . failure . _tag , "VcsUnsupportedOperationError" ) ;
482+ }
483+ } ) . pipe ( Effect . scoped , Effect . provide ( layerGitContract ) ) ,
407484) ;
408485
409486it . effect . each ( [
@@ -433,7 +510,8 @@ it.effect.each([
433510 yield * git ( [ "add" , `sub/${ file } ` ] ) ;
434511 }
435512 const tracked = [ ...committed , ...staged ] ;
436- yield * fs . writeFileString ( path . join ( parent , ".gitignore" ) , "sub/\n" ) ;
513+ yield * fs . writeFileString ( path . join ( parent , ".gitignore" ) , "\r\nsub/\n\r\n" ) ;
514+ yield * fs . writeFileString ( path . join ( cwd , ".gitignore" ) , "\r\n" ) ;
437515 for ( const file of tracked ) {
438516 yield * fs . writeFileString ( path . join ( cwd , file ) , `${ file } working\n` ) ;
439517 }
0 commit comments