Skip to content

Commit

Permalink
Finally found some time to push up a working version of this!
Browse files Browse the repository at this point in the history
  • Loading branch information
egyptianbman committed Apr 30, 2022
1 parent fd7f7d9 commit 4a80793
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 1 deletion.
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,70 @@
# zsh-git-worktree
# zsh-git-worktrees

A zsh plugin that makes git worktrees much more functional

## Why?

You're in the middle of working on something and someone asks you to change
gears and work on something else. You can commit and switch to another branch
or maybe stash and pop it later. Neither option is really ideal -- and this is
why git worktrees exist. The only problem with git worktrees is there isn't a
great workflow that allows the power of worktrees to shine through.

## How should I use this?

To get started, source `zsh-git-worktrees.zsh`, then run the following commands
for your repostiory:

```bash
$ mkdir my-neat-project
$ cd my-neat-project
$ git clone https://github.com/neat-org/my-neat-project main
$ cd main
$ gwt main
```

With the above and going forward, you'll have the following directory structure:

```
my-neat-project/
main/
my-neat-project -> my-neat-worktree
my-neat-worktree
my-other-neat-worktree
```

Using `gwt` to switch worktrees, you'll always be in a directory with the same
name as your project -- thanks to the symlink that gwt handles for you. This
helps with things like docker-compose, allowing you to switch between
worktrees, all while tricking compose into thinking you're in one "project".

## Auto completion

This plugin automatically loads auto completion. Feel free to hit tab after
`gwt` to get suggestions of available worktrees you can switch to.

## Usage

The following are some example command usage:

```bash
# List current worktrees
$ gwt

# Switch to neat-feature worktree, create it if it doesn't exist.
$ gwt neat-feature

# Rename the current worktree (and branch) to not-as-neat-feature
$ gwt mv not-as-neat-feature

# Remove completed feature and its corresponding branch
$ gwt rm completed-feature
```

Branch `main` is considered special as it holds your base repository. This
means it can't be renamed or replaced.

## Find a bug or want t contribute?

Hopefully you find this as helpful for you as it has been for me. Please feel
free to submit any suggestions or updates you make!
29 changes: 29 additions & 0 deletions completion/_gwt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#compdef gwt

__gwt_commands_and_branches() {
local -a branches
branches=$(git worktree list | awk '{ print $3 }' | sed -e 's/[[]//' -e 's/[]]//' | sort | uniq)
_describe 'options' '(ls\:"List current worktrees" switch\:"Switch to another worktree (creates if does not exist)" rm\:"Removes a worktree. If on current worktree, switches to main" '${branches}')'
}

__gwt_branches() {
if [[ $words[2] == 'ls' ]]; then
return
fi

local -a branches
branches=(${(f)"$(git worktree list | awk '{ print $3 }' | sed -e 's/[[]//' -e 's/[]]//' | sort | uniq)"})
_describe 'branch' branches
}

_gwt() {
local curcontext="$curcontext" ret=1

_arguments -s \
'1: :__gwt_commands_and_branches' \
'2: :__gwt_branches' && ret=0

return $ret
}

_gwt "$@"
18 changes: 18 additions & 0 deletions functions/__gwt_mv
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
local from=${1}
local to=${2}

if [[ -z ${2} ]]; then
from=$(basename $(/bin/pwd -P))
to=${1}
fi

if [[ ${from} == "main" || ${to} == "main" ]]; then
echo "Can't change main."
return -1
fi

git worktree move ../${from} ../${to}
git branch -m ${to}
__gwt_symlink ${to}

# vim: ft=zsh sw=2 ts=2 et
23 changes: 23 additions & 0 deletions functions/__gwt_switch
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
local to=${1}
local repo="$(basename $(git config remote.origin.url) | sed 's/.git//')" 2> /dev/null

if [[ -z ${to} || -z ${repo} ]]; then
echo "Are you in a project?"
return -1
fi

if [[ ! -d "../${to}" ]]; then
if git branch | grep ${to}; then
git checkout ${to}
git checkout -
else
git checkout -b ${to}
git checkout -
fi

git worktree add "../${to}"
fi

__gwt_symlink ${to}

# vim: ft=zsh sw=2 ts=2 et
17 changes: 17 additions & 0 deletions functions/__gwt_symlink
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
local repo="$(basename $(git config remote.origin.url) | sed 's/.git//')"
local branch=${1}

cd ../
rm ${repo} 2>/dev/null
ln -s ${branch} ${repo}

cd ${repo}
git branch --set-upstream-to origin/${branch}

if command -v tmux &> /dev/null; then
tmux set-environment -g PWD $(dirname ${PWD})/${repo}
fi

echo "Switched to \"${branch}\" worktree"

# vim: ft=zsh sw=2 ts=2 et
32 changes: 32 additions & 0 deletions functions/gwt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
case ${1} in
|ls)
git worktree list
;;
rm)
local removing="${2}"
local current=$(basename $(readlink -f "${PWD}"))

if [[ -z ${removing} ]]; then
echo "Can't find worktree to remove."
return -1
fi

if [[ "${current}" == "${removing}" ]]; then
__gwt_switch main
fi

git worktree remove "${2}"
git branch -d "${2}"
;;
mv)
__gwt_mv "${2}" "${3}"
;;
switch)
__gwt_switch "${2}"
;;
*)
__gwt_switch "${1}"
;;
esac

# vim: ft=zsh sw=2 ts=2 et
9 changes: 9 additions & 0 deletions zsh-git-worktrees.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fpath+="${0:A:h}/functions"

autoload -Uz gwt
autoload -Uz __gwt_mv
autoload -Uz __gwt_switch
autoload -Uz __gwt_symlink

fpath+="${0:A:h}/completion"
autoload -U compinit; compinit

0 comments on commit 4a80793

Please sign in to comment.