11import { existsSync , lstatSync , readFileSync , realpathSync , statSync } from 'node:fs' ;
2- import { dirname , isAbsolute , join , parse as parsePath , resolve , sep } from 'node:path' ;
2+ import { dirname , isAbsolute , join , resolve } from 'node:path' ;
3+ import { resolveChdirTarget } from '@/core/path' ;
34
45export const GIT_GLOBAL_OPTS_WITH_VALUE : ReadonlySet < string > = new Set ( [
56 '-c' ,
@@ -18,6 +19,14 @@ export const GIT_CONTEXT_ENV_OVERRIDES = [
1819 'GIT_INDEX_FILE' ,
1920] as const ;
2021
22+ export const GIT_CONFIG_AFFECTING_ENV_NAMES : ReadonlySet < string > = new Set ( [
23+ 'GIT_CONFIG_GLOBAL' ,
24+ 'GIT_CONFIG_NOSYSTEM' ,
25+ 'GIT_CONFIG_SYSTEM' ,
26+ 'HOME' ,
27+ 'XDG_CONFIG_HOME' ,
28+ ] ) ;
29+
2130export interface GitExecutionContext {
2231 gitCwd : string | null ;
2332 hasExplicitGitContext : boolean ;
@@ -246,62 +255,66 @@ function readCoreWorktree(configPath: string): string | null {
246255
247256 const match = trimmed . match ( / ^ w o r k t r e e \s * = \s * ( .* ) $ / i) ;
248257 if ( match ) {
249- configuredWorktree = unquoteGitConfigValue ( match [ 1 ] ?? '' ) ;
258+ configuredWorktree = parseGitConfigValue ( match [ 1 ] ?? '' ) ;
250259 }
251260 }
252261
253262 return configuredWorktree ;
254263}
255264
256- function unquoteGitConfigValue ( value : string ) : string {
265+ function parseGitConfigValue ( value : string ) : string {
257266 const trimmed = value . trim ( ) ;
258- if (
259- ( trimmed . startsWith ( '"' ) && trimmed . endsWith ( '"' ) ) ||
260- ( trimmed . startsWith ( "'" ) && trimmed . endsWith ( "'" ) )
261- ) {
262- return trimmed . slice ( 1 , - 1 ) ;
267+ if ( ! trimmed . startsWith ( '"' ) || ! trimmed . endsWith ( '"' ) ) {
268+ return trimmed ;
263269 }
264- return trimmed ;
270+ return unescapeDoubleQuotedGitConfigValue ( trimmed . slice ( 1 , - 1 ) ) ;
265271}
266272
267- function resolveGitCwd ( baseCwd : string , target : string ) : string | null {
268- try {
269- const resolved = resolveChdirTarget ( baseCwd , target ) ;
270- return isDirectory ( resolved ) ? resolved : null ;
271- } catch {
272- return null ;
273- }
274- }
275-
276- function resolveChdirTarget ( baseCwd : string , target : string ) : string {
277- const root = isAbsolute ( target ) ? getPathRoot ( target ) : '' ;
278- let current = root || baseCwd ;
279- for ( const component of getPathComponents ( root ? target . slice ( root . length ) : target ) ) {
280- if ( component === '' || component === '.' ) {
273+ function unescapeDoubleQuotedGitConfigValue ( value : string ) : string {
274+ let result = '' ;
275+ for ( let i = 0 ; i < value . length ; i ++ ) {
276+ const char = value [ i ] ;
277+ if ( char !== '\\' ) {
278+ result += char ;
281279 continue ;
282280 }
283- if ( component === '..' ) {
284- current = dirname ( current ) ;
281+
282+ const next = value [ i + 1 ] ;
283+ if ( next === undefined ) {
284+ result += char ;
285285 continue ;
286286 }
287287
288- const candidate = appendPathWithoutNormalizing ( current , component ) ;
289- current = lstatSync ( candidate ) . isSymbolicLink ( ) ? realpathSync ( candidate ) : candidate ;
288+ switch ( next ) {
289+ case '\\' :
290+ case '"' :
291+ result += next ;
292+ break ;
293+ case 'n' :
294+ result += '\n' ;
295+ break ;
296+ case 't' :
297+ result += '\t' ;
298+ break ;
299+ case 'b' :
300+ result += '\b' ;
301+ break ;
302+ default :
303+ result += `\\${ next } ` ;
304+ break ;
305+ }
306+ i ++ ;
290307 }
291- return current ;
308+ return result ;
292309}
293310
294- function appendPathWithoutNormalizing ( base : string , target : string ) : string {
295- return base . endsWith ( '/' ) || base . endsWith ( '\\' ) ? `${ base } ${ target } ` : `${ base } ${ sep } ${ target } ` ;
296- }
297-
298- function getPathRoot ( target : string ) : string {
299- return parsePath ( target ) . root ;
300- }
301-
302- function getPathComponents ( target : string ) : string [ ] {
303- const separator = process . platform === 'win32' ? / [ \\ / ] + / : / \/ + / ;
304- return target . split ( separator ) ;
311+ function resolveGitCwd ( baseCwd : string , target : string ) : string | null {
312+ try {
313+ const resolved = resolveChdirTarget ( baseCwd , target ) ;
314+ return isDirectory ( resolved ) ? resolved : null ;
315+ } catch {
316+ return null ;
317+ }
305318}
306319
307320function isDirectory ( path : string ) : boolean {
@@ -333,3 +346,6 @@ function findDotGit(cwd: string): string | null {
333346 current = parent ;
334347 }
335348}
349+
350+ /** @internal Exported for testing */
351+ export { parseGitConfigValue as _parseGitConfigValue } ;
0 commit comments