Repository navigation
Expand file tree
/
Copy pathwt-shell
More file actions
208 lines (197 loc) · 9.01 KB
/
Copy pathwt-shell
File metadata and controls
208 lines (197 loc) · 9.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#!/usr/bin/env bash
# wt-shell — the `wt` git-worktree shell function (front end for the `wt` binary).
#
# `wt` is the single command the user drives. It intercepts the operations that
# must run IN your shell — the ones that change the shell's current directory,
# which a child process cannot do — and forwards everything else to the binary:
# wt main cd to the main clone
# wt cd <query> cd to the worktree whose branch fuzzy-matches <query>
# wt add <branch> create a worktree (via the binary) AND cd into it
# wt rm | list | … forwarded straight to the binary
#
# Because it changes the shell's directory, this must be SOURCED, not executed.
# Source it from your shell rc:
# source ~/.local/bin/wt-shell
#
# The sourced `wt` function shadows the `wt` binary on your PATH; the function
# reaches the binary with `command wt`. Implementation / command behaviour:
# the `wt` binary (and SPEC.md in its repository).
# Curated top-level help for the `wt` command surface. Unlike `wt --help` (which
# is implementation-oriented: it lists `resolve`, not the shell-only `cd`), this
# describes the commands as the user invokes them through the function.
_wt_help() {
cat <<'EOF'
wt — git worktree helper (a shell front end for the wt binary)
Usage: wt <command> [args]
Commands:
wt list all worktrees (same as `wt list`)
wt list [-p] [-s] [-g] list worktrees: paths + branches (-s: + size, -g: + git
state — clean? merged? pushed? last commit)
wt -s | -g | -sg shorthand: list flags work without the `list` word
wt cd <query> cd to the worktree at <query>, or whose branch/path fuzzy-matches it
wt main cd to the main clone (same as `wt cd` or `wt cd main`)
wt add [opts] <branch> create a sibling worktree for <branch> and cd into it
wt add -d [commit-ish] ...or a detached worktree (no branch), default HEAD
wt rm [opts] [path] remove a worktree (offers to delete its branch too)
wt copy [-f] copy the configured paths ([copy] in the config file)
from the main clone into the current worktree
cd / main / add / rm change the shell's directory — which is why `wt` is a
sourced function rather than a plain program. For a command's own options, run
`wt <command> -h`, e.g. `wt add -h` or `wt rm -h`.
EOF
}
# Print "$2" to stderr painted with SGR code "$1", matching the binary's
# narration palette (36 = cyan info, 93 = bright-yellow warning). The
# remaining arguments are the command's args, scanned for the global
# --color; `auto` then follows the binary's stderr rule (CLICOLOR_FORCE
# beats NO_COLOR beats stderr-is-a-colour-terminal).
_wt_msg() {
local sgr=$1 msg=$2 when=auto prev= a on=
shift 2
for a in "$@"; do
case "$a" in
--color=*) when=${a#--color=} ;;
*) [ "$prev" = --color ] && when=$a ;;
esac
prev=$a
done
case "$when" in
always) on=1 ;;
never) ;;
*)
if [ -n "${CLICOLOR_FORCE:-}" ]; then on=1
elif [ -z "${NO_COLOR:-}" ] && [ -t 2 ] && [ "${TERM:-}" != dumb ]; then on=1
fi
;;
esac
if [ -n "$on" ]; then
printf '\033[%sm%s\033[0m\n' "$sgr" "$msg" >&2
else
printf '%s\n' "$msg" >&2
fi
}
_wt_info() { _wt_msg 36 "$@"; }
_wt_warn() { _wt_msg 93 "$@"; }
# Remember the main clone of the repo the shell is currently in, so
# _wt_recover has somewhere to go if this directory is later deleted out from
# under us. Refreshed at the top of every `wt` invocation; outside a repo the
# lookup fails and the previous value is kept.
_wt_remember_main() {
local m
m="$(command wt main 2>/dev/null)" && [ -n "$m" ] && _wt_main_clone=$m
}
# Move the shell somewhere real if its current directory has been deleted
# (e.g. the worktree was removed by another shell — getcwd fails and every
# command breaks until the shell cds out). Prefers the last main clone seen
# by `wt` in this shell; falls back to the nearest existing ancestor of the
# dead path. Silent no-op when cwd is alive, so it is also safe to run before
# every prompt:
# PROMPT_COMMAND="_wt_recover${PROMPT_COMMAND:+; $PROMPT_COMMAND}"
# Any arguments are scanned for the global --color (see _wt_msg).
_wt_recover() {
[ -d "$PWD" ] && return 0
local dead=$PWD
if [ -n "${_wt_main_clone:-}" ] && [ -d "$_wt_main_clone" ]; then
cd "$_wt_main_clone" &&
_wt_warn "wt: $dead is gone — moved to main clone $_wt_main_clone" "$@"
else
local p=$dead
while [ -n "$p" ] && [ ! -d "$p" ]; do p=${p%/*}; done
p=${p:-/}
cd "$p" && _wt_warn "wt: $dead is gone — moved to nearest existing dir $p" "$@"
fi
}
# True if any argument is a help flag (-h/--help).
_wt_wants_help() {
local a
for a in "$@"; do
case "$a" in -h|--help) return 0 ;; esac
done
return 1
}
wt() {
local target st
# A deleted cwd breaks everything below (git needs a working directory);
# rescue the shell first, then remember this repo's main clone for the
# next rescue.
_wt_recover "$@"
_wt_remember_main
case "${1:-}" in
-h|--help|help)
# Top-level help: our own curated overview, NOT `wt --help`.
_wt_help
return 0
;;
main|parent)
# `parent` is the command's former name, kept working silently.
# Per-command help forwards to the binary (it prints to stdout and
# exits, so it must NOT be captured-and-cd'd into). Otherwise: the
# binary prints the main-clone root on stdout; cd into it. The
# remaining args (e.g. the global --color) are forwarded.
shift
if _wt_wants_help "$@"; then command wt main --help; return $?; fi
target="$(command wt main "$@")"; st=$?
if [ $st -eq 0 ] && [ -n "$target" ]; then cd "$target"; else return $st; fi
;;
cd)
# `wt cd QUERY`: `wt resolve QUERY` fuzzy-matches QUERY against
# worktree branches and prints the matched worktree path on stdout;
# cd into it. Ambiguity / no-match are reported on stderr (exit 2 /
# 1) and leave cwd unchanged. Bare `wt cd`, and the literal query
# `main`, both behave like `wt main` (cd to the main clone).
# (`resolve` is the binary's reusable primitive; the `cd` verb lives
# only here, in the shell wrapper.)
shift
if _wt_wants_help "$@"; then command wt resolve --help; return $?; fi
if [ $# -eq 0 ] || [ "$1" = main ]; then
target="$(command wt main)"; st=$?
else
target="$(command wt resolve "$@")"; st=$?
fi
if [ $st -eq 0 ] && [ -n "$target" ]; then cd "$target"; else return $st; fi
;;
add)
# The binary creates the worktree (narrating on stderr) and prints
# the new worktree path on stdout; cd into it.
if _wt_wants_help "$@"; then command wt "$@"; return $?; fi
target="$(command wt "$@")"; st=$?
if [ $st -eq 0 ] && [ -n "$target" ]; then cd "$target"; else return $st; fi
;;
rm)
# rm can delete the directory the shell is standing in (removing the
# current worktree, or a parent of cwd). In that case the binary
# prints a safe dir (the main clone) on stdout; cd there so the
# shell is not stranded in a deleted directory (which would then
# break `getwd` and any later git command, including `wt main`).
# The prompts (remove the worktree? delete its branch?) still work:
# they are written to stderr and stdin stays attached under the
# capture.
if _wt_wants_help "$@"; then command wt "$@"; return $?; fi
target="$(command wt "$@")"; st=$?
[ -n "$target" ] && cd "$target" && _wt_info "wt: cd $target" "$@"
return $st
;;
*)
# Everything that does not change the shell's directory — list,
# copy, no args (-> list), AND any unknown command (the binary then
# prints a proper "unrecognized subcommand" error) — is forwarded
# with stdio attached. (`list`/`copy` handle their own -h directly
# here.)
command wt "$@"
;;
esac
}
# If executed directly (not sourced), the function can't affect the parent
# shell's cwd. Explain how to use it and exit gracefully.
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
cat >&2 <<'EOF'
wt-shell must be SOURCED, not executed — only a function running in your shell
can change the shell's directory. It is normally sourced from ~/.bashrc:
source ~/.local/bin/wt-shell
Then use the `wt` command, e.g. `wt main` or `wt add dev/fix-foo`
(run `wt -h` for the full command list).
Without sourcing, the plain `wt` binary still works — it just prints the
resulting path instead of cd-ing to it.
EOF
exit 0
fi