From d4bd0e0f61cfe15ffd8696dc86bcbdee1e1e30a6 Mon Sep 17 00:00:00 2001 From: "A.K.M. Adib" Date: Mon, 4 May 2026 15:21:50 -0400 Subject: [PATCH] feat(core): allow reusing existing worktrees with --worktree flag --- docs/cli/git-worktrees.md | 19 +++++++++++++++++-- .../core/src/services/worktreeService.test.ts | 13 ++++++++++++- packages/core/src/services/worktreeService.ts | 9 +++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/docs/cli/git-worktrees.md b/docs/cli/git-worktrees.md index 5020b3fa9a..47526778bb 100644 --- a/docs/cli/git-worktrees.md +++ b/docs/cli/git-worktrees.md @@ -80,8 +80,23 @@ manually remove the worktree if you no longer need it. ## Resuming work in a Git worktree -To resume a session in a worktree, navigate to the worktree directory and start -Gemini CLI with the `--resume` flag and the session ID: +To resume a session in an existing worktree, you can simply run Gemini with the +same `--worktree` (`-w`) name: + +```bash +gemini --worktree feature-search +``` + +Alternatively, you can navigate to the worktree directory and start Gemini +directly: + +```bash +cd .gemini/worktrees/feature-search +gemini +``` + +To resume a specific previous chat session within that worktree, use the +`--resume` flag and the session ID: ```bash cd .gemini/worktrees/feature-search diff --git a/packages/core/src/services/worktreeService.test.ts b/packages/core/src/services/worktreeService.test.ts index 3345fcb268..1d379e9bd0 100644 --- a/packages/core/src/services/worktreeService.test.ts +++ b/packages/core/src/services/worktreeService.test.ts @@ -85,7 +85,8 @@ describe('worktree utilities', () => { }); describe('createWorktree', () => { - it('should execute git worktree add with correct branch and path', async () => { + it('should execute git worktree add with correct branch and path if it does not exist', async () => { + vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT')); vi.mocked(execa).mockResolvedValue({ stdout: '' } as never); const resultPath = await createWorktree(projectRoot, worktreeName); @@ -98,7 +99,17 @@ describe('worktree utilities', () => { ); }); + it('should return existing path and not call git if it already exists', async () => { + vi.mocked(fs.access).mockResolvedValue(undefined); + + const resultPath = await createWorktree(projectRoot, worktreeName); + + expect(resultPath).toBe(expectedPath); + expect(execa).not.toHaveBeenCalled(); + }); + it('should throw an error if git worktree add fails', async () => { + vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT')); vi.mocked(execa).mockRejectedValue(new Error('git failed')); await expect(createWorktree(projectRoot, worktreeName)).rejects.toThrow( diff --git a/packages/core/src/services/worktreeService.ts b/packages/core/src/services/worktreeService.ts index 0b6bd20648..5873329c58 100644 --- a/packages/core/src/services/worktreeService.ts +++ b/packages/core/src/services/worktreeService.ts @@ -120,6 +120,15 @@ export async function createWorktree( name: string, ): Promise { const worktreePath = getWorktreePath(projectRoot, name); + + try { + await fs.access(worktreePath); + // Worktree path already exists, reuse it + return worktreePath; + } catch { + // Does not exist, proceed to create + } + const branchName = `worktree-${name}`; await execa('git', ['worktree', 'add', worktreePath, '-b', branchName], {