11import { afterEach , beforeEach , expect , test } from "bun:test" ;
2- import { mkdir , rm , writeFile } from "node:fs/promises" ;
2+ import { chmod , mkdir , readFile , rm , writeFile } from "node:fs/promises" ;
33import { realpathSync } from "node:fs" ;
4- import { join } from "node:path" ;
4+ import { delimiter , join } from "node:path" ;
55import { tmpdir } from "node:os" ;
66import { execFileSync } from "node:child_process" ;
77
@@ -27,6 +27,10 @@ afterEach(async () => {
2727 await rm ( root , { recursive : true , force : true } ) ;
2828} ) ;
2929
30+ function shQuote ( value : string ) : string {
31+ return `'${ value . replaceAll ( "'" , "'\\''" ) } '` ;
32+ }
33+
3034async function commitReadme ( dir : string ) : Promise < void > {
3135 await writeFile ( join ( dir , "README" ) , "x" ) ;
3236 execFileSync ( "git" , [ "add" , "README" ] , { cwd : dir , stdio : "ignore" } ) ;
@@ -51,6 +55,49 @@ test("projectKeyFor shares nested dirs under the same git toplevel", async () =>
5155 expect ( projectKeyFor ( nested ) ) . toBe ( projectKeyFor ( root ) ) ;
5256} ) ;
5357
58+ test ( "projectRootFor spawns git rev-parse once for the same cwd" , async ( ) => {
59+ initTemporaryGitRepo ( root ) ;
60+ await commitReadme ( root ) ;
61+
62+ const countFile = join ( root , "rev-parse-spawns" ) ;
63+ await writeFile ( countFile , "0\n" ) ;
64+ const realGit = execFileSync ( "/usr/bin/which" , [ "git" ] , {
65+ encoding : "utf8" ,
66+ } ) . trim ( ) ;
67+ const bin = join ( root , "git-bin" ) ;
68+ await mkdir ( bin ) ;
69+ const wrapper = join ( bin , "git" ) ;
70+ const resolved = realpathSync ( root ) ;
71+ await writeFile (
72+ wrapper ,
73+ `#!/bin/sh
74+ if [ "$1" = "rev-parse" ] && [ "$(pwd)" = ${ shQuote ( resolved ) } ]; then
75+ n=$(cat ${ shQuote ( countFile ) } )
76+ echo $((n + 1)) > ${ shQuote ( countFile ) }
77+ fi
78+ exec ${ shQuote ( realGit ) } "$@"
79+ ` ,
80+ ) ;
81+ await chmod ( wrapper , 0o755 ) ;
82+
83+ const previousPath = process . env . PATH ;
84+ process . env . PATH = `${ bin } ${ delimiter } ${ previousPath ?? "" } ` ;
85+ try {
86+ const first = projectRootFor ( root ) ;
87+ const second = projectRootFor ( root ) ;
88+ expect ( second ) . toBe ( first ) ;
89+ expect ( first ) . toBe ( realpathSync ( root ) ) ;
90+ const spawns = Number ( ( await readFile ( countFile , "utf8" ) ) . trim ( ) ) ;
91+ expect ( spawns ) . toBe ( 1 ) ;
92+ } finally {
93+ if ( previousPath === undefined ) {
94+ delete process . env . PATH ;
95+ } else {
96+ process . env . PATH = previousPath ;
97+ }
98+ }
99+ } ) ;
100+
54101test ( "linked worktrees have distinct project roots and keys from main and each other" , async ( ) => {
55102 initTemporaryGitRepo ( root ) ;
56103 await commitReadme ( root ) ;
0 commit comments