@@ -27,6 +27,7 @@ import {
2727 type GitCoreShape ,
2828 type ExecuteGitInput ,
2929 type ExecuteGitResult ,
30+ type GitStatusDetails ,
3031} from "../Services/GitCore.ts" ;
3132import { ServerConfig } from "../../config.ts" ;
3233import { decodeJsonResult } from "@okcode/shared/schemaJson" ;
@@ -121,6 +122,23 @@ function parsePorcelainConflictPath(line: string): string | null {
121122 return parsePorcelainPath ( line ) ;
122123}
123124
125+ const EMPTY_STATUS_DETAILS = {
126+ branch : null ,
127+ hasWorkingTreeChanges : false ,
128+ hasConflicts : false ,
129+ conflictedFiles : [ ] ,
130+ workingTree : {
131+ files : [ ] ,
132+ insertions : 0 ,
133+ deletions : 0 ,
134+ } ,
135+ hasUpstream : false ,
136+ aheadCount : 0 ,
137+ behindCount : 0 ,
138+ upstreamRef : null ,
139+ pr : null ,
140+ } satisfies GitStatusDetails ;
141+
124142function parseBranchLine ( line : string ) : { name : string ; current : boolean } | null {
125143 const trimmed = line . trim ( ) ;
126144 if ( trimmed . length === 0 ) return null ;
@@ -1042,15 +1060,34 @@ export const makeGitCore = (options?: { executeOverride?: GitCoreShape["execute"
10421060
10431061 const statusDetails : GitCoreShape [ "statusDetails" ] = ( cwd ) =>
10441062 Effect . gen ( function * ( ) {
1063+ const cwdStat = yield * fileSystem . stat ( cwd ) . pipe ( Effect . catch ( ( ) => Effect . succeed ( null ) ) ) ;
1064+ if ( ! cwdStat || cwdStat . type !== "Directory" ) {
1065+ return EMPTY_STATUS_DETAILS ;
1066+ }
1067+
10451068 yield * refreshStatusUpstreamIfStale ( cwd ) . pipe ( Effect . ignoreCause ( { log : true } ) ) ;
10461069
1047- const [ statusStdout , unstagedNumstatStdout , stagedNumstatStdout ] = yield * Effect . all (
1070+ const status = yield * executeGit (
1071+ "GitCore.statusDetails.status" ,
1072+ cwd ,
1073+ [ "status" , "--porcelain=2" , "--branch" ] ,
1074+ { allowNonZeroExit : true } ,
1075+ ) ;
1076+ if ( status . code !== 0 ) {
1077+ const stderr = status . stderr . trim ( ) ;
1078+ if ( stderr . toLowerCase ( ) . includes ( "not a git repository" ) ) {
1079+ return EMPTY_STATUS_DETAILS ;
1080+ }
1081+ return yield * createGitCommandError (
1082+ "GitCore.statusDetails.status" ,
1083+ cwd ,
1084+ [ "status" , "--porcelain=2" , "--branch" ] ,
1085+ stderr . length > 0 ? stderr : `git status failed with code ${ status . code } .` ,
1086+ ) ;
1087+ }
1088+
1089+ const [ unstagedNumstatStdout , stagedNumstatStdout ] = yield * Effect . all (
10481090 [
1049- runGitStdout ( "GitCore.statusDetails.status" , cwd , [
1050- "status" ,
1051- "--porcelain=2" ,
1052- "--branch" ,
1053- ] ) ,
10541091 runGitStdout ( "GitCore.statusDetails.unstagedNumstat" , cwd , [ "diff" , "--numstat" ] ) ,
10551092 runGitStdout ( "GitCore.statusDetails.stagedNumstat" , cwd , [
10561093 "diff" ,
@@ -1061,6 +1098,8 @@ export const makeGitCore = (options?: { executeOverride?: GitCoreShape["execute"
10611098 { concurrency : "unbounded" } ,
10621099 ) ;
10631100
1101+ const statusStdout = status . stdout ;
1102+
10641103 let branch : string | null = null ;
10651104 let upstreamRef : string | null = null ;
10661105 let aheadCount = 0 ;
0 commit comments