いままでworktree、よく開発するブランチは xxx-a,xxx-b,xxx-c のように3つくらい作っておいてそこで、pecoでdirectory移動して使っていたのだが、どのブランチをなににチェックアウトしてるのかを覚えておくのがだるすぎて、ようやくzshrc書いた。
# -------------------------------------
# git worktree
# -------------------------------------
export WORKTREE_BASE="${HOME}/worktrees"
function peco-worktree() {
if [[ ! -d "$WORKTREE_BASE" ]]; then
zle clear-screen
return 0
fi
local selected
selected=$(
find "$WORKTREE_BASE" -mindepth 2 -maxdepth 2 -type d 2>/dev/null \
| awk -F'/' -v OFS='\t' '{ print $(NF-1) "/" $NF, $0 }' \
| column -t -s $'\t' \
| peco --query "$LBUFFER"
)
if [[ -n "$selected" ]]; then
local target_path
target_path=$(echo "$selected" | awk '{print $NF}')
BUFFER="cd ${target_path}"
zle accept-line
fi
zle clear-screen
}
zle -N peco-worktree
bindkey '^w' peco-worktree
function cw() {
if [[ $# -lt 1 ]]; then
echo "Usage: cw <worktree-name>"
return 1
fi
local name="$1"
local repo_root
repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
if [[ -z "$repo_root" ]]; then
echo "Not in a git repository"
return 1
fi
local repo_name
repo_name=$(basename "$repo_root")
local worktree_path="${WORKTREE_BASE}/${repo_name}/${name}"
if [[ ! -d "$worktree_path" ]]; then
mkdir -p "${WORKTREE_BASE}/${repo_name}"
git -C "$repo_root" fetch origin "$name" --quiet 2>/dev/null
if git -C "$repo_root" show-ref --verify --quiet "refs/heads/${name}"; then
git -C "$repo_root" worktree add "$worktree_path" "$name" || return 1
elif git -C "$repo_root" show-ref --verify --quiet "refs/remotes/origin/${name}"; then
git -C "$repo_root" worktree add -b "$name" "$worktree_path" "origin/${name}" || return 1
else
local base
base=$(git -C "$repo_root" for-each-ref --format='%(refname:short)' refs/heads refs/remotes \
| grep -v '/HEAD$' \
| peco --prompt "base for ${name}> ")
if [[ -z "$base" ]]; then
echo "base branch not selected"
return 1
fi
git -C "$repo_root" worktree add -b "$name" "$worktree_path" "$base" || return 1
fi
fi
cd "$worktree_path" || return 1
[[ -n "$TMUX" ]] && tmux rename-window "$name"
c
}
function cwc() {
if [[ ! -d "$WORKTREE_BASE" ]]; then
return 0
fi
local now threshold removed
now=$(date +%s)
threshold=$((30 * 86400))
removed=0
while IFS= read -r wt; do
[[ -d "$wt" ]] || continue
local last_commit last_mtime newest
last_commit=$(git -C "$wt" log -1 --format=%ct 2>/dev/null || echo 0)
last_mtime=$(stat -f %m "$wt" 2>/dev/null || echo 0)
newest=$last_commit
(( last_mtime > newest )) && newest=$last_mtime
(( newest == 0 )) && continue
if (( now - newest > threshold )); then
local days=$(( (now - newest) / 86400 ))
echo "Removing (last touched ${days}d ago): $wt"
local main_wt
main_wt=$(git -C "$wt" worktree list --porcelain 2>/dev/null | awk '/^worktree / { print $2; exit }')
if [[ -n "$main_wt" && "$main_wt" != "$wt" ]]; then
git -C "$main_wt" worktree remove --force "$wt" 2>/dev/null || rm -rf "$wt"
else
rm -rf "$wt"
fi
removed=$((removed + 1))
fi
done < <(find "$WORKTREE_BASE" -mindepth 2 -maxdepth 2 -type d 2>/dev/null)
find "$WORKTREE_BASE" -mindepth 1 -maxdepth 1 -type d -empty -delete 2>/dev/null
echo "Removed $removed stale worktree(s)"
}
作ったショートカット
cw <branch_name>って打つと、base branchを何にするかの選択がpecoで選択できて、enter押すとalias c=claude code xxxな感じでclaude codeを起動して、tmuxのwindow nameをbranch nameにする。cwcって打つと1ヶ月使ってないwork treeを消すctrl + wって打つと、worktreeの一覧が出てpecoでインクリメンタルサーチして移動できる。
とりあえず1時間くらいしか使ってないけど、便利すぎて失職寸前ですね!!!!!!!!!!!!!1