Skip to content

Commit 13922ab

Browse files
committed
Added gh-summary as file
1 parent 30802b6 commit 13922ab

File tree

1 file changed

+306
-0
lines changed

1 file changed

+306
-0
lines changed

extras/git-summary/git-summary.sh

+306
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
#!/bin/bash
2+
3+
# git-summary - summarize git repos at some path
4+
#
5+
# Forked from https://github.com/lordadamson/git-summary
6+
#
7+
# Freely distributed under the MIT license. 2018@MirkoLedda
8+
9+
set -eu
10+
11+
# Colorcode
12+
GREEN='\e[0;32m'
13+
ORANGE='\e[0;33m'
14+
RED='\e[0;31m'
15+
PURPLE='\e[0;35m'
16+
NC='\e[0m' # No Color
17+
18+
usage() {
19+
sed 's/^ //' <<EOF
20+
git-summary - summarize git repos at some path
21+
22+
Usage: git-summary.sh [-h] [-l] [-d] [-q] [path]
23+
24+
Given a path to a folder containing one or more git repos,
25+
print a status summary table showing, for each repo:
26+
27+
- the folder name
28+
- the currently checked out branch
29+
- a short 2-column status string showing whether there are:
30+
* Local Changes:
31+
- untracked files "?_"
32+
- uncommitted new files "+_"
33+
- uncommitted changes "M_"
34+
- (nothing) " _"
35+
* Remote Changes:
36+
- unpulled commits for the current branch "_v"
37+
- unpushed commits for the current branch "_^"
38+
- (nothing) "_ "
39+
40+
Arguments:
41+
42+
-h Print this message
43+
44+
-l Local operation only. Without this the script runs
45+
"git fetch" in each repo before checking for unpushed/
46+
unpulled commits. As this can be time consuming, this
47+
flag lets you skip that.
48+
49+
-d Deep lookup. Will search within the entire tree of the
50+
current folder.
51+
52+
-q Print nothing for repos that are up to date. Also print
53+
a final tally.
54+
55+
path Path to folder containing git repos; if omitted, the
56+
current working directory is used.
57+
58+
EOF
59+
}
60+
61+
# Main
62+
git_summary() {
63+
64+
detect_OS
65+
detect_Git4Windows
66+
67+
local local_only=0
68+
local opt
69+
local deeplookup=0
70+
local quiet=0
71+
while getopts "hldq" opt; do
72+
case "${opt}" in
73+
h) usage ; exit 1 ;;
74+
l) local_only=1 ;; # Will skip "git fetch"
75+
d) deeplookup=1 ;;
76+
q) quiet=1 ;;
77+
esac
78+
done
79+
shift $((OPTIND-1))
80+
81+
# Use provided path, or default to pwd
82+
local target=$(${readlink_cmd} -f ${1:-`pwd`})
83+
local repos=$(list_repos $target $deeplookup)
84+
85+
if [[ -z $repos ]]; then
86+
exit
87+
fi
88+
89+
# We compute the repo names and branch names here so we can
90+
# compute their maximum lengths and lay things out nicely. This
91+
# can all be done much more easily via the column(1) utility, but
92+
# that has to consume all its input before it can write anything
93+
# out, which isn't great when you're running "git fetch" on a
94+
# whole bunch of repos. Doing it like this allows us to write the
95+
# output to stdout incrementally.
96+
97+
local branches=$(repo_branches $target)
98+
local max_repo_len=$(max_len "$repos")
99+
local max_branch_len=$(max_len "$branches")
100+
local template=$(printf "%%b%%-%ds %%-%ds %%-5s" $max_repo_len $max_branch_len)
101+
print_header "$template" $max_repo_len $max_branch_len
102+
103+
local repo_count=0
104+
105+
local f
106+
for f in $repos ; do
107+
summarize_one_git_repo $f "$template" "$local_only" "$quiet" >&1 &
108+
(( repo_count+=1 ))
109+
done
110+
wait
111+
112+
if [ $quiet -eq 1 ]; then
113+
echo "Checked ${repo_count} repositories."
114+
fi
115+
116+
}
117+
118+
# Autodetect the OS
119+
detect_OS() {
120+
if [ "$(uname)" == "Darwin" ]; then # macOS
121+
OS=Darwin
122+
readlink_cmd="greadlink"
123+
dirname_cmd="gdirname"
124+
gawk_cmd="awk"
125+
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then # Linux
126+
OS=Linux
127+
readlink_cmd="readlink"
128+
dirname_cmd="dirname"
129+
gawk_cmd="gawk"
130+
elif [ "$(expr substr $(uname -s) 1 6)" == "CYGWIN" ]; then # Cygwin
131+
OS=CYGWIN
132+
readlink_cmd="readlink"
133+
dirname_cmd="dirname"
134+
gawk_cmd="gawk"
135+
else
136+
echo "Cannot identify OS."
137+
exit 1
138+
fi
139+
}
140+
141+
GIT4WINDOWS=1
142+
143+
detect_Git4Windows() {
144+
if [[ "$OS" == "CYGWIN" && "$(git --version)" == *"windows"* ]]; then
145+
GIT4WINDOWS=0
146+
fi
147+
}
148+
149+
gitC() {
150+
local ldir=$1; shift;
151+
if [ $GIT4WINDOWS -eq 0 ]; then
152+
git -C "$(cygpath -w $ldir)" "$@"
153+
else
154+
git -C "$ldir" "$@"
155+
fi
156+
}
157+
158+
159+
print_header () {
160+
local template="$1"
161+
local max_repo_len=$2
162+
local max_branch_len=$3
163+
print_divider () {
164+
printf '=%.0s' $(seq 1 $max_repo_len)
165+
printf ' '
166+
printf '=%.0s' $(seq 1 $max_branch_len)
167+
printf ' '
168+
printf '=%.0s' $(seq 1 5)
169+
printf '\n'
170+
};
171+
172+
echo
173+
printf "$template\n" $NC Repository Branch State
174+
print_divider
175+
}
176+
177+
178+
summarize_one_git_repo () {
179+
180+
local f=$1
181+
local template=$2
182+
local local_only=$3
183+
local quiet=$4
184+
185+
local app_name=$f
186+
local branch_name=`gitC $f symbolic-ref HEAD | sed -e "s/^refs\/heads\///"`
187+
local numState=0
188+
189+
### Check remote state
190+
local rstate=""
191+
local has_upstream=`gitC $f rev-parse --abbrev-ref @{u} 2> /dev/null | wc -l`
192+
if [ $has_upstream -ne 0 ] ; then
193+
if [ $local_only -eq 0 ] ; then
194+
gitC $f fetch -q &> /dev/null
195+
fi
196+
# Unpulled and unpushed on *current* branch
197+
local unpulled=`gitC $f log --pretty=format:'%h' ..@{u} | wc -c`
198+
local unpushed=`gitC $f log --pretty=format:'%h' @{u}.. | wc -c`
199+
200+
if [ $unpulled -ne 0 ]; then
201+
rstate="${rstate}v"
202+
numState=1
203+
else
204+
rstate="${rstate} "
205+
fi
206+
207+
if [ $unpushed -ne 0 ]; then
208+
rstate="${rstate}^"
209+
numState=1
210+
else
211+
rstate="${rstate} "
212+
fi
213+
214+
else
215+
rstate="--"
216+
fi
217+
218+
### Check local state
219+
local state=""
220+
local untracked=`LC_ALL=C gitC $f status | grep Untracked -c`
221+
local new_files=`LC_ALL=C gitC $f status | grep "new file" -c`
222+
local modified=`LC_ALL=C gitC $f status | grep modified -c`
223+
224+
if [ $untracked -ne 0 ]; then
225+
state="${state}?"
226+
numState=2
227+
else
228+
state="${state} "
229+
fi
230+
231+
if [ $new_files -ne 0 ]; then
232+
state="${state}+"
233+
numState=2
234+
else
235+
state="${state} "
236+
fi
237+
238+
if [ $modified -ne 0 ]; then
239+
state="${state}M"
240+
numState=2
241+
else
242+
state="${state} "
243+
fi
244+
245+
### Print to stdout
246+
if [ $numState -eq 0 ]; then
247+
if [ $quiet -eq 0 ]; then
248+
printf "$template\n" $GREEN $app_name $branch_name "$state$rstate" >&1
249+
fi
250+
elif [ $numState -eq 1 ]; then
251+
printf "$template\n" $ORANGE $app_name $branch_name "$state$rstate" >&1
252+
elif [ $numState -eq 2 ]; then
253+
printf "$template\n" $RED $app_name $branch_name "$state$rstate" >&1
254+
fi
255+
}
256+
257+
258+
# Given the path to a git repo, compute its current branch name.
259+
repo_branch () {
260+
gitC "$1" symbolic-ref HEAD | sed -e "s/^refs\/heads\///"
261+
}
262+
263+
264+
# Given a path to a folder containing some git repos, compute the
265+
# names of the folders which actually do contain git repos.
266+
list_repos () {
267+
# https://stackoverflow.com/questions/23356779/how-can-i-store-find-command-result-as-arrays-in-bash
268+
git_directories=()
269+
270+
local find_cmd
271+
if [ $deeplookup -eq 0 ]; then
272+
find_cmd="find -L $1 -maxdepth 2 -type d -name .git -print0"
273+
else
274+
find_cmd="find -L $1 -type d -name .git -print0"
275+
fi
276+
277+
while IFS= read -r -d $'\0'; do
278+
git_directories+=("$REPLY")
279+
done < <($find_cmd 2>/dev/null)
280+
281+
for i in ${git_directories[*]}; do
282+
if [[ ! -z $i ]]; then
283+
$dirname_cmd -z $i | xargs -0 -L1
284+
fi
285+
done
286+
}
287+
288+
289+
# Given the path to a folder containing git some repos, compute the
290+
# names of the current branches in the repos.
291+
repo_branches () {
292+
local path=$1
293+
local repo
294+
for repo in $(list_repos $path) ; do
295+
echo $(repo_branch $repo)
296+
done
297+
}
298+
299+
300+
max_len () {
301+
echo "$1" | $gawk_cmd '{ print length }' | sort -rn | head -1
302+
}
303+
304+
trap "printf '$NC'" EXIT
305+
306+
git_summary $@

0 commit comments

Comments
 (0)