1
- import { resolve , SEPARATOR } from "jsr:@std/path@^1.0.0" ;
1
+ import { resolve } from "jsr:@std/path@^1.0.0" ;
2
2
import { Cache } from "jsr:@lambdalisue/ttl-cache@^1.0.0" ;
3
3
import { execute } from "./process.ts" ;
4
4
import { decodeUtf8 } from "../util/text.ts" ;
@@ -7,6 +7,17 @@ const ttl = 30000; // seconds
7
7
const cacheWorktree = new Cache < string , string | Error > ( ttl ) ;
8
8
const cacheGitdir = new Cache < string , string | Error > ( ttl ) ;
9
9
10
+ /**
11
+ * Check if we've reached the filesystem root by comparing a path with its parent.
12
+ * This works cross-platform (Windows, Linux, Mac).
13
+ *
14
+ * @param currentPath - The path to check
15
+ * @returns true if the path is the filesystem root, false otherwise
16
+ */
17
+ function isFilesystemRoot ( currentPath : string ) : boolean {
18
+ return currentPath === resolve ( currentPath , ".." ) ;
19
+ }
20
+
10
21
/**
11
22
* Find a root path of a git working directory.
12
23
*
@@ -18,12 +29,24 @@ export async function findWorktree(cwd: string): Promise<string> {
18
29
const result = cacheWorktree . get ( path ) ?? await ( async ( ) => {
19
30
let result : string | Error ;
20
31
try {
32
+ // Search upward from current directory to find worktree root
33
+ let currentPath = path ;
34
+ while ( ! isFilesystemRoot ( currentPath ) ) {
35
+ if ( await isWorktreeRoot ( currentPath ) ) {
36
+ result = currentPath ;
37
+ cacheWorktree . set ( path , result ) ;
38
+ return result ;
39
+ }
40
+ currentPath = resolve ( currentPath , ".." ) ;
41
+ }
42
+
43
+ // If no worktree found, use normal detection for regular repositories
21
44
result = await revParse ( path , [
22
45
"--show-toplevel" ,
23
46
"--show-superproject-working-tree" ,
24
47
] ) ;
25
48
} catch ( e ) {
26
- result = e ;
49
+ result = e as Error ;
27
50
}
28
51
cacheWorktree . set ( path , result ) ;
29
52
return result ;
@@ -47,7 +70,7 @@ export async function findGitdir(cwd: string): Promise<string> {
47
70
try {
48
71
result = await revParse ( path , [ "--git-dir" ] ) ;
49
72
} catch ( e ) {
50
- result = e ;
73
+ result = e as Error ;
51
74
}
52
75
cacheGitdir . set ( path , result ) ;
53
76
return result ;
@@ -58,14 +81,22 @@ export async function findGitdir(cwd: string): Promise<string> {
58
81
return result ;
59
82
}
60
83
61
- async function revParse ( cwd : string , args : string [ ] ) : Promise < string > {
62
- const terms = cwd . split ( SEPARATOR ) ;
63
- if ( terms . includes ( ".git" ) ) {
64
- // `git rev-parse` does not work in a ".git" directory
65
- // so use a parent directory of it instead.
66
- const index = terms . indexOf ( ".git" ) ;
67
- cwd = terms . slice ( 0 , index ) . join ( SEPARATOR ) ;
84
+ async function isWorktreeRoot ( currentPath : string ) : Promise < boolean > {
85
+ try {
86
+ const gitPath = resolve ( currentPath , ".git" ) ;
87
+ const stat = await Deno . stat ( gitPath ) ;
88
+ if ( stat . isFile ) {
89
+ // Found a .git file, verify it's a valid git worktree
90
+ await revParse ( currentPath , [ "--git-dir" ] ) ;
91
+ return true ;
92
+ }
93
+ } catch {
94
+ // Either .git doesn't exist or it's not a valid worktree
68
95
}
96
+ return false ;
97
+ }
98
+
99
+ async function revParse ( cwd : string , args : string [ ] ) : Promise < string > {
69
100
const stdout = await execute ( [ "rev-parse" , ...args ] , { cwd } ) ;
70
101
const output = decodeUtf8 ( stdout ) ;
71
102
return resolve ( output . split ( / \n / , 2 ) [ 0 ] ) ;
0 commit comments