-
Notifications
You must be signed in to change notification settings - Fork 32
/
gitup
executable file
·126 lines (107 loc) · 2.61 KB
/
gitup
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
#!/usr/bin/env bash
declare -A colors
colors[red]=$(tput setaf 1)
colors[green]=$(tput setaf 2)
colors[blue]=$(tput setaf 4)
colors[reset]=$(tput sgr0)
declare processes=4
declare quiet
declare force
declare -a dirs
declare -a ignore_dir
declare -a ignore_dirs
declare -a errs
usage() {
LESS=-FEXR less <<'HELP'
gitup [OPTIONS] [dirs]
search for git repos and update them
if unspecified, dir defaults to $HOME
-i [dir] comma separated list of directory paths to not search
-p [number] how many processes to run `git pull` in parallel
-q quiet level, may be stacked
first level suppresses output from `git pull`
second level suppresses job info
-F don't run interactively, `git pull` all dirs
use with caution, make sure you know which dirs will be matched
works best if gitup is provided a list of dirs known to have git repos
-h print this help
HELP
}
color() {
local c
c="$1"
shift
printf '%s' "${colors[$c]}"
printf '%s\n' "$@"
printf '%s' "${colors[reset]}"
}
err() {
color red "$@" >&2
}
die() {
[[ -n "$1" ]] && err "$1"
exit 1
}
has() {
local verbose
if [[ $1 = '-v' ]]; then
verbose=1
shift
fi
for c; do c="${c%% *}"
if ! command -v "$c" &> /dev/null; then
(( "$verbose" > 0 )) && err "$c not found"
return 1
fi
done
}
has -v fzf git || die
while getopts ':hqp:i:F' x; do o="$OPTARG"
case "$x" in
h) usage; exit; ;;
p) processes="$o" ;;
q) (( ++quiet )) ;;
i) IFS=',' read -ra ignore_dir <<< "$o" ;;
F) (( ++force )) ;;
esac
done
shift $(( OPTIND - 1 ))
while :; do
if [[ -d "$1" ]]; then
dirs+=( "$1" )
fi
shift || break
done
for o in "${ignore_dir[@]}"; do
ignore_dirs+=( -path "*/$o" -o )
done
(( ${#dirs[@]} > 0 )) || dirs=("$HOME")
mapfile -t repos < <(find "${dirs[@]}" \
\( "${ignore_dirs[@]}" \
-fstype 'devfs' \
-o -fstype 'devtmpfs' \
-o -fstype 'proc' \
\) -prune -o -name '.git' -printf '%h\n' 2> /dev/null |
fzf --multi --cycle --inline-info +s -e ${force:+-f /})
(( ${#repos[@]} > 0 )) || exit
update() {
local name dir
dir="$1"
name="${dir##*/}"
(( quiet > 1 )) || color blue ":: updating $name"
if git -C "$dir" pull ${quiet:+-q}; then
(( quiet > 1 )) || color green ":: updated $name"
else
errs+=( "$name" )
fi
}
for d in "${repos[@]}"; do
(( count++ >= processes )) && wait -n
update "$d" &
done
wait
if (( "${#errs[@]}" > 0 )); then
color red 'The following packages failed to update:'
color red " ${errs[*]}"
fi
color green "updated ${#repos[@]} repos"