diff --git a/apps/server/src/git/Layers/GitCore.test.ts b/apps/server/src/git/Layers/GitCore.test.ts index 2667c7aaa1732d41ee5e8127c33322d0573eb1da..5a8cf2cc1dd5962224a74abf0ac394c55cb4b812 100644 --- a/apps/server/src/git/Layers/GitCore.test.ts +++ b/apps/server/src/git/Layers/GitCore.test.ts @@ -1295,5 +1295,54 @@ it.layer(TestLayer)("git integration", (it) => { expect(didFailRecency).toBe(true); }), ); + + it.effect("falls back to empty remote branch data when remote lookups fail", () => + Effect.gen(function* () { + const tmp = yield* makeTmpDir(); + const remote = yield* makeTmpDir(); + yield* initRepoWithCommit(tmp); + yield* git(remote, ["init", "--bare"]); + yield* git(tmp, ["remote", "add", "origin", remote]); + + const realGitService = yield* GitService; + let didFailRemoteBranches = false; + let didFailRemoteNames = false; + const core = yield* makeIsolatedGitCore({ + execute: (input) => { + if (input.args.join(" ") === "branch --no-color --remotes") { + didFailRemoteBranches = true; + return Effect.fail( + new GitCommandError({ + operation: "git.test.listBranchesRemoteBranches", + command: `git ${input.args.join(" ")}`, + cwd: input.cwd, + detail: "remote unavailable", + }), + ); + } + if (input.args.join(" ") === "remote") { + didFailRemoteNames = true; + return Effect.fail( + new GitCommandError({ + operation: "git.test.listBranchesRemoteNames", + command: `git ${input.args.join(" ")}`, + cwd: input.cwd, + detail: "remote unavailable", + }), + ); + } + return realGitService.execute(input); + }, + }); + + const result = yield* core.listBranches({ cwd: tmp }); + + expect(result.isRepo).toBe(true); + expect(result.branches.length).toBeGreaterThan(0); + expect(result.branches.every((branch) => !branch.isRemote)).toBe(true); + expect(didFailRemoteBranches).toBe(true); + expect(didFailRemoteNames).toBe(true); + }), + ); }); }); diff --git a/apps/server/src/git/Layers/GitCore.ts b/apps/server/src/git/Layers/GitCore.ts index d3b7fc567bec80dbf64d8668cb2cff195f5532fe..78a2d94ddd2da785a94d3bcb01c2b06c6b425589 100644 --- a/apps/server/src/git/Layers/GitCore.ts +++ b/apps/server/src/git/Layers/GitCore.ts @@ -664,6 +664,38 @@ const makeGitCore = Effect.gen(function* () { ); } + const remoteBranchResultEffect = executeGit( + "GitCore.listBranches.remoteBranches", + input.cwd, + ["branch", "--no-color", "--remotes"], + { + timeoutMs: 10_000, + allowNonZeroExit: true, + }, + ).pipe( + Effect.catch((error) => + Effect.logWarning( + `GitCore.listBranches: remote branch lookup failed for ${input.cwd}: ${error.message}. Falling back to an empty remote branch list.`, + ).pipe(Effect.as({ code: 1, stdout: "", stderr: "" })), + ), + ); + + const remoteNamesResultEffect = executeGit( + "GitCore.listBranches.remoteNames", + input.cwd, + ["remote"], + { + timeoutMs: 5_000, + allowNonZeroExit: true, + }, + ).pipe( + Effect.catch((error) => + Effect.logWarning( + `GitCore.listBranches: remote name lookup failed for ${input.cwd}: ${error.message}. Falling back to an empty remote name list.`, + ).pipe(Effect.as({ code: 1, stdout: "", stderr: "" })), + ), + ); + const [defaultRef, worktreeList, remoteBranchResult, remoteNamesResult, branchLastCommit] = yield* Effect.all( [ @@ -685,25 +717,24 @@ const makeGitCore = Effect.gen(function* () { allowNonZeroExit: true, }, ), - executeGit( - "GitCore.listBranches.remoteBranches", - input.cwd, - ["branch", "--no-color", "--remotes"], - { - timeoutMs: 10_000, - allowNonZeroExit: true, - }, - ), - executeGit("GitCore.listBranches.remoteNames", input.cwd, ["remote"], { - timeoutMs: 5_000, - allowNonZeroExit: true, - }), + remoteBranchResultEffect, + remoteNamesResultEffect, branchRecencyPromise, ], { concurrency: "unbounded" }, ); const remoteNames = remoteNamesResult.code === 0 ? parseRemoteNames(remoteNamesResult.stdout) : []; + if (remoteBranchResult.code !== 0 && remoteBranchResult.stderr.trim().length > 0) { + yield* Effect.logWarning( + `GitCore.listBranches: remote branch lookup returned code ${remoteBranchResult.code} for ${input.cwd}: ${remoteBranchResult.stderr.trim()}. Falling back to an empty remote branch list.`, + ); + } + if (remoteNamesResult.code !== 0 && remoteNamesResult.stderr.trim().length > 0) { + yield* Effect.logWarning( + `GitCore.listBranches: remote name lookup returned code ${remoteNamesResult.code} for ${input.cwd}: ${remoteNamesResult.stderr.trim()}. Falling back to an empty remote name list.`, + ); + } const defaultBranch = defaultRef.code === 0