I also swapped the printf and the redirection since I think I prefer it this way.
91 lines
3.0 KiB
Bash
Executable File
91 lines
3.0 KiB
Bash
Executable File
#!/usr/bin/env zsh
|
|
|
|
# Create a temporary worktree.
|
|
#
|
|
# Useful for doing quick tasks on other branches without modifying the current
|
|
# working tree. So no broken/outdated symlinks to dotfiles or unnecessary
|
|
# recompiling after switching back for example.
|
|
#
|
|
# Checks out the first argument in a worktree at a temporary directory. Then
|
|
# spawns an interactive shell inside of it.
|
|
#
|
|
# When the shell closes the worktree is tried to be removed. Until that works
|
|
# without problems (e.g. dirty), a new shell is spawned to resolve all conflicts
|
|
# (e.g. stashing).
|
|
#
|
|
# Instead of dropping in an interactive shell, the commands to execute can be
|
|
# passed via stdin. If any conflicts arise, all further shells are interactive.
|
|
# TODO: Override with flag that just `stash -u`
|
|
#
|
|
# Examples for scripted usage:
|
|
# Merge branches without leaving the current one:
|
|
# % git-checkout-worktree main <<<"git merge dev"
|
|
#
|
|
# Same for rebase (as `git rebase dev feature` switches to `feature`):
|
|
# % git-checkout-worktree feature <<<"git rebase dev"
|
|
#
|
|
#
|
|
# TODO: The git alias version seems to break with the same arguments that the
|
|
# zsh native one works with.
|
|
# TODO: Supplying a flag like --no-checkout breaks the naming of the worktree
|
|
|
|
emulate -L zsh -o err_return -o no_unset
|
|
|
|
local git_dir cwd_offset REPO_NAME WORKTREE_PATH
|
|
# Use the folder name of the main working tree to make calls from another
|
|
# temporary working tree possible
|
|
git_dir="${$(git rev-parse --git-dir):A}"
|
|
[[ $git_dir == */.git/modules/* ]] || git_dir="${git_dir%%/.git*}"
|
|
REPO_NAME="${git_dir:t}"
|
|
WORKTREE_PATH="$(mktemp -d -p "" "wtree.$REPO_NAME.${1//\//_}.XXX")"
|
|
|
|
local errc ret=0
|
|
git worktree add "$WORKTREE_PATH" "$@" || ret=$?
|
|
|
|
if (( ret )); then
|
|
rmdir "$WORKTREE_PATH"
|
|
return $ret
|
|
fi
|
|
|
|
trap '
|
|
errc=$?
|
|
printf >&2 "Exiting abnormally. Check and possibly remove \"%s\" manually.\n" "'"$WORKTREE_PATH"'"
|
|
return $errc
|
|
' INT QUIT TERM EXIT
|
|
|
|
cwd_offset="${${PWD#$(git rev-parse --show-toplevel)}#/}"
|
|
pushd -q "$WORKTREE_PATH"
|
|
until [[ -d $cwd_offset || -z $cwd_offset ]]; do
|
|
cwd_offset="${cwd_offset:h}"
|
|
done
|
|
[[ -z $cwd_offset ]] || cd "$cwd_offset"
|
|
|
|
# Discard some environment variables that were set by git when calling this
|
|
# script through an alias.
|
|
#
|
|
# Is set for submodules and will confuse git in the subshell
|
|
unset GIT_DIR
|
|
# Not sure if this can bring any issues, but better be safe
|
|
unset GIT_PREFIX
|
|
# TODO: Do we want to unset this too? Could have been set on purpose by the user
|
|
# and not git. Maybe only for the subshell via `env`?
|
|
#unset GIT_EXEC_PATH
|
|
|
|
"$SHELL" && errc=$? || errc=$?
|
|
(( !errc )) || echo "shell exited with $errc"
|
|
|
|
# Restart the shell (forcefully interactive) until the worktree is removed
|
|
until [[ ! -e "$WORKTREE_PATH" ]] || git worktree remove "$WORKTREE_PATH"; do
|
|
[[ -t 0 ]] ||
|
|
printf >&2 "Dropping into interactive shell to resolve conflicts\n"
|
|
"$SHELL" -i && errc=$? || errc=$?
|
|
(( !errc )) || echo "shell exited with $errc"
|
|
done
|
|
|
|
# Reset traps and PWD
|
|
trap '-' INT QUIT TERM EXIT
|
|
popd -q || true
|
|
|
|
git worktree prune
|
|
return $errc
|