@@ -16,6 +16,8 @@ import {
1616import { testEffect } from "../lib/effect"
1717
1818const it = testEffect ( Layer . mergeAll ( Snapshot . defaultLayer , FSUtil . defaultLayer , testInstanceStoreLayer ) )
19+ // Windows forbids both * and : in directory names.
20+ const nonWindowsIt = process . platform === "win32" ? it . live . skip : it . live
1921
2022// Git always outputs /-separated paths internally. Snapshot.patch() joins them
2123// with path.join (which produces \ on Windows) then normalizes back to /.
@@ -448,6 +450,97 @@ it.live(
448450 } ) ,
449451)
450452
453+ it . live (
454+ "subdirectory snapshots include scoped changes only" ,
455+ Effect . gen ( function * ( ) {
456+ const dir = yield * scopedGitTmpdir ( )
457+ const frontend = path . join ( dir , "frontend" )
458+ yield * write ( `${ frontend } /tracked.txt` , "initial" )
459+ yield * write ( `${ frontend } /deleted.txt` , "initial" )
460+ yield * write ( `${ dir } /backend/tracked.txt` , "initial" )
461+ yield * write ( `${ dir } /backend/deleted.txt` , "initial" )
462+ yield * exec ( dir , [ "git" , "add" , "." ] )
463+ yield * exec ( dir , [ "git" , "commit" , "-m" , "init" ] )
464+ yield * Effect . gen ( function * ( ) {
465+ const snapshot = yield * Snapshot . Service
466+ const before = yield * snapshot . track ( )
467+ expect ( before ) . toBeTruthy ( )
468+ yield * write ( `${ frontend } /tracked.txt` , "changed" )
469+ yield * write ( `${ frontend } /untracked.txt` , "new" )
470+ yield * rm ( `${ frontend } /deleted.txt` )
471+ yield * write ( `${ dir } /backend/tracked.txt` , "changed" )
472+ yield * rm ( `${ dir } /backend/deleted.txt` )
473+ const patch = yield * snapshot . patch ( before ! )
474+ const diff = yield * snapshot . diff ( before ! )
475+ expect ( patch . files ) . toContain ( fwd ( frontend , "tracked.txt" ) )
476+ expect ( patch . files ) . toContain ( fwd ( frontend , "untracked.txt" ) )
477+ expect ( patch . files ) . toContain ( fwd ( frontend , "deleted.txt" ) )
478+ expect ( patch . files ) . not . toContain ( fwd ( dir , "backend" , "tracked.txt" ) )
479+ expect ( patch . files ) . not . toContain ( fwd ( dir , "backend" , "deleted.txt" ) )
480+ expect ( diff ) . not . toContain ( "backend/tracked.txt" )
481+ expect ( diff ) . not . toContain ( "backend/deleted.txt" )
482+ } ) . pipe ( provideInstance ( frontend ) )
483+ } ) ,
484+ )
485+
486+ nonWindowsIt (
487+ "subdirectory snapshots treat wildcard characters literally" ,
488+ Effect . gen ( function * ( ) {
489+ const dir = yield * scopedGitTmpdir ( )
490+ const subdir = path . join ( dir , "src*" )
491+ yield * write ( `${ subdir } /file.txt` , "initial" )
492+ yield * write ( `${ subdir } /later-ignored.txt` , "initial" )
493+ yield * write ( `${ dir } /srca/file.txt` , "initial" )
494+ yield * exec ( dir , [ "git" , "add" , "." ] )
495+ yield * exec ( dir , [ "git" , "commit" , "-m" , "init" ] )
496+ yield * Effect . gen ( function * ( ) {
497+ const snapshot = yield * Snapshot . Service
498+ const before = yield * snapshot . track ( )
499+ expect ( before ) . toBeTruthy ( )
500+ yield * write ( `${ subdir } /file.txt` , "changed" )
501+ yield * write ( `${ subdir } /later-ignored.txt` , "changed" )
502+ yield * write ( `${ subdir } /.gitignore` , "later-ignored.txt\n" )
503+ yield * write ( `${ dir } /srca/file.txt` , "changed" )
504+ const patch = yield * snapshot . patch ( before ! )
505+ const diff = yield * snapshot . diff ( before ! )
506+ expect ( patch . files ) . toContain ( fwd ( subdir , "file.txt" ) )
507+ expect ( patch . files ) . toContain ( fwd ( subdir , ".gitignore" ) )
508+ expect ( patch . files ) . not . toContain ( fwd ( subdir , "later-ignored.txt" ) )
509+ expect ( patch . files ) . not . toContain ( fwd ( dir , "srca" , "file.txt" ) )
510+ expect ( diff ) . toContain ( "src*/later-ignored.txt" )
511+ expect ( diff ) . toContain ( "deleted file mode" )
512+ expect ( diff ) . not . toContain ( "srca/file.txt" )
513+ } ) . pipe ( provideInstance ( subdir ) )
514+ } ) ,
515+ )
516+
517+ nonWindowsIt (
518+ "subdirectory snapshots treat leading colons literally" ,
519+ Effect . gen ( function * ( ) {
520+ const dir = yield * scopedGitTmpdir ( )
521+ const subdir = path . join ( dir , ":src" )
522+ yield * write ( `${ subdir } /kept.txt` , "initial" )
523+ yield * write ( `${ subdir } /later-ignored.txt` , "initial" )
524+ yield * exec ( dir , [ "git" , "add" , "." ] )
525+ yield * exec ( dir , [ "git" , "commit" , "-m" , "init" ] )
526+ yield * Effect . gen ( function * ( ) {
527+ const snapshot = yield * Snapshot . Service
528+ const before = yield * snapshot . track ( )
529+ expect ( before ) . toBeTruthy ( )
530+ yield * write ( `${ subdir } /kept.txt` , "changed" )
531+ yield * write ( `${ subdir } /later-ignored.txt` , "changed" )
532+ yield * write ( `${ subdir } /.gitignore` , "later-ignored.txt\n" )
533+ const patch = yield * snapshot . patch ( before ! )
534+ const diff = yield * snapshot . diff ( before ! )
535+ expect ( patch . files ) . toContain ( fwd ( subdir , "kept.txt" ) )
536+ expect ( patch . files ) . toContain ( fwd ( subdir , ".gitignore" ) )
537+ expect ( patch . files ) . not . toContain ( fwd ( subdir , "later-ignored.txt" ) )
538+ expect ( diff ) . toContain ( ":src/later-ignored.txt" )
539+ expect ( diff ) . toContain ( "deleted file mode" )
540+ } ) . pipe ( provideInstance ( subdir ) )
541+ } ) ,
542+ )
543+
451544it . instance (
452545 "gitignore changes" ,
453546 withTrackedSnapshot ( ( { tmp, snapshot, before } ) =>
0 commit comments