-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhook-setup
224 lines (209 loc) · 8.2 KB
/
hook-setup
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#! /usr/bin/env sh
# Remove trailing and squeeze multiple '/'
normalize() {
printf '%s' "$1" | tr -s '/' | sed 's^/$^^'
}
# Build relative path from arg1 to arg2
# Return arg2, if has not equal parts
# Require absolute paths as args & confused with '.' & '..'!
relpath () (
_from="$(normalize "$1")"
_to="$(normalize "$2")"
[ ! x"${_from#/}" = x"${_from}" ] && [ ! x"${_to#/}" = x"${_to}" ] || { printf 'relpath: %s\n' 'Absolute paths is required!' "arg1='${_from}'" "arg2='${_to}'" >&2; return 1; }
[ x"$(printf '%s ' "${_from}" "${_to}" | grep -E '\./' 2>/dev/null)" = x ] || { printf 'relpath: %s\n' "Use of '.'/'..' in absolute path is confusing..." "arg1='${_from}'" "arg2='${_to}'" >&2; return 1; }
_from="${_from#/}"
_depth=$(printf '%s' "${_from}" | tr "/\n" "\n/" | wc -l) # depth to root
# Eat equal parts
_OIFS="${IFS}"
IFS="/"
set -- ${_to}
IFS="${_OIFS}"
while shift; do # First shift remove emty elem
[ x"${_from#"$1"}" = x"${_from}" ] && break
_from="${_from#"$1"/}"
_to="${_to#/"$1"}"
done
_steps=$(printf '%s' "${_from}" | tr "/\n" "\n/" | wc -l) # depth remain
[ ${_depth} -eq ${_steps} ] && printf '%s' "${_to}" && return 0 # no equal parts found
_ret=
for i in $(seq 1 ${_steps}); do
_ret="../${_ret}"
done
printf '%s' "${_ret}${_to#/}"
)
gitbin="$(which git)" || { printf '%s\n' 'Well, this is discouraging...' "Can't find git bin\! Is it installed?"; exit 1; }
hooks_dest="${1:-$(cd "$(dirname -- "${self}")/.."; "${gitbin}" rev-parse --show-toplevel 2>/dev/null || pwd)/.git}/hooks"
hooks_dest="$(normalize "${hooks_dest}")"
g_iHooksPage=0
g_iHooksPageSize="${g_page_size:=20}"
if [ ! -e "${hooks_dest}" ] || [ ! -d "${hooks_dest}" ] || [ $(printf '%s' "${hooks_dest}" | grep -v '.git/hooks') ]; then
printf '%s\n' "Local hooks dir '${hooks_dest}' is missing, not a dir or not in '.git' folder!" \
"If You use '--git-dir' option, please specify full path to Your '.git' dir as argument for this script." \
"Otherwise, copy this script inside worktree or (preferred) attach as submodule."
exit 1
elif [ -e "${g_hooks_source}" ] && [ ! -d "${g_hooks_source}" ]; then
printf '%s\n' "Source of hooks '${g_hooks_source}' exist, but not a dir!"
exit 1
elif [ ! -e "${g_hooks_source}" ]; then
mkdir -p "${g_hooks_source}"
fi
# ANSI escape sequence
ESS="\033["
# Menu header (each arg is line)
set --\
'This setup will make soft links' \
"in '${hooks_dest}'" \
"to '${self}'" \
'When hook is (Installed), You may drop Your scripts in' \
"'${g_hooks_source}/<hook_name>.d'" \
''
# Last line is block separator
g_aHeader=$(asave "${@}")
# Available hooks
set --\
pre-commit prepare-commit-msg commit-msg post-commit \
applypatch-msg pre-applypatch post-applypatch \
pre-rebase post-rewrite post-checkout post-merge pre-push
g_aHooks=$(asave "${@}")
if git flow version >/dev/null 2>&1; then
eval "set -- $g_aHooks" \
filter-flow-hotfix-finish-tag-message filter-flow-hotfix-start-version filter-flow-release-branch-tag-message \
filter-flow-release-finish-tag-message filter-flow-release-start-version post-flow-bugfix-delete \
post-flow-bugfix-finish post-flow-bugfix-publish post-flow-bugfix-pull post-flow-bugfix-start post-flow-bugfix-track \
post-flow-feature-delete post-flow-feature-finish post-flow-feature-publish post-flow-feature-pull post-flow-feature-start \
post-flow-feature-track post-flow-hotfix-delete post-flow-hotfix-finish post-flow-hotfix-publish post-flow-hotfix-start \
post-flow-release-branch post-flow-release-delete post-flow-release-finish post-flow-release-publish \
post-flow-release-start post-flow-release-track pre-flow-feature-delete pre-flow-feature-finish pre-flow-feature-publish \
pre-flow-feature-pull pre-flow-feature-start pre-flow-feature-track pre-flow-hotfix-delete pre-flow-hotfix-finish \
pre-flow-hotfix-publish pre-flow-hotfix-start pre-flow-release-branch pre-flow-release-delete pre-flow-release-finish \
pre-flow-release-publish pre-flow-release-start pre-flow-release-track
g_aHooks=$(asave "${@}")
fi
eval "set -- $g_aHooks" \
''
g_aHooks=$(asave "${@}")
# Prints msg (arg2+) on specified line (arg1)
pline() {
_ln=1
[ $(expr $1 + 0 2>/dev/null) ] && [ $1 -gt 0 ] && _ln=$1 && shift
printf '%b' "${ESS}${_ln};1H$*${ESS}K"
}
# Prints menu header
pheader() {
eval "set -- ${g_aHeader}"
_sline=0
for i in $(seq 1 $#); do
_line="$((${_sline} + ${i}))"
eval _msg="\$${i}"
pline "${_line}" "${_msg}"
done
}
# Prints menu hooks list & hooks state; if hook index (arg1) is provided - act on hook line only
phooks() {
_arg=$1
_hookid=""
eval "set -- ${g_aHooks}"
case "${_arg}" in
[1-9]|[1-9][0-9]) _hookid=${_arg};;
n|N) [ $((${g_iHooksPage} * ${g_iHooksPageSize})) -le $# ] && g_iHooksPage=$((g_iHooksPage + 1));;
p|P) [ ${g_iHooksPage} -gt 0 ] && g_iHooksPage=$((g_iHooksPage - 1));;
esac
_sline=$(printf '%s' "${g_aHeader}" | wc -l)
for i in $(seq $((1 + (${g_iHooksPage} * ${g_iHooksPageSize}))) $((($g_iHooksPage + 1) * ${g_iHooksPageSize}))); do
[ ! x"${_hookid}" = x ] \
&& [ ${_hookid} -gt $((${g_iHooksPage} * ${g_iHooksPageSize})) ] \
&& [ ${_hookid} -le $((($g_iHooksPage + 1) * ${g_iHooksPageSize})) ] \
&& i=${_hookid}
eval _name="\$${i}"
[ x"${_name}" = x ] && break
_line=$((${_sline} + ${i} - (${g_iHooksPage} * ${g_iHooksPageSize})))
_msg="${i} ${_name} "
if [ x"$(rreadlink "${hooks_dest}/${_name}" 2>/dev/null)" = x"${self}" ]; then
_msg="${_msg}(Installed)"
elif [ -e "${hooks_dest}/${_name}" ] || [ -L "${hooks_dest}/${_name}" ]; then
_msg="${_msg}(Occupied by"
[ -e "${hooks_dest}/${_name}" ] && _msg="${_msg} other" || _msg="${_msg} broken"
[ -L "${hooks_dest}/${_name}" ] && _msg="${_msg} link!)" || _msg="${_msg} file!)"
fi
pline ${_line} "${_msg}"
[ ! x"${_hookid}" = x ] && break
done
return 0
}
# Print menu status buffer
# Call with args will add lines to buffer (arg is line)
# Call without args will print and clear buffer
pstatus() {
_input=$(asave "${@}")
if [ $(printf '%s' "${_input}" | wc -l) -gt 0 ]; then
eval "set -- ${g_aStatus} ${_input}"
g_aStatus=$(asave "${@}")
else
eval "set -- ${g_aStatus}"
[ $(expr $1 + 0 2>/dev/null) ] || eval "set -- 0 ${g_aStatus}" # First invocation fix
_count=1
shift
[ $# -gt ${_count} ] && _count=$#
_sline=$(($(printf '%s' "${g_aHeader}" | wc -l) + ${g_iHooksPageSize}))
[ $(((${g_iHooksPage} + 1) * ${g_iHooksPageSize})) -gt $(printf '%s' "${g_aHooks}" | wc -l) ] \
&& _sline=$((${_sline} - ${g_iHooksPageSize} * (${g_iHooksPage} + 1) % $(printf '%s' "${g_aHooks}" | wc -l))) \
&& _count=$((${_count} + ${g_iHooksPageSize} * (${g_iHooksPage} + 1) % $(printf '%s' "${g_aHooks}" | wc -l)))
for i in $(seq 1 ${_count}); do
_line=$((${_sline} + ${i}))
eval _msg="\$${i}"
pline ${_line} "${_msg}"
done
# Re-print last line for accurate cursor positioning
_line=$(( ${_sline} + $#))
eval _msg="\$$#"
pline ${_line} "${_msg}"
# Clear buffer and store count of printed lines in arg1
set -- "$(printf '%s' "${g_aStatus}" | wc -l)"
g_aStatus=$(asave "${@}")
fi
}
# Install/uninstall hook wrapper
ihook() {
case $1 in
[1-9]|[1-9][0-9]) _hookid=$1;;
*) return 0;;
esac
eval "set -- ${g_aHooks}"
eval _name="\$${_hookid}"
[ x"${_name}" = x ] && return 0
if [ x"$(rreadlink "${hooks_dest}/${_name}" 2>/dev/null)" = x"${self}" ]; then
rm -f "${hooks_dest}/${_name}" 2>/dev/null \
|| pstatus "Can't remove :(" \
"('rm -f ${hooks_dest}/${_name}' returned '${?}')"
else
if [ -e "${hooks_dest}/${_name}" ] || [ -L "${hooks_dest}/${_name}" ]; then
pstatus '/!\WARNING/!\ All content of:' \
"'${hooks_dest}/${_name}'" \
'Will be lost!!! Continue? (enter YES in capital letters): '
pstatus
read _ans
case "${_ans}" in
'YES') rm -f "${hooks_dest}/${_name}" 2>/dev/null \
||{ pstatus "Can't remove :(" \
"('rm -f ${hooks_dest}/${_name}' returned '${?}')"; \
return 0; };;
*) return 0;;
esac
fi
(cd ${hooks_dest}; ln -s "$(relpath "${hooks_dest}/${_name}" "${self}")" "${_name}")
fi
}
# Menu begin
clear
pheader
phooks
while :; do
pstatus "Please, enter number to operate on hook, 'n'/'p' for next/previous page or 'q' to exit setup" ""
pstatus
read ui
case "${ui}" in
[1-9]|[1-9][0-9]) ihook "${ui}"; phooks "${ui}";;
n|N|p|P) phooks "${ui}";;
q|Q) clear; exit 0;;
esac
done