From 629bf40d832f7eba6a35eae91c9ada2505d0b2cf Mon Sep 17 00:00:00 2001 From: herjarsa Date: Mon, 10 Aug 2026 00:20:12 +0200 Subject: [PATCH] fix(git): silence getWorktrees warning when directory is not a repo `getWorktrees` logs a warn-level line every time the managed OpenCode process or any other caller passes a directory that is not inside a git repository. The OpenChamber desktop main.log fills with hundreds of these "Failed to list worktrees, returning empty list: fatal: not a git repository ..." entries over a normal session. The empty-list fallback is already correct (worktrees are an optional feature), but the warning is noise that hides real git failures. Use the existing `isNotGitRepositoryError` helper to suppress the warn specifically for the "not a git repository" case and keep the warning for genuine failures (lock contention, permission errors, corrupt repos, etc.). --- packages/web/server/lib/git/service.js | 10 ++++- packages/web/server/lib/git/service.test.js | 44 ++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/web/server/lib/git/service.js b/packages/web/server/lib/git/service.js index 04baa715..2dd6609c 100644 --- a/packages/web/server/lib/git/service.js +++ b/packages/web/server/lib/git/service.js @@ -3738,7 +3738,15 @@ export async function getWorktrees(directory) { path: entry.worktree, })); } catch (error) { - console.warn('Failed to list worktrees, returning empty list:', error?.message || error); + // Worktrees are an optional feature. When the caller passes a directory + // that is not inside any git repository (for example, the managed + // OpenCode's working directory or an unconfigured project path), git + // exits with "fatal: not a git repository ...". Treat that as an + // authoritative empty result so the route handler can still respond + // 200 [] and the desktop main.log stays free of noise. + if (!isNotGitRepositoryError(error)) { + console.warn('Failed to list worktrees, returning empty list:', error?.message || error); + } return []; } } diff --git a/packages/web/server/lib/git/service.test.js b/packages/web/server/lib/git/service.test.js index 3127fe92..827bdb6d 100644 --- a/packages/web/server/lib/git/service.test.js +++ b/packages/web/server/lib/git/service.test.js @@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { afterEach, describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import simpleGit from 'simple-git'; import { @@ -13,6 +13,7 @@ import { getBranches, getRangeDiff, getStatus, + getWorktrees, isGitRepository, populateWorktreeWithLockRecovery, removeWorktree, @@ -460,6 +461,47 @@ describe('worktree root resolution', () => { }); }); +// --------------------------------------------------------------------------- +// getWorktrees +// --------------------------------------------------------------------------- + +describe('getWorktrees', () => { + if (!canRunGit()) { + it.skip('git binary not available', () => {}); + return; + } + + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + + afterEach(() => { + warnSpy.mockClear(); + }); + + it('returns an empty list for a non-git directory without warning', async () => { + const nonGit = createTempDir(); + + const result = await getWorktrees(nonGit); + + expect(result).toEqual([]); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('returns the worktrees for a real git repository', async () => { + const repo = createTempDir(); + runGit(repo, ['init', '-b', 'main']); + runGit(repo, ['config', 'user.email', 'test@example.com']); + runGit(repo, ['config', 'user.name', 'Test User']); + fs.writeFileSync(path.join(repo, 'README.md'), '# Test\n'); + runGit(repo, ['add', 'README.md']); + runGit(repo, ['commit', '-m', 'init']); + + const result = await getWorktrees(repo); + + expect(Array.isArray(result)).toBe(true); + expect(warnSpy).not.toHaveBeenCalled(); + }); +}); + // --------------------------------------------------------------------------- // createWorktree // ---------------------------------------------------------------------------