Skip to content

fix: handle restack when branches are checked out in other worktrees#672

Draft
STRRL wants to merge 1 commit intoaviator-co:masterfrom
STRRL:fix/671-worktree-restack
Draft

fix: handle restack when branches are checked out in other worktrees#672
STRRL wants to merge 1 commit intoaviator-co:masterfrom
STRRL:fix/671-worktree-restack

Conversation

@STRRL
Copy link

@STRRL STRRL commented Mar 7, 2026

Fixes #671

Problem

av stack restack fails when stacked branches are checked out in different worktrees:

error: failed to checkout branch "branch-name": fatal: 'branch-name' is already used by worktree at '/path/to/worktree'

During restack, av tries to git checkout each branch to rebase it. Git refuses because the branch is already checked out in another worktree.

Fix

  • Added WorktreeForBranch() to detect if a branch is checked out in a worktree (via git worktree list --porcelain)
  • Modified RebaseParse() to run the rebase from the worktree directory when the target branch is in a different worktree, instead of attempting a checkout
  • Added runInDir() helper to temporarily run git commands from a different directory

Tests

  • TestWorktreeForBranch — verifies detection of branches in worktrees vs main repo
  • TestRebaseInWorktree — verifies rebase succeeds when branch is in another worktree

Note: This fix was developed with AI assistance. I will review the code personally before marking ready for review.

…viator-co#671)

When restacking, git rebase fails if the target branch is checked out
in another worktree. Detect this case and run the rebase from the
worktree directory where the branch is already checked out, omitting
the branch argument so git doesn't attempt a checkout.
@aviator-app
Copy link
Contributor

aviator-app bot commented Mar 7, 2026

Current Aviator status

Aviator will automatically update this comment as the status of the PR changes.
Comment /aviator refresh to force Aviator to re-examine your PR (or learn about other /aviator commands).

This pull request is currently open (not queued).

How to merge

To merge this PR, comment /aviator merge or add the mergequeue label.


See the real-time status of this PR on the Aviator webapp.
Use the Aviator Chrome Extension to see the status of your PR within GitHub.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves an issue where av stack restack would fail if branches in the stack were checked out in separate Git worktrees. The solution involves intelligently detecting if a branch resides in a worktree and, if so, performing the rebase operation directly within that worktree's context, thereby preventing Git checkout conflicts and ensuring smooth restacking across worktrees.

Highlights

  • Problem Addressed: The av stack restack command previously failed when stacked branches were checked out in different Git worktrees, due to Git's refusal to checkout a branch already in use by another worktree.
  • Worktree Detection: A new WorktreeForBranch() function was introduced to detect if a branch is checked out in a separate worktree by parsing git worktree list --porcelain output.
  • Worktree-Aware Rebase: The RebaseParse() function was modified to execute the rebase operation from the correct worktree directory when the target branch is found in a different worktree, bypassing the need for a git checkout.
  • Helper Function: A runInDir() helper was added to temporarily change the execution directory for Git commands, facilitating worktree-specific operations.
  • New Tests: Dedicated tests, TestWorktreeForBranch and TestRebaseInWorktree, were added to ensure the correct detection of branches in worktrees and the successful execution of rebase operations within them.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • internal/git/git.go
    • Added runInDir method to Repo for executing Git commands in a specified directory.
  • internal/git/rebase.go
    • Modified Rebase function to check for branches in other worktrees and execute rebase from the worktree's directory if found.
  • internal/git/worktree.go
    • Added new file worktree.go containing the WorktreeForBranch function to determine if a branch is checked out in a Git worktree.
  • internal/git/worktree_test.go
    • Added TestWorktreeForBranch to verify worktree detection logic.
    • Added TestRebaseInWorktree to confirm rebase functionality when branches are in separate worktrees.
Activity
  • The fix was developed with AI assistance.
  • The author plans to personally review the code before marking it ready for review.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses the issue of av stack restack failing when branches are checked out in different worktrees. The solution to detect the worktree and execute the rebase command from the correct directory is well-implemented. The addition of tests for this new functionality is also a great inclusion. I've identified a potential race condition in the new runInDir helper function and a few areas where error handling could be made more robust. My detailed feedback is in the comments below.

Comment on lines +189 to +192
saved := r.repoDir
r.repoDir = dir
defer func() { r.repoDir = saved }()
return r.Run(ctx, opts)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This implementation of runInDir is not safe for concurrent use. It mutates the repoDir field of the Repo struct, which can lead to race conditions if other goroutines are using the same Repo instance. A safer approach is to create a shallow copy of the Repo object and modify its repoDir for the scope of this call, avoiding mutation of the original object.

Suggested change
saved := r.repoDir
r.repoDir = dir
defer func() { r.repoDir = saved }()
return r.Run(ctx, opts)
rCopy := *r
rCopy.repoDir = dir
return (&rCopy).Run(ctx, opts)

return "", err
}

repoDir, _ := filepath.EvalSymlinks(r.repoDir)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error returned by filepath.EvalSymlinks is ignored. If resolving the symlink fails, repoDir will contain the original, unresolved path. This could lead to incorrect comparisons later on, especially on systems like macOS where paths like /var are symlinks to /private/var. The error should be handled to ensure correctness.

	repoDir, err := filepath.EvalSymlinks(r.repoDir)
	if err != nil {
		return "", err
	}

if ref, ok := strings.CutPrefix(line, "branch "); ok {
shortName := strings.TrimPrefix(ref, "refs/heads/")
if shortName == branch {
resolved, _ := filepath.EvalSymlinks(currentWorktree)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the earlier comment, the error from filepath.EvalSymlinks is ignored here. This can lead to incorrect behavior if the worktree path cannot be resolved. The error should be handled to ensure the comparison against the main repository directory is reliable.

				resolved, err := filepath.EvalSymlinks(currentWorktree)
				if err != nil {
					return "", err
				}

Comment on lines +35 to +36
resolvedWt, _ := filepath.EvalSymlinks(wt)
resolvedExpected, _ := filepath.EvalSymlinks(wtPath)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Errors from filepath.EvalSymlinks are being ignored. In tests, it's important to check for errors to ensure the test setup is correct and the function is being tested under the right conditions. If EvalSymlinks were to fail, this test could produce misleading results. Please handle the errors, for example by using require.NoError(t, err).

Suggested change
resolvedWt, _ := filepath.EvalSymlinks(wt)
resolvedExpected, _ := filepath.EvalSymlinks(wtPath)
resolvedWt, err := filepath.EvalSymlinks(wt)
require.NoError(t, err)
resolvedExpected, err := filepath.EvalSymlinks(wtPath)
require.NoError(t, err)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: av stack restack fails when stacked branches are checked out in different worktrees

1 participant