11import { execFile } from 'node:child_process' ;
2- import { mkdir , mkdtemp , readFile , rm , writeFile } from 'node:fs/promises' ;
2+ import { lstat , mkdir , mkdtemp , readFile , rm , writeFile } from 'node:fs/promises' ;
33import { tmpdir } from 'node:os' ;
44import { join } from 'node:path' ;
55import { promisify } from 'node:util' ;
@@ -208,7 +208,7 @@ describe('WorktreeManager', () => {
208208 } ) ;
209209 } ) ;
210210
211- describe ( 'copying .gitignore-d files into a new worktree' , ( ) => {
211+ describe ( 'linking/ copying .gitignore-d files into a new worktree' , ( ) => {
212212 beforeEach ( async ( ) => {
213213 repo = await makeRepo ( true ) ;
214214 // ignore node_modules/ and .env, then leave them untracked on disk
@@ -220,23 +220,45 @@ describe('WorktreeManager', () => {
220220 await writeFile ( join ( repo , '.env' ) , 'SECRET=1\n' ) ;
221221 } ) ;
222222
223- it ( 'copies ignored files/dirs from the repo root by default' , async ( ) => {
223+ it ( 'symlinks ignored files/dirs to the repo root by default' , async ( ) => {
224224 const wm = new WorktreeManager ( repo ) ;
225225 const wt = await wm . add ( 'with-ignored' ) ;
226+ // symlink なので実体はリポジトリルート側と共有される(読むと元の内容が見える)
226227 expect ( await readFile ( join ( wt . path , '.env' ) , 'utf8' ) ) . toBe ( 'SECRET=1\n' ) ;
227228 expect ( await readFile ( join ( wt . path , 'node_modules' , 'dep' , 'index.js' ) , 'utf8' ) ) . toBe (
228229 'module.exports = 1\n' ,
229230 ) ;
231+ expect ( ( await lstat ( join ( wt . path , '.env' ) ) ) . isSymbolicLink ( ) ) . toBe ( true ) ;
232+ expect ( ( await lstat ( join ( wt . path , 'node_modules' ) ) ) . isSymbolicLink ( ) ) . toBe ( true ) ;
230233 } ) ;
231234
232- it ( 'does not copy .codiva (would recurse into worktrees)' , async ( ) => {
235+ it ( 'copies real files (not symlinks) when ignoredFiles is "copy"' , async ( ) => {
236+ const wm = new WorktreeManager ( repo , { ignoredFiles : 'copy' } ) ;
237+ const wt = await wm . add ( 'copied' ) ;
238+ expect ( await readFile ( join ( wt . path , '.env' ) , 'utf8' ) ) . toBe ( 'SECRET=1\n' ) ;
239+ expect ( await readFile ( join ( wt . path , 'node_modules' , 'dep' , 'index.js' ) , 'utf8' ) ) . toBe (
240+ 'module.exports = 1\n' ,
241+ ) ;
242+ expect ( ( await lstat ( join ( wt . path , '.env' ) ) ) . isSymbolicLink ( ) ) . toBe ( false ) ;
243+ expect ( ( await lstat ( join ( wt . path , 'node_modules' ) ) ) . isSymbolicLink ( ) ) . toBe ( false ) ;
244+ } ) ;
245+
246+ it ( 'copy mode keeps the worktree fully independent from the repo root' , async ( ) => {
247+ const wm = new WorktreeManager ( repo , { ignoredFiles : 'copy' } ) ;
248+ const wt = await wm . add ( 'independent' ) ;
249+ // worktree 側を書き換えても元へ波及しない(symlink との差)
250+ await writeFile ( join ( wt . path , '.env' ) , 'SECRET=changed\n' ) ;
251+ expect ( await readFile ( join ( repo , '.env' ) , 'utf8' ) ) . toBe ( 'SECRET=1\n' ) ;
252+ } ) ;
253+
254+ it ( 'does not link .codiva (would recurse into worktrees)' , async ( ) => {
233255 const wm = new WorktreeManager ( repo ) ;
234256 const wt = await wm . add ( 'no-codiva' ) ;
235257 await expect ( readFile ( join ( wt . path , '.codiva' , 'state.json' ) , 'utf8' ) ) . rejects . toBeTruthy ( ) ;
236258 } ) ;
237259
238- it ( 'skips copying when copyIgnored is false ' , async ( ) => {
239- const wm = new WorktreeManager ( repo , { copyIgnored : false } ) ;
260+ it ( 'skips linking when ignoredFiles is "none" ' , async ( ) => {
261+ const wm = new WorktreeManager ( repo , { ignoredFiles : 'none' } ) ;
240262 const wt = await wm . add ( 'bare' ) ;
241263 await expect ( readFile ( join ( wt . path , '.env' ) , 'utf8' ) ) . rejects . toBeTruthy ( ) ;
242264 } ) ;
0 commit comments