t2100: add some --no-commit tests
[topgit/pro.git] / tg-info.sh
blob4ec2c0d79993450a74baee594b0193ebaab417fd
1 #!/bin/sh
2 # TopGit - A different patch queue manager
3 # Copyright (C) Petr Baudis <pasky@suse.cz> 2008
4 # Copyright (C) Kyle J. McKay <mackyle@gmail.com> 2015, 2016, 2017
5 # GPLv2
7 USAGE="\
8 Usage: ${tgname:-tg} [...] info [--heads | --leaves | --series[=<head>]] [<name>]
9 Or: ${tgname:-tg} [...] info [--deps | --dependencies | --dependents] [<name>]"
11 usage()
13 if [ "${1:-0}" != 0 ]; then
14 printf '%s\n' "$USAGE" >&2
15 else
16 printf '%s\n' "$USAGE"
18 exit ${1:-0}
21 ## Parse options
23 datamode=
24 heads=
25 leaves=
26 deps=
27 dependents=
28 series=
29 serieshead=
30 verbose=
32 while [ $# -gt 0 ]; do case "$1" in
33 -h|--help)
34 usage
36 --heads)
37 heads=1 datamode=1
39 --leaves)
40 leaves=1 datamode=1
42 --deps|--dependencies)
43 deps=1 datamode=1
45 --dependents)
46 dependents=1 datamode=1
48 --series)
49 series=1 datamode=1
51 --series=*)
52 series=1 datamode=1
53 serieshead="${1#--series=}"
55 -v|--verbose)
56 verbose=$(( ${verbose:-0} + 1 ))
58 -vv|-vvv|-vvvv|-vvvvv)
59 verbose=$(( ${verbose:-0} + ${#1} - 1 ))
61 -?*)
62 echo "Unknown option: $1" >&2
63 usage 1
66 break
68 esac; shift; done
69 [ -z "$datamode" ] || [ "$heads$leaves$deps$dependents$series" = "1" ] ||
70 die "mutually exclusive options: --series --deps --heads --leaves --dependents"
71 [ $# -gt 0 ] || set -- HEAD
72 [ $# -eq 1 ] || die "name already specified ($1)"
73 name="$1"
75 process_dep()
77 if [ -n "$_dep_is_tgish" ] && [ -z "$_dep_missing$_dep_annihilated" ]; then
78 printf '%s\n' "$_dep ${_depchain##* }"
82 if [ -n "$heads" ]; then
83 no_remotes=1
84 base_remote=
85 verify="$name"
86 v_verify_topgit_branch tgbranch "${name:-HEAD}" -f || tgbranch=
87 [ -n "$tgbranch" ] || tgbranch="$(git cat-file blob HEAD:.topdeps 2>/dev/null | paste -d ' ' -s -)" || :
88 if [ -n "$tgbranch" ]; then
89 # faster version with known TopGit branch name(s)
90 navigate_deps -s=-1 -1 -- "$tgbranch" | sort
91 exit 0
93 hash="$(git rev-parse --verify --quiet "$verify^0" --)" || die "no such commit-ish: $name"
94 depslist="$(get_temp depslist)"
95 tg summary --topgit-heads |
96 while read -r onetghead; do
97 printf '%s %s\n' "$onetghead" "$onetghead"
98 recurse_deps process_dep "$onetghead"
99 done | sort -u >"$depslist"
100 git branch --no-color --contains "$hash" | cut -c 3- |
101 join -o 2.2 - "$depslist" |
102 sort -u
103 exit 0
106 v_cntargs() { eval "$1=$(( $# - 1 ))"; }
107 if [ -n "$series" ]; then
108 if [ -z "$serieshead" ]; then
109 v_verify_topgit_branch name "${name:-HEAD}"
110 heads="$(navigate_deps -s=-1 -1 -- "$name" | sort | paste -d ' ' -s -)" || heads="$name"
111 v_cntargs headcnt $heads
112 if [ "$headcnt" -gt 1 ]; then
113 err "multiple heads found"
114 info "use the --series=<head> option on one of them:" >&2
115 for ahead in $heads; do
116 info "$tab$ahead" >&2
117 done
118 die "--series requires exactly one head"
120 [ "$headcnt" = 1 ] || die "programmer bug"
121 serieshead="$heads"
122 else
123 v_verify_topgit_branch serieshead "$serieshead"
124 v_verify_topgit_branch name "${name:-HEAD}" -f || name=
126 seriesf="$(get_temp series)"
127 recurse_deps_internal --series -- "$serieshead" | awk '{print $0 " " NR}' | sort >"$seriesf"
128 refslist=
129 [ -z "$tg_read_only" ] || [ -z "$tg_ref_cache" ] || ! [ -s "$tg_ref_cache" ] ||
130 refslist="-r=\"$tg_ref_cache\""
131 flagname=
132 [ -z "$name" ] || [ "$serieshead" = "$name" ] || flagname="$name"
133 output() {
134 eval run_awk_topgit_msg -n -nokind "$refslist" '"refs/$topbases"' |
135 join "$seriesf" - | sort -k2,2n | awk -v "flag=$flagname" '
137 bn = $1
138 mark = ""
139 if (flag != "") mark = (bn == flag) ? "* " : " "
140 bn = mark bn
141 desc = $0
142 sub(/^[^ ]+ [^ ]+ /, "", desc)
143 printf "%-39s\t%s\n", bn, desc
146 } && page output
147 exit 0
150 v_verify_topgit_branch name "${name:-HEAD}"
152 if [ -n "$leaves" ]; then
153 find_leaves "$name"
154 exit 0
157 if [ -n "$deps$dependents" ]; then
158 alldeps="$(get_temp alldeps)"
159 tg --no-pager summary --deps >"$alldeps" || die "tg summary --deps failed"
160 if [ -n "$deps" ]; then
161 awk -v annb="$name" 'NF == 2 && $2 != "" && $1 == annb { print $2 }' <"$alldeps"
162 else
163 awk -v annb="$name" 'NF == 2 && $1 != "" && $2 == annb { print $1 }' <"$alldeps"
165 exit 0
168 base_rev="$(git rev-parse --short --verify "refs/$topbases/$name^0" -- 2>/dev/null)" ||
169 die "not a TopGit-controlled branch"
171 measure="$(measure_branch "refs/heads/$name" "$base_rev")"
173 echo "Topic Branch: $name ($measure)"
175 nocommits=
176 [ "$(git rev-parse --verify --short "refs/heads/$name" --)" != "$base_rev" ] || nocommits=1
178 [ -n "$nocommits" ] || git cat-file blob "$name:.topmsg" 2>/dev/null | grep ^Subject: || :
180 if [ "${verbose:-0}" -ge 1 ]; then
181 scratch="$(get_temp scratch)"
182 printf '%s\n' "$name" >"$scratch"
183 dependents="$(get_temp dependents_list)"
184 tg summary --deps | sort -k2,2 | join -1 2 - "$scratch" | cut -d ' ' -f 2 | sort -u >"$dependents"
185 if ! [ -s "$dependents" ]; then
186 echo "Dependents: [none]"
187 else
188 if [ "${verbose:-0}" -le 1 ]; then
189 sed '1{ s/^/Dependents: /; n; }; s/^/ /;' <"$dependents"
190 else
191 minwidth=0
192 while read -r endent; do
193 [ ${#endent} -le $minwidth ] || minwidth=${#endent}
194 done <"$dependents"
195 prefix="Dependents:"
196 while read -r endent; do
197 ood=
198 contained_by "refs/heads/$name" "refs/$topbases/$endent^0" || ood=1
199 if [ -n "$ood" ]; then
200 printf '%s %-*s [needs merge]\n' "$prefix" $minwidth "$endent"
201 else
202 printf '%s %s\n' "$prefix" "$endent"
204 prefix=" "
205 done <"$dependents"
210 if [ -n "$nocommits" ]; then
211 echo "* No commits."
212 exit 0
215 echo "Base: $base_rev"
216 branch_contains "refs/heads/$name" "refs/$topbases/$name" ||
217 echo "* Base is newer than head! Please run \`$tgdisplay update\`."
219 if has_remote "$name"; then
220 echo "Remote Mate: $base_remote/$name"
221 # has_remote only checks the single ref it's passed therefore
222 # check to see if the remote base is present especially since remote
223 # bases in the old location do not automatically fetched by default
224 if ref_exists "refs/remotes/$base_remote/${topbases#heads/}/$name"; then
225 branch_contains "refs/$topbases/$name" "refs/remotes/$base_remote/${topbases#heads/}/$name" ||
226 echo "* Local base is out of date wrt. the remote base."
227 else
228 echo "* Remote base ($base_remote/${topbases#heads/}/$name) is missing."
230 branch_contains "refs/heads/$name" "refs/remotes/$base_remote/$name" ||
231 echo "* Local head is out of date wrt. the remote head."
232 branch_contains "refs/remotes/$base_remote/$name" "refs/heads/$name" ||
233 echo "* Local head is ahead of the remote head."
236 git cat-file blob "$name:.topdeps" 2>/dev/null |
237 awk 'NR == 1 {print "Depends: " $0} NR != 1 {print " " $0}'
239 depcheck="$(get_temp tg-depcheck)"
240 missing_deps=
241 needs_update "$name" >"$depcheck" || :
242 if [ -n "$missing_deps" ]; then
243 echo "MISSING: $missing_deps"
245 depcheck2="$(get_temp tg-depcheck2)"
246 sed '/^!/d' <"$depcheck" >"$depcheck2"
247 if [ -s "$depcheck2" ]; then
248 echo "Needs update from:"
249 # 's/ [^ ]* *$//' -- last is $name
250 # 's/^[:] /:/' -- don't distinguish base updates
251 <"$depcheck2" sed -e 's/ [^ ]* *$//' -e 's/^[:] /:/' |
252 while read dep chain; do
253 case "$dep" in
255 dep="${dep#:}"
256 fulldep="refs/heads/$dep"
257 extradep="refs/$topbases/$dep"
260 extradep=
261 case "$dep" in
262 refs/*)
263 fulldep="$dep";;
265 fulldep="refs/heads/$dep";;
266 esac
268 esac
269 printf '%s' "$dep "
270 [ -n "$chain" ] && printf '%s' "(<= $(echol "$chain" | sed 's/ / <= /')) "
271 printf '%s' "($(eval measure_branch '"$fulldep"' '"refs/heads/$name"' ${extradep:+\"\$extradep\"}))"
272 echo
273 done | sed 's/^/ /'
274 else
275 echo "Up-to-date${missing_deps:+ (except for missing dependencies)}."