163 lines
5.4 KiB
Bash
163 lines
5.4 KiB
Bash
worktree() {
|
|
if [[ "$1" == "" ]]; then
|
|
echo "usage: worktree {add,cd,list,remove,sync} <branch> [<branch>...]"
|
|
return 1
|
|
elif [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
|
|
echo "usage: worktree {add,cd,list,remove,sync} <branch> [<branch>...]
|
|
|
|
Manage git worktrees by branch name."
|
|
return 0
|
|
fi
|
|
|
|
local cmd=$1
|
|
local branch=$2
|
|
|
|
if ! git rev-parse --git-dir &>/dev/null; then
|
|
print -P "%F{red}error:%f not a git repository"
|
|
return 1
|
|
fi
|
|
|
|
if [[ "$cmd" != "cd" ]] && \
|
|
[[ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" == "true" ]] && \
|
|
[[ "$(git rev-parse --absolute-git-dir)" != "$(git rev-parse --show-toplevel)/.git" ]]
|
|
then
|
|
print -P "%F{red}error:%f must be run from the main clone, not a worktree"
|
|
return 1
|
|
fi
|
|
|
|
case $cmd in
|
|
add)
|
|
if [[ -z "$branch" ]]; then
|
|
print -P "%F{red}error:%f missing <branch> argument"
|
|
return 1
|
|
fi
|
|
local root=$(git rev-parse --show-toplevel)
|
|
local wt_dest=${root:h}/${root:t}@${branch}
|
|
if ! git show-ref --verify --quiet refs/heads/"$branch" && \
|
|
! git show-ref --verify --quiet refs/remotes/origin/"$branch"; then
|
|
git branch "$branch" origin/main || return 1
|
|
fi
|
|
git worktree add "$wt_dest" "$branch" || return 1
|
|
if [ -f $root/.enter ] && [ -f $root/.exit ]; then
|
|
cp $root/.enter $wt_dest/.enter
|
|
cp $root/.exit $wt_dest/.exit
|
|
_autoenv_authorized $wt_dest/.enter yes
|
|
_autoenv_authorized $wt_dest/.exit yes
|
|
fi
|
|
;;
|
|
cd)
|
|
if [[ -z "$branch" ]]; then
|
|
print -P "%F{red}error:%f missing <branch> argument"
|
|
return 1
|
|
fi
|
|
local wt_path
|
|
wt_path=$(git worktree list --porcelain | awk -v b="$branch" \
|
|
'/^worktree /{ sub(/^worktree /, ""); p=$0 } /^branch refs\/heads\//{ sub(/^branch refs\/heads\//, ""); if($0==b) print p }')
|
|
if [[ -z "$wt_path" ]]; then
|
|
print -P "%F{red}error:%f no worktree for branch: $branch"
|
|
return 1
|
|
fi
|
|
cd "$wt_path"
|
|
;;
|
|
list|ls)
|
|
local root=$(git rev-parse --show-toplevel)
|
|
local prefix="${root:h}/${root:t}@"
|
|
git worktree list --porcelain | awk -v prefix="$prefix" '
|
|
/^worktree /{ sub(/^worktree /, ""); p=$0 }
|
|
/^branch refs\/heads\//{ if(index(p, prefix)==1) { sub(/^branch refs\/heads\//, ""); print } }
|
|
'
|
|
;;
|
|
rm|remove)
|
|
local force=0
|
|
local branches=()
|
|
local arg
|
|
for arg in "${@:2}"; do
|
|
case "$arg" in
|
|
-f|--force) force=1 ;;
|
|
*) branches+=("$arg") ;;
|
|
esac
|
|
done
|
|
if (( ${#branches[@]} == 0 )); then
|
|
print -P "%F{red}error:%f missing <branch> argument"
|
|
return 1
|
|
fi
|
|
local root=$(git rev-parse --show-toplevel)
|
|
local wt_list
|
|
wt_list=$(git worktree list --porcelain)
|
|
local failed=0
|
|
local wt_path
|
|
for branch in "${branches[@]}"; do
|
|
wt_path=$(echo "$wt_list" | awk -v b="$branch" \
|
|
'/^worktree /{ sub(/^worktree /, ""); p=$0 } /^branch refs\/heads\//{ sub(/^branch refs\/heads\//, ""); if($0==b) print p }')
|
|
if [[ -z "$wt_path" ]]; then
|
|
print -P "%F{yellow}warning:%f no worktree for branch: $branch"
|
|
continue
|
|
fi
|
|
if [[ -f "$wt_path/BUILD" ]] || [[ -f "$wt_path/WORKSPACE" ]]; then
|
|
if (( $+commands[bazel] )); then
|
|
bazel --output_base="$wt_path" clean --expunge
|
|
elif [[ -x "$wt_path/bazelw" ]]; then
|
|
"$wt_path/bazelw" clean --expunge
|
|
fi
|
|
fi
|
|
local -a rm_args=()
|
|
(( force )) && rm_args+=(--force)
|
|
if ! git worktree remove "${rm_args[@]}" "$wt_path"; then
|
|
failed=1
|
|
continue
|
|
fi
|
|
local parent=${wt_path:h}
|
|
while [[ "$parent" != "${root:h}" ]] && [[ -d "$parent" ]]; do
|
|
rmdir "$parent" 2>/dev/null || break
|
|
parent=${parent:h}
|
|
done
|
|
done
|
|
return $failed
|
|
;;
|
|
sync)
|
|
local root=$(git rev-parse --show-toplevel)
|
|
local prefix="${root:h}/${root:t}@"
|
|
local wt_list
|
|
wt_list=$(git worktree list --porcelain)
|
|
local branches=("${@:2}")
|
|
if (( ${#branches[@]} == 0 )); then
|
|
branches=("${(@f)$(echo "$wt_list" | awk -v prefix="$prefix" '
|
|
/^worktree /{ sub(/^worktree /, ""); p=$0 }
|
|
/^branch refs\/heads\//{ if(index(p, prefix)==1) { sub(/^branch refs\/heads\//, ""); print } }
|
|
')}")
|
|
fi
|
|
local failed=0
|
|
local wt_path
|
|
for branch in "${branches[@]}"; do
|
|
[[ -z "$branch" ]] && continue
|
|
wt_path=$(echo "$wt_list" | awk -v b="$branch" \
|
|
'/^worktree /{ sub(/^worktree /, ""); p=$0 } /^branch refs\/heads\//{ sub(/^branch refs\/heads\//, ""); if($0==b) print p }')
|
|
if [[ -z "$wt_path" ]]; then
|
|
print -P "%F{yellow}warning:%f no worktree for branch: $branch"
|
|
failed=1
|
|
continue
|
|
fi
|
|
if [[ "$wt_path" == "$root" ]]; then
|
|
continue
|
|
fi
|
|
for f in .enter .exit; do
|
|
[[ -f "$root/$f" ]] || continue
|
|
if [[ -L "$wt_path/$f" ]]; then
|
|
rm "$wt_path/$f"
|
|
elif [[ -f "$wt_path/$f" ]] && cmp -s "$root/$f" "$wt_path/$f"; then
|
|
continue
|
|
fi
|
|
cp "$root/$f" "$wt_path/$f"
|
|
_autoenv_authorized "$wt_path/$f" yes
|
|
print -P "%F{green}updated:%f $wt_path/$f"
|
|
done
|
|
done
|
|
return $failed
|
|
;;
|
|
*)
|
|
print -P "%F{red}error:%f unknown command: $cmd"
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|