Roaming through git worktrees
git worktree
is great. It has become an integral part of my workflow in any non-trivial project.
If you’re not familiar with git worktrees start here. If you need some motivation see here.
Manually cd
ing across directories gets old really quickly. Generally, I rely on zoxide to efficiently traverse my filesystem. However, zoxide struggles when it comes to worktrees, as I explain here.
Instead, here are the techniques I use to improve my worktree flow, especially in monorepos.
Move between different worktrees
worktree-welder is a tiny cli utility I created to quickly switch between worktrees.
My favorite part about it is that it preserves our position relative to the worktree root. This means that if we’re in worktree-1/packages/backend
and want to switch to worktree-2
, it’ll take us to worktree-2/packages/backend
. I find this to be helpful, especially in large monorepos.
Move within the current worktree
Inspired by fzf’s ALT-C
keybinding, I created a zsh widget to seamlessly move within the current worktree. When invoked:
- It presents an fzf picker for all the directories in the current worktree.
- Once a selection is made, we
cd
to our selection.
One thing I find very useful is that the picker is scoped to the root of current git repository, not $PWD
, which enables this kind of thing:
- Assume we’re in a nested directory such as
myproject/packages/backend
. - We invoke our widget.
- We can fuzzy search across all the paths in the current repo, not just
backend/**/*
.
Also, it always presents the root of the current worktree as the first option. This means that going to the root of our current worktree is always 2 keystrokes away, regardless of how deep we are in the worktree (invoke the widget with ALT-i
then hit ENTER
to select the first option).
#!/usr/bin/env zsh
# fzf picker to cd to any directory of the current git repo (regardless of cwd)
# Inspired by fzf-cd-widget: https://github.com/junegunn/fzf/blob/7320b7df62039c879c4f609bca946ea09b438a98/shell/key-bindings.zsh#L74-L95
fzf-cd-repo-widget() {
local original_path=$(pwd)
# Find the root of the monorepo
local monorepo_path=$(git rev-parse --show-toplevel 2> /dev/null)
if [[ -z $monorepo_path ]]; then
zle redisplay
return 0
fi
# Change to the monorepo root
cd "$monorepo_path"
# Command to search for directories within the current git repo, including the root
local cmd="{ echo '.'; fd --type=directory .; }"
setopt localoptions pipefail no_aliases 2> /dev/null
# Use fzf to select a directory
local dir="$(eval "$cmd" | fzf --height=40%)"
# Change back to the original directory
cd "$original_path"
if [[ -z "$dir" ]]; then
zle redisplay
return 0
fi
# Prepare the command to change directory
zle push-line
BUFFER="builtin cd -- '${monorepo_path}/${dir}'"
zle accept-line
local ret=$?
unset dir
zle reset-prompt
return $ret
}
zle -N fzf-cd-repo-widget
# bind the widget to ALT+i
bindkey "^[i" fzf-cd-repo-widget
To use the preceding snippet, save it to a new file fzf-cd-repo-widget.zsh
, then source it from your .zshrc
:
# ~/.zshrc
source path/to/fzf-cd-repo-widget.zsh