-
Notifications
You must be signed in to change notification settings - Fork 43
/
Copy pathgit-graph-dag
executable file
·169 lines (145 loc) · 4.33 KB
/
git-graph-dag
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
#!/usr/bin/env sh
# # Draw a graphviz diagram of the Git DAG
#
# Labels consist of the short SHA1 and any refs.
# Unreachable commits (ignoring the reflog) will be drawn with dashed lines.
#
# ## Usage
#
# `git graph-dag [<flags>] [<git refs>] (-- [git rev-list args])`
#
# Flag | Description
# ---- | -----------
# -h | Show this screen.
# -c | Also output commit message (must be short!).
# -w | Watch mode (see below).
# -- | Pass any args through to git rev-list.
#
# ## Examples
#
# git graph-dag HEAD~10..
# git graph-dag -c HEAD~10..
# git graph-dag HEAD~10.. abcdefa
# git graph-dag HEAD~10.. abcdefa abcdefb
# git graph-dag -c -- --reflog --all
# git graph-dag -w
#
# # Linux & Windows (WSL) w/ ImageMagick
# git graph-dag HEAD~10.. | dot -Tpng | display -antialias
#
# # OS X
# git graph-dag HEAD~10.. | dot -Tpng | open -a Preview.app -f
#
# ## Watch mode
#
# Install (then remove) a Git hook that will update a PNG image on every ref
# change and start the `display` ImageMagick viewer that will watch that file
# for changes. This is an easy and simple way to view updates to the DAG in
# real-time as they happen (useful for live demos when teaching Git classes).
# E.g.:
#
# Terminal 1:
#
# git init /path/to/foo
# cd /path/to/foo
# git graph-dag -w
#
# Terminal 2:
#
# cd /path/to/foo
# touch A; git add A; git commit -m 'Add-A'
# touch B; git add B; git commit -m 'Add-B'
# git reset --hard HEAD~1
# touch C; git add C; git commit -m 'Add-C'
# git commit --amend -o -m 'Add-C-2'
HOOKSCRIPT='.git/hooks/reference-transaction'
GITDIR="$(git rev-parse --absolute-git-dir)"
test $? -eq 0 || exit 1
IMG="${GITDIR}/graph-dag.png"
run_watch() {
command -v dot 1>/dev/null || \
{ printf 'Graphviz required.\n' 1>&2; exit 1; }
if [ -e "$HOOKSCRIPT" ]; then
printf 'Existing hook found. Not overwriting!\n' 1>&2
exit 1
fi
trap '
excode=$?; trap - EXIT;
rm -rf -- '"$HOOKSCRIPT"'
exit $excode
' INT TERM EXIT
cat >> "$HOOKSCRIPT" <<-EOS
#!/usr/bin/env sh
git graph-dag -c -- --reflog --all | dot -Tpng > "$IMG"
EOS
chmod +x "$HOOKSCRIPT"
# Look for existing commits:
git rev-list --quiet --all -n 1 HEAD 2>/dev/null
if [ $? -eq 0 ]; then
git graph-dag -c -- --reflog --all | dot -Tpng > "$IMG"
else
# Make blank 200x200 PNG placeholder.
cat <<-'EOF' | base64 -d | gzip -d > "$IMG"
H4sICHoQAWQAA2dyYXBoLWRhZy5wbmcA6wzwc+flkuJiYGDg9fRwCQLSJ0CYgw1Iro1YN4+BgbHV
08UxpOLW28uMvAwMHMwKb+ffvMukVnCh0U+NdU6UgAALC6OjA4JC57MIogswovMdGQjwHQQI8cmz
l6BDyLEYwyFk2Ysi0Ngvti1Qxu3VLD1tYLwweLr6uaxzSmgCAISVGHO+AQAA
EOF
fi
uname -s | grep -q Darwin
if [ $? -eq 0 ]; then
open -W -g -a Preview.app "$IMG"
else
command -v display 1>/dev/null || \
{ printf 'ImageMagick required.\n' 1>&2; exit 1; }
display -update 1 -antialias "$IMG"
fi
exit
}
main() {
while getopts chw opt; do
case $opt in
c) show_msg=1;;
h) show_help=1;;
w) run_watch;;
esac
done
shift $(( OPTIND - 1 ))
if [ $# -eq 0 ] || [ -n "$show_help" ] ; then
awk 'NR == 1 { next } /^$/ { exit } { print substr($0, 3) }' "$0"
exit 1
fi
{
git fsck --unreachable --no-reflogs --no-progress \
| awk '/commit/ { print $3 }'
printf 'XXX\n'
git rev-list --no-commit-header --pretty=format:'%H|%h|%p|%D|%f' "$@" ;
} |
awk -v show_msg="$show_msg" '
BEGIN {
FS="|"
print "digraph lattice {"
}
/^XXX$/ { revlist = 1; next }
!revlist { unreachable[$1] = 1 }
revlist {
hashfull = $1
hash = $2
split($3, parents, " ")
refs = $4
msg = $5
sub(" -> ", " -\\> ", refs)
unreachable[hashfull] == 1 ? isunr = 1 : isunr = 0
for (p in parents) {
printf("x%s -> x%s\n", hash, parents[p])
}
printf("x%s [shape=Mrecord, style=%s, label=<%s%s%s>]\n",
hash, \
isunr == 1 ? "dashed" : "filled", \
"<b>" hash "</b>",
show_msg == 1 ? " <font color=\"gray20\">" msg "</font>" : "", \
refs == "" ? "" : "<br/><font color=\"gray20\" point-size=\"10.0\">" refs "</font>")
}
END { printf("}\n") }
'
}
main "$@"