@@ -27,6 +27,7 @@ import {
2727 type ExecuteGitProgress ,
2828 type GitCommitOptions ,
2929 type GitCoreShape ,
30+ type GitStatusDetails ,
3031 type ExecuteGitInput ,
3132 type ExecuteGitResult ,
3233} from "../Services/GitCore.ts" ;
@@ -59,6 +60,18 @@ const STATUS_UPSTREAM_REFRESH_FAILURE_COOLDOWN = Duration.seconds(5);
5960const STATUS_UPSTREAM_REFRESH_CACHE_CAPACITY = 2_048 ;
6061const DEFAULT_BASE_BRANCH_CANDIDATES = [ "main" , "master" ] as const ;
6162const GIT_LIST_BRANCHES_DEFAULT_LIMIT = 100 ;
63+ const NON_REPOSITORY_STATUS_DETAILS = Object . freeze < GitStatusDetails > ( {
64+ isRepo : false ,
65+ hasOriginRemote : false ,
66+ isDefaultBranch : false ,
67+ branch : null ,
68+ upstreamRef : null ,
69+ hasWorkingTreeChanges : false ,
70+ workingTree : { files : [ ] , insertions : 0 , deletions : 0 } ,
71+ hasUpstream : false ,
72+ aheadCount : 0 ,
73+ behindCount : 0 ,
74+ } ) ;
6275
6376type TraceTailState = {
6477 processedChars : number ;
@@ -359,6 +372,16 @@ function quoteGitCommand(args: ReadonlyArray<string>): string {
359372 return `git ${ args . join ( " " ) } ` ;
360373}
361374
375+ function isMissingGitCwdError ( error : GitCommandError ) : boolean {
376+ const normalized = `${ error . detail } \n${ error . message } ` . toLowerCase ( ) ;
377+ return (
378+ normalized . includes ( "no such file or directory" ) ||
379+ normalized . includes ( "notfound: filesystem.access" ) ||
380+ normalized . includes ( "enoent" ) ||
381+ normalized . includes ( "not a directory" )
382+ ) ;
383+ }
384+
362385function toGitCommandError (
363386 input : Pick < ExecuteGitInput , "operation" | "cwd" | "args" > ,
364387 detail : string ,
@@ -1190,7 +1213,11 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {
11901213 {
11911214 allowNonZeroExit : true ,
11921215 } ,
1193- ) ;
1216+ ) . pipe ( Effect . catchIf ( isMissingGitCwdError , ( ) => Effect . succeed ( null ) ) ) ;
1217+
1218+ if ( statusResult === null ) {
1219+ return NON_REPOSITORY_STATUS_DETAILS ;
1220+ }
11941221
11951222 if ( statusResult . code !== 0 ) {
11961223 const stderr = statusResult . stderr . trim ( ) ;
@@ -1322,7 +1349,10 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {
13221349 ) ;
13231350
13241351 const statusDetails : GitCoreShape [ "statusDetails" ] = Effect . fn ( "statusDetails" ) ( function * ( cwd ) {
1325- yield * refreshStatusUpstreamIfStale ( cwd ) . pipe ( Effect . ignoreCause ( { log : true } ) ) ;
1352+ yield * refreshStatusUpstreamIfStale ( cwd ) . pipe (
1353+ Effect . catchIf ( isMissingGitCwdError , ( ) => Effect . void ) ,
1354+ Effect . ignoreCause ( { log : true } ) ,
1355+ ) ;
13261356 return yield * readStatusDetailsLocal ( cwd ) ;
13271357 } ) ;
13281358
@@ -1719,6 +1749,16 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {
17191749 timeoutMs : 10_000 ,
17201750 allowNonZeroExit : true ,
17211751 } ,
1752+ ) . pipe (
1753+ Effect . catchIf ( isMissingGitCwdError , ( ) =>
1754+ Effect . succeed ( {
1755+ code : 128 ,
1756+ stdout : "" ,
1757+ stderr : "fatal: not a git repository" ,
1758+ stdoutTruncated : false ,
1759+ stderrTruncated : false ,
1760+ } ) ,
1761+ ) ,
17221762 ) ;
17231763
17241764 if ( localBranchResult . code !== 0 ) {
0 commit comments