-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgit_status.env
578 lines (513 loc) · 15.1 KB
/
git_status.env
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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
#!/bin/bash
#
# Copyright (c) 2016
# Author: Victor Arribas <[email protected]>
# License: GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
## Usage:
# source git_status.env [-ps1 [--no-color]|-revert]
#
# Invoke git_status provides you a BASH function called
# `git_status` that holds functionality
# Additional args:
# -ps1 - modifies PS1 to provide repo status info
# --no-color - plain text mode.
# Character '*' hints about uncommited files.
# -revert - revert PS1 variable to original state
## git_status
# gives status info of every git repositories in current directory tree
# repositories are alphabetically processed
# expected to be used in frameworks or workspaces with several projects
function git_status
{
# help
if [ "$1" = "--help" ]
then
cat<<EOF
git_status
Copyright (c) 2016
Author: Victor Arribas <[email protected]>
License: GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
gives status info of every git repository in current directory tree.
repositories are alphabetically processed for better reading.
expected to be used in frameworks or workspaces with several projects.
You can expect follow output format:
[*source dir* @ *head info*]
*git status --short*
*new line*
Explain:
source dir - git's root directory
head info - git HEAD status (like __git_ps1)
color feedback is commit status:
* green - nothing to commit
* red - uncommited files (dirty workspace)
Usage: git_status [--no-color | --color] [--short | --long | --full]
--no-color - disable echoing colors. '*' means dirty workspace
--color - enable echoing colors (default)
--short - prints git repo name
--long - prints dir relative to \$PWD (default)
--full - prints absolute directory to git repo
EOF
return
fi
for i in $#
do
case $1 in
'--color')
__git_status_color_enabled=1
;;
'--no-color')
__git_status_color_enabled=0
;;
'--short')
__git_status_dir_mode=0
;;
'--long')
__git_status_dir_mode=1
;;
'--full')
__git_status_dir_mode=2
;;
esac
shift
done
_pwd="$(pwd)"
# find every git repos
# .git directory == git repo
# .git file == git submodule redirection
find -H "$_pwd" -name '\.git' 2>/dev/null | sort | while read repo
do
repo_dir=$(dirname "$repo")
cd "$repo_dir"
# Color switcher
if [ ${__git_status_color_enabled-1} -eq 1 ]
then
local GIT_STATUS=$(__git_ps1_custom_color 'color-simple' $EXTRA_COMMAND)
else
local GIT_STATUS=$(__git_ps1_custom $EXTRA_COMMAND)
local color_toggle='--porcelain'
fi
# Directory mode switcher
case ${__git_status_dir_mode-1} in
0)
echo -e "$GIT_STATUS"
;;
1)
local short=$(basename "$repo_dir")
local long=${repo_dir#$_pwd}
if test -z "$long" # empty
then
# pwd directory is a git directory. No substitution
echo -e "$GIT_STATUS"
else
# deeper directory (remove trail / of pwd)
local long=${long#/}
echo -e "$GIT_STATUS" | sed "s:$short:$long:"
fi
;;
2)
local short=$(basename "$repo_dir")
local long=$repo_dir
echo -e "$GIT_STATUS" | sed "s:$short:$long:"
;;
esac
# Print status
if test -n "$(git status --short --porcelain)"
then
git status --short $color_toggle
else
#git status
echo "nothing to commit, working directory clean"
fi
echo
cd "$_pwd"
done
#unset GIT_DIR
}
## __git_status_main
# main run
function __git_status_main
{
# help
if [ "$1" = "--help" ]
then
cat<<EOF
git_status
Copyright (c) 2016
Author: Victor Arribas <[email protected]>
License: GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
Invoke git_status provides you a BASH function called *git_status*
that holds functionality. You can expect follow output format:
[<source dir> @ <head info>]
source dir - git's root directory
head info - git HEAD status (like __git_ps1)
color feedback is commit status:
* green - nothing to commit
* red - uncommited files (dirty workspace)
Usage:
source git_status.env [-ps1 [--no-color]|-revert]
-ps1 - modifies PS1 to provide repo status info.
This arg can be passed as many times as you
wish. Script ensures idempotence to simplify
*source <whatever>* logic.
--no-color - plain text mode.
Character '*' hints about uncommited files.
Modifies PS1 in any case.
-revert - revert PS1 variable to original state.
EOF
fi
# inject into PS1
if [ "$1" = "-ps1" ]
then
## Update: injection based on PROMPT_COMMAND
# instead of PS1 already ensures idempotence
# because we do not need to control append over
# PS1.
# Old rationale ramains for historical purposes:
## PS1 must be only staged once
# this allow idempotence and simplifies usage
# Reasoning: if __PS1_PREGIT is not defined suspect that PS1 is untouched
# therefore, PS1 is only injected once
# exists also scenario where PS1 has been overwrite and one can re-inject
# lost PS1 fields; this can be done with `-f` extra arg
## Choose between colorized and not colorized
if [ "$2" = "--no-color" ]
then
__git_ps1_command=__git_ps1_custom
else
__git_ps1_command=__git_ps1_custom_color
fi
## Idempotence obtained over existence of constant
# value __PS1_PREGIT
# If __PS1_PREGIT stores old/deprecated PS1, you
# must do -revert and -ps1 or update it.
if [ "${__PS1_PREGIT-x}" = "x" ]
then
__PS1_PREGIT=$PS1
__PROMPT_COMMAND=$PROMPT_COMMAND
PROMPT_COMMAND=__git_ps1_command_prompt
export PROMPT_COMMAND
fi
fi
# revert PS1 variable value
if [ "$1" = "-revert" ]
then
PS1=${__PS1_PREGIT-$PS1}
export PS1
unset __PS1_PREGIT
PROMPT_COMMAND=${__PROMPT_COMMAND-$PROMPT_COMMAND}
unset __PROMPT_COMMAND
export PROMPT_COMMAND
fi
}
# run and purge main()
__git_status_main $@
unset __git_status_main
## __git_ps1_command_prompt
# Reference:
# [1] http://stackoverflow.com/questions/6592077/bash-prompt-and-echoing-colors-inside-a-function
# [2] http://unix.stackexchange.com/questions/207941/custom-bash-prompt-cursor-positioning-issue
# Rationale: color definition and any other PS1 non-printable commands MUST
# be at PS1 and should not be defined and/or echoed by a function.
# Any above sequences not parsed directly at PS1 will break cursor
# positioning (as explained at [2]).
# Therefore, if this kind of tweak is required, ONLY functional and correct
# way to do it is through PROMPT_COMMAND [1]
# Remember that colors must be wrapped with \[ + \] to tell bash about non
# printable chraracters.
__git_ps1_command_prompt()
{
local PS1_GIT=$($__git_ps1_command 'color-ps1')
# suffix
#PS1=$(echo $__PS1_PREGIT | sed 's,\\\$[ ]*,,')
#PS1="$PS1 $PS1_GIT"
#PS1="$PS1\$ "
# prefix
PS1="$PS1_GIT$__PS1_PREGIT"
}
## __git_ps1_custom
# like __git_ps1 but preceded by git's checkout root directory
# output format is [<source dir> @ <head info>]
__git_ps1_custom()
{
__git_test_inside_work_tree || return
if test -n "$(git status --porcelain)"
then
local status='*'
else
local status=''
fi
repo_name=$(basename "$(git rev-parse --show-toplevel)")
echo -n "[$repo_name @ $(__git_ps1 "%s")$status]"
}
## __git_ps1_custom_color
# colorized version of __git_ps1_custom
__git_ps1_custom_color()
{
__git_test_inside_work_tree || return
case $1 in
color-simple)
local c_red='\e[31m'
local c_green='\e[32m'
local c_lblue='\e[1;34m' # used for directories (like ls)
local c_clear='\e[0m'
;;
color-ps1|*)
local c_red='\[\e[31m\]'
local c_green='\[\e[32m\]'
local c_lblue='\[\e[1;34m\]' # used for directories (like ls)
local c_clear='\[\e[0m\]'
;;
esac
if test -n "$(git status --porcelain)"
then
local c_status=$c_red
else
local c_status=$c_green
fi
repo_name=$(basename "$(git rev-parse --show-toplevel)")
echo -n "[$c_lblue$repo_name$c_clear @ $c_status$(__git_ps1 "%s")$c_clear]"
}
## __git_test_inside_work_tree
# native way to check if we should run `__git_ps1`
# requires git >= 1.6.0
__git_test_inside_work_tree(){
test "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)"
return $?
}
### git-prompt.sh ###
# 2016-01-12 + changes
# https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh
#
# Copyright (C) 2006,2007 Shawn O. Pearce <[email protected]>
# Distributed under the GNU General Public License, version 2.0.
# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
# when called from PS1 using command substitution
# in this mode it prints text to add to bash PS1 prompt (includes branch name)
#
# __git_ps1 requires 2 or 3 arguments when called from PROMPT_COMMAND (pc)
# in that case it _sets_ PS1. The arguments are parts of a PS1 string.
# when two arguments are given, the first is prepended and the second appended
# to the state string when assigned to PS1.
# The optional third parameter will be used as printf format string to further
# customize the output of the git-status string.
# In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true
__git_ps1 ()
{
# preserve exit status
local exit=$?
local pcmode=no
local detached=no
local ps1pc_start='\u@\h:\w '
local ps1pc_end='\$ '
local printf_format=' (%s)'
case "$#" in
2|3) pcmode=yes
ps1pc_start="$1"
ps1pc_end="$2"
printf_format="${3:-$printf_format}"
# set PS1 to a plain prompt so that we can
# simply return early if the prompt should not
# be decorated
PS1="$ps1pc_start$ps1pc_end"
;;
0|1) printf_format="${1:-$printf_format}"
;;
*) return $exit
;;
esac
# ps1_expanded: This variable is set to 'yes' if the shell
# subjects the value of PS1 to parameter expansion:
#
# * bash does unless the promptvars option is disabled
# * zsh does not unless the PROMPT_SUBST option is set
# * POSIX shells always do
#
# If the shell would expand the contents of PS1 when drawing
# the prompt, a raw ref name must not be included in PS1.
# This protects the user from arbitrary code execution via
# specially crafted ref names. For example, a ref named
# 'refs/heads/$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' might cause the
# shell to execute 'sudo rm -rf /' when the prompt is drawn.
#
# Instead, the ref name should be placed in a separate global
# variable (in the __git_ps1_* namespace to avoid colliding
# with the user's environment) and that variable should be
# referenced from PS1. For example:
#
# __git_ps1_foo=$(do_something_to_get_ref_name)
# PS1="...stuff...\${__git_ps1_foo}...stuff..."
#
# If the shell does not expand the contents of PS1, the raw
# ref name must be included in PS1.
#
# The value of this variable is only relevant when in pcmode.
#
# Assume that the shell follows the POSIX specification and
# expands PS1 unless determined otherwise. (This is more
# likely to be correct if the user has a non-bash, non-zsh
# shell and safer than the alternative if the assumption is
# incorrect.)
#
local ps1_expanded=yes
[ -z "$ZSH_VERSION" ] || [[ -o PROMPT_SUBST ]] || ps1_expanded=no
[ -z "$BASH_VERSION" ] || shopt -q promptvars || ps1_expanded=no
local repo_info rev_parse_exit_code
repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
--is-bare-repository --is-inside-work-tree \
--short HEAD 2>/dev/null)"
rev_parse_exit_code="$?"
if [ -z "$repo_info" ]; then
return $exit
fi
local short_sha
if [ "$rev_parse_exit_code" = "0" ]; then
short_sha="${repo_info##*$'\n'}"
repo_info="${repo_info%$'\n'*}"
fi
local inside_worktree="${repo_info##*$'\n'}"
repo_info="${repo_info%$'\n'*}"
local bare_repo="${repo_info##*$'\n'}"
repo_info="${repo_info%$'\n'*}"
local inside_gitdir="${repo_info##*$'\n'}"
local g="${repo_info%$'\n'*}"
if [ "true" = "$inside_worktree" ] &&
[ -n "${GIT_PS1_HIDE_IF_PWD_IGNORED-}" ] &&
[ "$(git config --bool bash.hideIfPwdIgnored)" != "false" ] &&
git check-ignore -q .
then
return $exit
fi
local r=""
local b=""
local step=""
local total=""
if [ -d "$g/rebase-merge" ]; then
__git_eread "$g/rebase-merge/head-name" b
__git_eread "$g/rebase-merge/msgnum" step
__git_eread "$g/rebase-merge/end" total
if [ -f "$g/rebase-merge/interactive" ]; then
r="|REBASE-i"
else
r="|REBASE-m"
fi
else
if [ -d "$g/rebase-apply" ]; then
__git_eread "$g/rebase-apply/next" step
__git_eread "$g/rebase-apply/last" total
if [ -f "$g/rebase-apply/rebasing" ]; then
__git_eread "$g/rebase-apply/head-name" b
r="|REBASE"
elif [ -f "$g/rebase-apply/applying" ]; then
r="|AM"
else
r="|AM/REBASE"
fi
elif [ -f "$g/MERGE_HEAD" ]; then
r="|MERGING"
elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
r="|CHERRY-PICKING"
elif [ -f "$g/REVERT_HEAD" ]; then
r="|REVERTING"
elif [ -f "$g/BISECT_LOG" ]; then
r="|BISECTING"
fi
if [ -n "$b" ]; then
:
elif [ -h "$g/HEAD" ]; then
# symlink symbolic ref
b="$(git symbolic-ref HEAD 2>/dev/null)"
else
local head=""
if ! __git_eread "$g/HEAD" head; then
return $exit
fi
# is it a symbolic ref?
b="${head#ref: }"
if [ "$head" = "$b" ]; then
detached=yes
b="$(
case "${GIT_PS1_DESCRIBE_STYLE-}" in
(contains)
git describe --contains HEAD ;;
(branch)
git describe --contains --all HEAD ;;
(describe)
git describe HEAD ;;
(* | default)
git describe --tags --exact-match HEAD ;;
esac 2>/dev/null)" ||
b="$short_sha..."
b="($b)"
fi
fi
fi
if [ -n "$step" ] && [ -n "$total" ]; then
r="$r $step/$total"
fi
local w=""
local i=""
local s=""
local u=""
local c=""
local p=""
if [ "true" = "$inside_gitdir" ]; then
if [ "true" = "$bare_repo" ]; then
c="BARE:"
else
b="GIT_DIR!"
fi
elif [ "true" = "$inside_worktree" ]; then
if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
[ "$(git config --bool bash.showDirtyState)" != "false" ]
then
git diff --no-ext-diff --quiet || w="*"
git diff --no-ext-diff --cached --quiet || i="+"
if [ -z "$short_sha" ] && [ -z "$i" ]; then
i="#"
fi
fi
if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
git rev-parse --verify --quiet refs/stash >/dev/null
then
s="$"
fi
if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
[ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- ':/*' >/dev/null 2>/dev/null
then
u="%${ZSH_VERSION+%}"
fi
if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
__git_ps1_show_upstream
fi
fi
local z="${GIT_PS1_STATESEPARATOR-" "}"
# NO color option unless in PROMPT_COMMAND mode
if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
__git_ps1_colorize_gitstring
fi
b=${b##refs/heads/}
if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
__git_ps1_branch_name=$b
b="\${__git_ps1_branch_name}"
fi
local f="$w$i$s$u"
local gitstring="$c$b${f:+$z$f}$r$p"
if [ $pcmode = yes ]; then
if [ "${__git_printf_supports_v-}" != yes ]; then
gitstring=$(printf -- "$printf_format" "$gitstring")
else
printf -v gitstring -- "$printf_format" "$gitstring"
fi
PS1="$ps1pc_start$gitstring$ps1pc_end"
else
printf -- "$printf_format" "$gitstring"
fi
return $exit
}
__git_eread ()
{
local f="$1"
shift
test -r "$f" && read "$@" <"$f"
}