feat: support rebasing branches checked out in worktrees#145
Conversation
| if not os.path.isabs(meta_dir): | ||
| meta_dir = os.path.join(worktree_path, meta_dir) | ||
| meta_dir = os.path.realpath(meta_dir) | ||
| for subdir in ('rebase-merge', 'rebase-apply'): |
There was a problem hiding this comment.
Should we also handle cherry-pick/merge/bisect here? What do you think?
| return ref[len('refs/heads/'):] | ||
| return None | ||
|
|
||
| def _rebase_in_worktree(self, branch, target, worktree_path, |
There was a problem hiding this comment.
Could we reuse GitWrapper.rebase and GitWrapper.stasher here? It seems like they implement the same logic if I understand correctly
|
Thanks for this PR! I've left two clarifying questions as comments. Also, your commits seem to change all files.pythonhosted.org in |
|
Oops sorry about that. I'll get those fixed |
When a branch is checked out in a separate git worktree, `git checkout` fails with 'already used by worktree'. Instead of checking out such branches in the main repo, detect them via `git worktree list --porcelain` and perform the rebase (or fast-forward merge) directly in the worktree directory. This handles: - Fast-forward: runs `git merge --ff-only` in the worktree - Rebase: stashes worktree changes if dirty, rebases, then unstashes Fixes the error: fatal: '<branch>' is already used by worktree at '<path>' Co-Authored-By: Oz <oz-agent@warp.dev>
When a linked worktree is in the middle of a rebase, git reports it as 'detached' in `git worktree list --porcelain` rather than pointing to the branch ref. This caused `git checkout <branch>` to fail with exit code 128 since git still considers the branch locked to that worktree. Now detached worktrees are inspected for rebase state via rebase-merge/head-name and rebase-apply/head-name, and any branch found there is reported as 'rebase in progress' and skipped. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename mid_rebase_branches -> in_progress_branches; skip worktrees mid-cherry-pick, -merge, and -bisect in addition to mid-rebase - Extract _get_worktree_meta_dir() helper to eliminate duplicated .git pointer-file parsing - Add _worktree_has_in_progress_op() to detect cherry-pick/merge/bisect - Add suppress_pop to stasher() so the stash is not popped when a rebase fails with conflicts - Simplify _rebase_in_worktree() to use GitWrapper.stasher() and GitWrapper.rebase() instead of duplicating that logic
e7f417f to
7e80e39
Compare
|
Rebased and addressed both comments! For cherry-pick/merge/bisect — added For reusing stasher/rebase — needed to add a Also fixed the uv.lock — that was my local Netflix pip mirror leaking in, sorry about that. |
Problem
When a branch is checked out in a separate git worktree,
git upfails with:This happens because
rebase_all_branches()tries togit checkoutthe branch, which git refuses since it's already checked out in another worktree.Solution
Before attempting checkout, build a map of branches to their worktree paths via
git worktree list --porcelain. For branches found in worktrees:git merge --ff-onlydirectly in the worktreeThis matches the existing behavior for non-worktree branches — failed rebases are left for the user to resolve.
Changes
gitup.py: Added_build_worktree_map()and_rebase_in_worktree()methods; modifiedrebase_all_branches()to dispatch worktree branches accordinglytest_worktree.py: Tests both fast-forward and rebase scenarios with a worktree branchTesting
All 37 tests pass (36 existing + 1 new).
Warp conversation
Co-Authored-By: Oz oz-agent@warp.dev