-
-
Notifications
You must be signed in to change notification settings - Fork 59
/
git-blametool.sh
executable file
·328 lines (274 loc) · 12.8 KB
/
git-blametool.sh
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#!/bin/bash
# This file is part of eRCaGuy_dotfiles: https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles
# Status: it works!
# Author: Gabriel Staples
# www.GabrielStaples.com
# www.ElectricRCAircraftGuy.com
# DESCRIPTION:
# git-blametool.sh:
# This is a wrapper around `git blame` to open up the output in your favorite text editor.
# DEMO AND SCREENSHOTS:
# See my answer here: https://stackoverflow.com/a/66433627/4561887
# INSTALLATION INSTRUCTIONS:
# 1. Install your editor of choice.
# 1. For me, that's Sublime Text 3. So, install it from here: https://www.sublimetext.com/3.
# 2. Install the "Git" package so that you get access to the "Git --> Git Blame" syntax
# highlighting.
# 2. Set up your blametool configuration settings as desired. I am setting my blametool editor to
# 'subl' (Sublime Text 3), and I am telling it to auto-delete the `git blame` output file
# after each run. Set these as you desire:
# # read your current setting, if any
# git config --global blametool.editor
# # Set your blame tool to `subl` (Sublime Text)
# git config --global blametool.editor subl
# # OR: set your blame tool to `code` (VSCode)
# git config --global blametool.editor code
# # set it to automatically delete autogenerated blametool files when done with them
# git config --global blametool.auto-delete-tempfile-when-done true # set to true or false (case-sensitive)
# 3. Open up "~/.gitconfig", after running the commands above, and verify you see the following,
# or similar, in the end of the file:
# ```
# [blametool]
# editor = subl
# auto-delete-tempfile-when-done = true
# ```
# 4. Create a symlink in ~/bin to this script so you can run it from anywhere.
# cd /path/to/here
# mkdir -p ~/bin
# ln -si "${PWD}/git-blametool.sh" ~/bin/git-blametool # required
# ln -si "${PWD}/git-blametool.sh" ~/bin/git-gs_blametool # optional; replace "gs" with your initials
# ln -si "${PWD}/git-blametool.sh" ~/bin/gs_git-blametool # optional; replace "gs" with your initials
# 5. Now you can use this command directly anywhere you like in any of these 5 ways:
# 1. `git blametool` <=== my preferred way to use this program, so it feels just like a native `git` cmd!
# 2. `git-blametool`
# 3. `git gs_blametool`
# 4. `git-gs_blametool`
# 3. `gs_git-blametool`
# 6. See `git blametool -h` for more details.
# References:
# 1. Issue to make this feature: https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/issues/13
VERSION="0.2.0"
AUTHOR="Gabriel Staples"
RETURN_CODE_SUCCESS=0
RETURN_CODE_ERROR=1
SCRIPT_NAME="$(basename "$0")"
VERSION_SHORT_STR="git blametool ('$SCRIPT_NAME') version $VERSION"
VERSION_LONG_STR="\
$VERSION_SHORT_STR
Author = $AUTHOR
See '$SCRIPT_NAME -h' for more info.
"
DEFAULT_EDITOR="subl"
DEFAULT_EDITOR_DESCRIPTION="Sublime Text 3"
TEMP_DIR="$HOME/temp/git-blametool"
HELP_STR="\
$VERSION_SHORT_STR
Purpose: open up your 'git blame' output in your favorite editor, so you can see who made
what changes, when, and in what commit, easily, and in your editor of choice.
Usage:
See 'man git blame' and 'git blame -h' for additional details. Since this is a thin wrapper
around 'git blame', it takes ALL of the same parameters that 'git blame' takes too! A typical
usage of 'git blametool' is like this:
$SCRIPT_NAME [-h|-?] [-v|--version] [commit_hash] <file_path>
Open up the 'git blame' output for file \"file_path\" from commit or branch
\"commit_hash\" in your editor specified by 'git config blametool.editor'.
If no editor is set, the default, '$DEFAULT_EDITOR' ($DEFAULT_EDITOR_DESCRIPTION) is
used. You can set your editor of choice with:
git config --global blametool.editor [editor-executable]
Example:
git config --global blametool.editor gedit
Read your currently-set editor value with:
git config --global blametool.editor
$SCRIPT_NAME
print this help menu
$SCRIPT_NAME [-h|-?]
print this help menu
$SCRIPT_NAME [-v|--version]
print author & version
Examples:
$SCRIPT_NAME master path/to/myfile.c
$SCRIPT_NAME path/to/myfile.c
$SCRIPT_NAME -h
$SCRIPT_NAME -v
$SCRIPT_NAME --version
Configuration:
1. Set your blametool editor. If not set, the default is '$DEFAULT_EDITOR'
($DEFAULT_EDITOR_DESCRIPTION). General form:
git config --global blametool.editor [editor-executable]
Example: set your editor as Sublime Text 3 (its command-line executable is 'subl'):
git config --global blametool.editor subl
Other popular choices:
git config --global blametool.editor vim
git config --global blametool.editor emacs
git config --global blametool.editor nano
git config --global blametool.editor gedit
git config --global blametool.editor leafpad
Remember, you can use ANY editor you want. You can set the 'editor' to a full path too if
you like:
git config --global blametool.editor \"/path/to/editor\"
2. Set whether or not to auto-delete the temporary file when done (if not set, the default is
'true'). Be sure to set to 'true' or 'false' (case-sensitive), NOT 'True' or 'False' or
'TRUE' or 'FALSE'! General form:
git config --global blametool.auto-delete-tempfile-when-done [true|false]
Examples:
git config --global blametool.auto-delete-tempfile-when-done true
git config --global blametool.auto-delete-tempfile-when-done false
Source Code:
https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/useful_scripts/git-blametool.sh
"
print_help() {
echo "$HELP_STR" | less -RFX
}
print_version() {
echo "$VERSION_LONG_STR"
}
# Parse the 'git blametool'-specific arguments first. Then, find the revision (git commit) argument,
# either `<rev>` or `<rev>..<rev>` below, as well as the `<file>` argument.
# Note: `man git blame` shows these are the possible args:
# git blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
# [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
# [--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>..<rev>]
# [--] <file>
parse_args() {
# For advanced argument parsing help and demo, see:
# https://stackoverflow.com/a/14203146/4561887.
if [ $# -eq 0 ]; then
echo "No arguments supplied. Printing help menu:"
print_help
exit $RETURN_CODE_ERROR
fi
# skip over all optional args beginning with `-`. But, if you see a `-L` or `-S` or
# `--contents`, skip over the accompanying mandatory positional arg right after those too.
# This will leave just 1 or 2 positional arguments remaining, with the last arg being
# `<file>`, and the 2nd-to-last arg (if it exists at all, since it doesn't have to be
# provided) being either `<rev>` or `<rev>..<rev>`.
POSITIONAL_ARGS_ARRAY=()
while [[ $# -gt 0 ]]; do
arg="$1"
# first letter of `arg`; see: https://stackoverflow.com/a/10218528/4561887
first_letter="${arg:0:1}"
case $arg in
# Help menu
"-h"|"-?")
print_help
exit $RETURN_CODE_SUCCESS
;;
# Version
"-v"|"--version")
print_version
exit $RETURN_CODE_SUCCESS
;;
# Unknown option (ie: unmatched in the switch cases above)
*)
# Only store into the position arguments array those arguments which do NOT
# begin with "-"!
if [ "$first_letter" != "-" ]; then
POSITIONAL_ARGS_ARRAY+=("$1") # save it in an array for later
fi
shift # past argument
if [ "$arg" == "-L" ] || [ "$arg" == "-S" ] || [ "$arg" == "--contents" ]; then
shift # past value
fi
;;
esac
done
# echo "POSITIONAL_ARGS_ARRAY contains: '${POSITIONAL_ARGS_ARRAY[*]}'." # for debugging
FILE_IN="" # `<file>`
COMMIT="" # `<rev>` or `<rev>..<rev>`
array_len=${#POSITIONAL_ARGS_ARRAY[@]}
if [ $array_len -ge 1 ]; then
FILE_IN="${POSITIONAL_ARGS_ARRAY[-1]}" # last filtered arg
fi
if [ $array_len -ge 2 ]; then
COMMIT="${POSITIONAL_ARGS_ARRAY[-2]}" # 2nd-to-last filtered arg
fi
if [ $array_len -gt 2 ]; then
echo "\
Warning:
This isn't supposed to happen. 'git blametool' parsing may be faulty. Positional arguments
array is longer than expected. Please open an issue here:
https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/issues. Include this output.
array_len = $array_len. POSITIONAL_ARGS_ARRAY contains: '${POSITIONAL_ARGS_ARRAY[*]}'. Your cmd:
$FULL_CMD\
"
fi
# # debugging
# # TEST 1 INPUT:
# # git blametool hey how --are -- -you doing -L l_arg -S s_arg --contents contents_arg -a -b -c
# # TEST 1 EXPECTED RESULTS:
# # last filtered arg = doing
# # 2nd-to-last filtered arg = how
# echo "last filtered arg = $FILE_IN"
# echo "2nd-to-last filtered arg = $COMMIT"
get_commit_hash
}
# Get the short commit hash specified by the user's inputs to `git blame`/`git blametool`.
# This will be used as part of the temporary file name, for instance, to make it obvious for which
# commit the temp file is showing `git blame` output.
get_commit_hash() {
# For the `--pretty=format` options, see: https://stackoverflow.com/a/1441062/4561887. See my
# comments under that answer too.
commit_hash="$(git log -1 --pretty=format:"%h" $COMMIT)"
ret_code="$?"
if [ "$ret_code" -ne "$RETURN_CODE_SUCCESS" ]; then
echo "Warning: 'git blametool' failed to get git commit hash."
commit_hash=""
else
echo "Commit hash = $commit_hash"
fi
}
main() {
mkdir -p "$TEMP_DIR"
# Read the user's settings.
editor="$(git config blametool.editor)"
auto_delete_tempfile_when_done="$(git config blametool.auto-delete-tempfile-when-done)"
if [ -z "$editor" ]; then # see `man test` or `man [` for meaning of `-z`
# If no editor is set by the user, use Sublime Text 3 (subl) as the default
echo "NOTICE: you have set no text editor as your git blametool, so '$DEFAULT_EDITOR'"
echo " ($DEFAULT_EDITOR_DESCRIPTION) will be used by default. To override this setting"
echo " and set your own git blametool editor, call"
echo " 'git config --global blametool.editor [editor-executable]'. Example: "
echo " 'git config --global blametool.editor gedit'."
editor="$DEFAULT_EDITOR"
fi
echo "git blametool editor = '$editor'."
if [ -z "$auto_delete_tempfile_when_done" ]; then
auto_delete_tempfile_when_done="true"
fi
timestamp="$(date "+%Y%m%d-%H%M%S.%3N")"
filename_suffix="$timestamp"
if [ -n "$commit_hash" ]; then
filename_suffix="${filename_suffix}__${commit_hash}"
fi
# See my own answer about `basename`: https://stackoverflow.com/a/60157372/4561887
# - Ex: if `FILE_IN` is "some/path/file.txt", then `FILE_OUT`
# will now be simply "file.txt.git-blame".
FILE_OUT="$(basename "$FILE_IN")__${filename_suffix}.git-blame"
FILE_OUT_FULL_PATH="${TEMP_DIR}/${FILE_OUT}"
echo "Temporary file path: \"$FILE_OUT_FULL_PATH\"."
echo " - Note: filename format is \"filename__YYYYMMDD-HHMMSS.milliseconds__commitHash.git-blame\", where"
echo " YYYYMMDD-HHMMSS.milliseconds is the timestamp of when you ran this script, NOT"
echo " the timestamp of when the commit was created."
echo "Creating temporary file with output from 'git blame'."
git blame "$@" > "$FILE_OUT_FULL_PATH"
# Obtain return code from `git blame`; see: https://stackoverflow.com/a/38533260/4561887
ret_code="$?"
# echo "Return code from 'git blame' = $ret_code" # debugging
# open output in your favorite text editor only if `git blame` is successful
if [ "$ret_code" -eq "$RETURN_CODE_SUCCESS" ]; then
echo "Opening temporary file."
$editor "$FILE_OUT_FULL_PATH"
sleep 0.75 # give the file time to open
fi
if [ "$auto_delete_tempfile_when_done" == "true" ]; then
echo "Deleting temporary file."
rm "$FILE_OUT_FULL_PATH"
fi
}
# --------------------------------------------------------------------------------------------------
# main program entry point
# --------------------------------------------------------------------------------------------------
ALL_ARGS_ARRAY="$@"
FULL_CMD="$0 $ALL_ARGS_ARRAY"
parse_args $ALL_ARGS_ARRAY
main $ALL_ARGS_ARRAY