Skip to content

Feat: add interactive git worktree operations#402

Merged
wfxr merged 3 commits intowfxr:mainfrom
suft:worktrees
Mar 10, 2026
Merged

Feat: add interactive git worktree operations#402
wfxr merged 3 commits intowfxr:mainfrom
suft:worktrees

Conversation

@suft
Copy link
Contributor

@suft suft commented Nov 3, 2024

Check list

  • I have performed a self-review of my code
  • I have commented my code in hard-to-understand areas
  • I have made corresponding changes to the documentation

Description

I recently adopted using multiple fixed worktrees as part of my workflow to help with productivity.
Each worktree is used for a different type of concurrent activity:

  • main for looking at the pristine code
  • work for looking at my code
  • review for looking at someone else’s code
  • background for my computer to look at my code
    • operates in the detached head state
  • scratch for everything else
. (bare repo)
├── background/ (* worktree)
├── config
├── description
├── HEAD
├── hooks/
├── info/
├── logs/
├── main/ (* worktree)
├── objects/
├── packed-refs
├── refs/
├── review/ (* worktree)
├── rr-cache/
├── scratch/ (*worktree)
├── work/(* worktree)
└── worktrees/

Example of worktrees in a bare clone
Screenshot 2024-11-13 at 7 29 51 PM

Another approach is to use worktrees as a replacement of, or a supplement to git branches. Instead of switching branches, you just change directories. So that would involve creating a new worktree and branch, then delete the worktree upon merging.

Worktree Operations (should we stick with these abbreviations?)

  • locking a worktree - gwl
  • unlocking a worktree - gwu
  • removing a worktree - gwr
  • jumping to a worktree - gwj
    • similar to switching branches
    • involves a combination of selecting a result of git worktree list and cd into that result (not a native git operation)

Screenshots

bash (repo with short history) - gwj
FORGIT_WORKTREE_PREVIEW_GIT_OPTS='--oneline --graph --decorate --color'
Screenshot 2024-11-04 at 8 24 20 PM

fish (repo with mid-length history) - gwj
FORGIT_WORKTREE_PREVIEW_GIT_OPTS='--oneline --graph --decorate --color'
Screenshot 2024-11-10 at 11 41 59 AM

bash (repo with mid-length history) - gwj
FORGIT_WORKTREE_PREVIEW_GIT_OPTS='--oneline --graph --decorate --color --max-count=100'
Screenshot 2024-11-10 at 12 46 05 PM

Closes #399.

Type of change

  • Bug fix
  • New feature
  • Refactor
  • Breaking change
  • Documentation change

Test environment

  • Shell
    • bash
    • zsh
    • fish
  • OS
    • Linux
    • Mac OS X
    • Windows
    • Others:

@carlfriedrich
Copy link
Collaborator

@suft Thanks for your contribution! Is this ready for review or did you make it a draft on purpose?

@suft
Copy link
Contributor Author

suft commented Nov 4, 2024

@carlfriedrich I made it draft on purpose. Still have a few things to adjust.

bin/git-forgit Outdated
[[ "$sha" == "(bare)" ]] && return
# the trailing '--' ensures that this works for branches that have a name
# that is identical to a file
git log "$sha" "${_forgit_log_preview_options[@]}" --
Copy link
Contributor Author

@suft suft Nov 10, 2024

Choose a reason for hiding this comment

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

  • I should probably add preview options FORGIT_WORKTREE_PREVIEW_GIT_OPTS (like Add *_PREVIEW_GIT_OPTS variables #396)
  • I've noticed this can run a bit slow (on some computers) if you have a repo with a long history because it attempts to get the entire history for the branch checked out in that worktree
    • It would be good to see what others think when they test this out
  • Would be quicker if I limit the log entries in the worktree preview options

@suft suft force-pushed the worktrees branch 3 times, most recently from cae816c to 525441f Compare November 10, 2024 19:44
@suft suft marked this pull request as ready for review November 10, 2024 19:45
Copy link
Collaborator

@sandr01d sandr01d left a comment

Choose a reason for hiding this comment

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

Thanks for you work @suft! I think this is going to be a nice improvement. There are a few things that need to be adjusted. In some of my comments I've pinged the other maintainers for their opinions. Please hold back on implementing those, as those comments are opinionated and I'd like to have feedback from the others first before deciding in which direction to go.

@suft suft requested a review from sandr01d November 14, 2024 00:32
@carlfriedrich
Copy link
Collaborator

@suft Great implementation so far, @sandr01d great review. This will be a good addition to forgit.

Copy link
Collaborator

@sandr01d sandr01d left a comment

Choose a reason for hiding this comment

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

Thanks for the changes @suft, looks good to me so far. Let me summarize the things that still need to be addressed:

  • The default preview should look the same as for other commands that use git log (#402 (comment))
  • _forgit_worktree_jump does not change the directory when forgit is used as a git subcommand (#402 (comment))
  • The root worktree should not be selectable with gwl. We can make it non-interactable with --header-lines=1 and also bring this back in the other functions that you used this with. Don't mind my earlier comment regarding this. (#402 (comment))
  • We should add completions for the new commands (#402 (comment))
  • We should allow passing additional arguments to the underlying git commands (#402 (comment)). The logic for detecting whether the git command should be executed immediately without using fzf needs to be adjusted to do so. Instead of testing if any arguments were passed, we need to test whether non flag arguments were passed. You can use _forgit_contains_non_flags to do so.

@cjappl
Copy link
Collaborator

cjappl commented Nov 15, 2024

(it looks like there is very thorough reviewing going on here, please ping me if you need another pair of eyes, otherwise I completely defer to @sandr01d and @carlfriedrich )

@suft suft marked this pull request as draft November 18, 2024 00:09
@suft suft marked this pull request as ready for review December 18, 2024 19:08
@suft suft requested a review from sandr01d December 18, 2024 19:08
@suft suft marked this pull request as draft December 18, 2024 19:24
@suft suft marked this pull request as ready for review December 18, 2024 19:50
@suft
Copy link
Contributor Author

suft commented Dec 18, 2024

Thanks for the changes @suft, looks good to me so far. Let me summarize the things that still need to be addressed:

The root worktree doesn't show up as the first selection, so --header-lines=1 will affect a different worktree (which I'm guessing is not the behaviour we want), unless we reverse the fzf item list.

Screenshot 2024-12-18 at 3 02 14 PM

@suft suft changed the title feat: add interactive git worktree operations Feat: add interactive git worktree operations Dec 18, 2024
@wfxr wfxr force-pushed the worktrees branch 2 times, most recently from f5c8d6c to 3bfa1d2 Compare February 6, 2026 10:17
@wfxr
Copy link
Owner

wfxr commented Feb 6, 2026

Thanks again to @suft for the original work and idea on this PR! I've taken over and done a reimplementation based on the original proposal and the review feedback from @sandr01d and @carlfriedrich. Also thanks to @iloveitaly for providing the link, which helped improve the display format of the worktree list.

First — my sincere apologies, @suft. I accidentally force-pushed to your branch, which overwrote your original commits. I'm really sorry about that. Hopefully you still have a local copy of your original implementation. Again, very sorry for the mishap.

Summary of the current implementation

Two new commands:

Alias Command Description
gwt forgit worktree Interactive worktree browser — select a worktree and cd into it
gwd forgit worktree_delete Interactive git worktree remove selector (multi-select)

Worktree browser features

  • Rich display format: [XY] /path/to/worktree (branch) 3 hours ago
    • X: * = current worktree, = other
    • Y: L (yellow) = locked, P (yellow) = prunable, = normal
  • Preview pane: git status -s + git log --oneline for the selected worktree
  • Keybindings:
    • Ctrl-Y — copy worktree path to clipboard
    • Alt-L — toggle lock/unlock (with live reload)
  • Bare repo support
  • Passthrough: gwt <args> forwards arguments directly to git worktree

Also included

  • Shell completions for bash, zsh, and fish
  • Refactored _forgit_branch_list / _forgit_extract_branch_name helpers, replacing the fragile LC_ALL=C sort -k1.1,1.1 -rs pattern in checkout_branch, switch_branch, branch_delete, and cherry_pick_from_branch
image

Differences from the original proposal

Aspect Original proposal Current implementation
Jump to worktree gwj — dedicated jump command gwt — browse + jump as a single command
Lock worktree gwl — standalone command Alt-L keybinding inside the worktree browser (toggle)
Unlock worktree gwu — standalone command Same Alt-L toggle (no separate command)
Remove worktree gwr gwd (renamed to worktree_delete for consistency with branch_delete)
Preview git log with FORGIT_WORKTREE_PREVIEW_GIT_OPTS git status -s + git log --oneline; customizable via FORGIT_WORKTREE_FZF_OPTS
Main worktree Reviewers requested --header-lines=1 to make it non-selectable Current worktree listed first, remains selectable (needed for lock/unlock); delete excludes main worktree

The main design shift: instead of multiple standalone commands (lock/unlock/jump/remove), the current approach uses a single browser with keybindings for in-place operations + a separate delete command, keeping the alias namespace smaller.

I've done a thorough self-review already, but given the scope of this PR — new feature + refactoring of existing branch-related code — I'd appreciate another round of review. @cjappl @carlfriedrich @sandr01d Would you mind taking a look when you have time? And @suft, your input would also be very valuable given all the thought you put into the original design!

This comment was marked as resolved.

@wfxr wfxr marked this pull request as ready for review February 6, 2026 10:57
@sandr01d
Copy link
Collaborator

Hey @wfxr, could you rearrange the commits in this MR, so we have one commit for each refactoring and one for the new workspace commands? This would make reviewing a lot easier for me.

@carlfriedrich
Copy link
Collaborator

@wfxr Thanks a lot for pushing this forward and your work on this! I like that you combined multiple commands into one and make use of keybindings instead (that goes into the direction I proposed in #259 some time ago).

I did a quick test of your branch and it seems to works as expected.

For a useful code review I am going with @sandr01d: given the huge size of the PR it would be really helpful to have a clean history of atomic commits on this branch.

@wfxr wfxr force-pushed the worktrees branch 2 times, most recently from cb7a084 to b7382d9 Compare February 27, 2026 12:42
@wfxr
Copy link
Owner

wfxr commented Feb 27, 2026

Thanks @sandr01d @carlfriedrich for the review feedback! I've reorganized the branch into two atomic commits:

This should make reviewing much easier. PTAL when you get a chance!

@wfxr
Copy link
Owner

wfxr commented Mar 7, 2026

Hi @sandr01d @carlfriedrich, I'd like to move forward with this PR. I've thoroughly reviewed the implementation myself and also had it reviewed with AI assistance. Additionally, I've been using this feature in my daily workflow for quite a while now without encountering any issues.

If there's no review feedback by Monday, I'll go ahead and merge this. That said, comments and suggestions are always welcome — feel free to leave them anytime, even after it's merged, and I'll address them in follow-up commits.

@sandr01d
Copy link
Collaborator

sandr01d commented Mar 8, 2026

First — my sincere apologies, @suft. I accidentally force-pushed to your branch, which overwrote your original commits. I'm really sorry about that. Hopefully you still have a local copy of your original implementation. Again, very sorry for the mishap.

Just found out that I still had the original commits on my machine, so I pushed them here.

Copy link
Collaborator

@sandr01d sandr01d left a comment

Choose a reason for hiding this comment

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

Sorry for the delay @wfxr, I was very busy in the last weeks and couldn't find the time for a review.

First of all, thank you both for your work @wfxr and @suft, I think this is coming along really nicely. I really like the refactoring you did with the branch helpers. I think this improves readability a lot, good work! I've added a few, mostly minor, notes. I think we should be able to merge this sometime soon.

@wfxr
Copy link
Owner

wfxr commented Mar 9, 2026

Thanks for the thorough review, @sandr01d! I've addressed all your comments — either applied the suggestions or replied with explanations. PTAL

wfxr and others added 2 commits March 10, 2026 09:14
- Add _forgit_strip_ansi for removing ANSI escape codes
- Add _forgit_branch_list to list branches with current branch first
- Add _forgit_extract_branch_name to parse branch names from git output
- Update checkout_branch, switch_branch, branch_delete, cherry_pick_from_branch,
  and branch_preview to use the new helpers
- Fix _forgit_inside_work_tree to suppress stderr
Add worktree browser (gwt) and worktree delete selector (gwd) with:
- Interactive worktree list with lock status, branch info, and age
- Preview showing working tree status and recent commits
- Ctrl-Y to copy worktree path, Alt-L to toggle lock/unlock
- Shell integration for zsh (with cd) and fish (with cd)
- Tab completions for zsh, bash, and fish
- Documentation in README with keybindings and options

Co-Authored-By: Sufien Tout <sufientout@gmail.com>
- Extract dim formatting into _forgit_print_dim() to replace hardcoded
  ANSI sequences in _forgit_branch_list()
- Add empty state check and alt-l lock toggle reload for worktree delete
- Fix worktree path completion to handle paths with spaces using -z flag
- Add worktree command completion for bash and zsh
@wfxr wfxr merged commit 3b9ebce into wfxr:main Mar 10, 2026
4 checks passed
@wfxr wfxr mentioned this pull request Mar 10, 2026
17 tasks
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.

Feature Request: Add git worktree switcher like gcb

7 participants