@@ -472,3 +472,87 @@ describe("Project.addSandbox and Project.removeSandbox", () => {
472472 expect ( events . some ( ( e ) => e . payload . type === Project . Event . Updated . type ) ) . toBe ( true )
473473 } )
474474} )
475+
476+ describe ( "Project.fromDirectory with bare repos" , ( ) => {
477+ test ( "worktree from bare repo should cache in bare repo, not parent" , async ( ) => {
478+ await using tmp = await tmpdir ( { git : true } )
479+
480+ const parentDir = path . dirname ( tmp . path )
481+ const barePath = path . join ( parentDir , `bare-${ Date . now ( ) } .git` )
482+ const worktreePath = path . join ( parentDir , `worktree-${ Date . now ( ) } ` )
483+
484+ try {
485+ await $ `git clone --bare ${ tmp . path } ${ barePath } ` . quiet ( )
486+ await $ `git worktree add ${ worktreePath } HEAD` . cwd ( barePath ) . quiet ( )
487+
488+ const { project } = await run ( ( svc ) => svc . fromDirectory ( worktreePath ) )
489+
490+ expect ( project . id ) . not . toBe ( ProjectID . global )
491+ expect ( project . worktree ) . toBe ( barePath )
492+
493+ const correctCache = path . join ( barePath , "opencode" )
494+ const wrongCache = path . join ( parentDir , ".git" , "opencode" )
495+
496+ expect ( await Bun . file ( correctCache ) . exists ( ) ) . toBe ( true )
497+ expect ( await Bun . file ( wrongCache ) . exists ( ) ) . toBe ( false )
498+ } finally {
499+ await $ `rm -rf ${ barePath } ${ worktreePath } ` . quiet ( ) . nothrow ( )
500+ }
501+ } )
502+
503+ test ( "different bare repos under same parent should not share project ID" , async ( ) => {
504+ await using tmp1 = await tmpdir ( { git : true } )
505+ await using tmp2 = await tmpdir ( { git : true } )
506+
507+ const parentDir = path . dirname ( tmp1 . path )
508+ const bareA = path . join ( parentDir , `bare-a-${ Date . now ( ) } .git` )
509+ const bareB = path . join ( parentDir , `bare-b-${ Date . now ( ) } .git` )
510+ const worktreeA = path . join ( parentDir , `wt-a-${ Date . now ( ) } ` )
511+ const worktreeB = path . join ( parentDir , `wt-b-${ Date . now ( ) } ` )
512+
513+ try {
514+ await $ `git clone --bare ${ tmp1 . path } ${ bareA } ` . quiet ( )
515+ await $ `git clone --bare ${ tmp2 . path } ${ bareB } ` . quiet ( )
516+ await $ `git worktree add ${ worktreeA } HEAD` . cwd ( bareA ) . quiet ( )
517+ await $ `git worktree add ${ worktreeB } HEAD` . cwd ( bareB ) . quiet ( )
518+
519+ const { project : projA } = await run ( ( svc ) => svc . fromDirectory ( worktreeA ) )
520+ const { project : projB } = await run ( ( svc ) => svc . fromDirectory ( worktreeB ) )
521+
522+ expect ( projA . id ) . not . toBe ( projB . id )
523+
524+ const cacheA = path . join ( bareA , "opencode" )
525+ const cacheB = path . join ( bareB , "opencode" )
526+ const wrongCache = path . join ( parentDir , ".git" , "opencode" )
527+
528+ expect ( await Bun . file ( cacheA ) . exists ( ) ) . toBe ( true )
529+ expect ( await Bun . file ( cacheB ) . exists ( ) ) . toBe ( true )
530+ expect ( await Bun . file ( wrongCache ) . exists ( ) ) . toBe ( false )
531+ } finally {
532+ await $ `rm -rf ${ bareA } ${ bareB } ${ worktreeA } ${ worktreeB } ` . quiet ( ) . nothrow ( )
533+ }
534+ } )
535+
536+ test ( "bare repo without .git suffix is still detected via core.bare" , async ( ) => {
537+ await using tmp = await tmpdir ( { git : true } )
538+
539+ const parentDir = path . dirname ( tmp . path )
540+ const barePath = path . join ( parentDir , `bare-no-suffix-${ Date . now ( ) } ` )
541+ const worktreePath = path . join ( parentDir , `worktree-${ Date . now ( ) } ` )
542+
543+ try {
544+ await $ `git clone --bare ${ tmp . path } ${ barePath } ` . quiet ( )
545+ await $ `git worktree add ${ worktreePath } HEAD` . cwd ( barePath ) . quiet ( )
546+
547+ const { project } = await run ( ( svc ) => svc . fromDirectory ( worktreePath ) )
548+
549+ expect ( project . id ) . not . toBe ( ProjectID . global )
550+ expect ( project . worktree ) . toBe ( barePath )
551+
552+ const correctCache = path . join ( barePath , "opencode" )
553+ expect ( await Bun . file ( correctCache ) . exists ( ) ) . toBe ( true )
554+ } finally {
555+ await $ `rm -rf ${ barePath } ${ worktreePath } ` . quiet ( ) . nothrow ( )
556+ }
557+ } )
558+ } )
0 commit comments