tg-info: support -i and -w options
[topgit/pro.git] / tg-info.sh
blob9e55ce52ee1aad5d24a845556f3368919df93ace
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, 2018
5 # GPLv2
7 USAGE="\
8 Usage: ${tgname:-tg} [...] info [-i | -w] [--heads | --leaves | --series[=<head>]] [<name>]
9 Or: ${tgname:-tg} [...] info [-i | -w] [--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=
31 head_from=
33 while [ $# -gt 0 ]; do case "$1" in
34 -h|--help)
35 usage
37 -i|-w)
38 [ -z "$head_from" ] || die "-i and -w are mutually exclusive"
39 head_from="$1"
41 --heads)
42 heads=1 datamode=1
44 --leaves)
45 leaves=1 datamode=1
47 --deps|--dependencies)
48 deps=1 datamode=1
50 --dependents)
51 dependents=1 datamode=1
53 --series)
54 series=1 datamode=1
56 --series=*)
57 series=1 datamode=1
58 serieshead="${1#--series=}"
60 -v|--verbose)
61 verbose=$(( ${verbose:-0} + 1 ))
63 -vv|-vvv|-vvvv|-vvvvv)
64 verbose=$(( ${verbose:-0} + ${#1} - 1 ))
66 -?*)
67 echo "Unknown option: $1" >&2
68 usage 1
71 break
73 esac; shift; done
74 [ -z "$datamode" ] || [ "$heads$leaves$deps$dependents$series" = "1" ] ||
75 die "mutually exclusive options: --series --deps --heads --leaves --dependents"
76 [ $# -gt 0 ] || set -- HEAD
77 [ $# -eq 1 ] || die "name already specified ($1)"
78 name="$1"
80 process_dep()
82 if [ -n "$_dep_is_tgish" ] && [ -z "$_dep_missing$_dep_annihilated" ]; then
83 printf '%s\n' "$_dep ${_depchain##* }"
87 if [ -n "$heads" ]; then
88 no_remotes=1
89 base_remote=
90 v_verify_topgit_branch tgbranch "${name:-HEAD}" -f || tgbranch=
91 [ -n "$tgbranch" ] || tgbranch="$(cat_file HEAD:.topdeps $head_from 2>/dev/null | paste -d ' ' -s -)" || :
92 v_get_tdopt with_deps_opts "$head_from"
93 if [ -n "$tgbranch" ]; then
94 # faster version with known TopGit branch name(s)
95 eval navigate_deps "$with_deps_opts" -s=-1 -1 -- '"$tgbranch"' | sort
96 exit 0
98 hash="$(git rev-parse --verify --quiet "${name:-HEAD}^0" --)" || die "no such commit-ish: ${name:-HEAD}"
99 depslist="$(get_temp depslist)"
100 eval tg --no-pager summary $head_from --topgit-heads |
101 while read -r onetghead; do
102 printf '%s %s\n' "$onetghead" "$onetghead"
103 recurse_deps process_dep "$onetghead"
104 done | sort -u >"$depslist"
105 git branch --no-color --contains "$hash" | cut -c 3- |
106 join -o 2.2 - "$depslist" |
107 sort -u
108 exit 0
111 v_cntargs() { eval "$1=$(( $# - 1 ))"; }
112 if [ -n "$series" ]; then
113 v_get_tdopt with_deps_opts "$head_from"
114 if [ -z "$serieshead" ]; then
115 v_verify_topgit_branch name "${name:-HEAD}"
116 heads="$(eval navigate_deps "$with_deps_opts" -s=-1 -1 -- '"$name"' | sort | paste -d ' ' -s -)" || heads="$name"
117 v_cntargs headcnt $heads
118 if [ "$headcnt" -gt 1 ]; then
119 err "multiple heads found"
120 info "use the --series=<head> option on one of them:" >&2
121 for ahead in $heads; do
122 info "$tab$ahead" >&2
123 done
124 die "--series requires exactly one head"
126 [ "$headcnt" = 1 ] || die "programmer bug"
127 serieshead="$heads"
128 else
129 v_verify_topgit_branch serieshead "$serieshead"
130 v_verify_topgit_branch name "${name:-HEAD}" -f || name=
132 seriesf="$(get_temp series)"
133 recurse_deps_internal --series -- "$serieshead" | awk '{print $0 " " NR}' | sort >"$seriesf"
134 refslist=
135 [ -z "$tg_read_only" ] || [ -z "$tg_ref_cache" ] || ! [ -s "$tg_ref_cache" ] ||
136 refslist="-r=\"$tg_ref_cache\""
137 flagname=
138 [ -z "$name" ] || [ "$serieshead" = "$name" ] || flagname="$name"
139 output() {
140 v_get_tmopt tm_opt "$head_from"
141 eval run_awk_topgit_msg -n "-nokind${tm_opt:+ $tm_opt}" "$refslist" '"refs/$topbases"' |
142 join "$seriesf" - | sort -k2,2n | awk -v "flag=$flagname" '
144 bn = $1
145 mark = ""
146 if (flag != "") mark = (bn == flag) ? "* " : " "
147 bn = mark bn
148 desc = $0
149 sub(/^[^ ]+ [^ ]+ /, "", desc)
150 printf "%-39s\t%s\n", bn, desc
153 } && page output
154 exit 0
157 v_verify_topgit_branch name "${name:-HEAD}"
159 if [ -n "$leaves" ]; then
160 v_get_tdopt with_deps_opts "$head_from"
161 find_leaves "$name"
162 exit 0
165 if [ -n "$deps$dependents" ]; then
166 alldeps="$(get_temp alldeps)"
167 tg --no-pager summary $head_from --tgish-only --deps >"$alldeps" || die "tg summary --deps failed"
168 if [ -n "$deps" ]; then
169 awk -v annb="$name" 'NF == 2 && $2 != "" && $1 == annb { print $2 }' <"$alldeps"
170 else
171 awk -v annb="$name" 'NF == 2 && $1 != "" && $2 == annb { print $1 }' <"$alldeps"
173 exit 0
176 base_rev="$(git rev-parse --short --verify "refs/$topbases/$name^0" -- 2>/dev/null)" ||
177 die "not a TopGit-controlled branch"
179 measure="$(measure_branch "refs/heads/$name" "$base_rev")"
181 echo "Topic Branch: $name ($measure)"
183 noskipiw=
184 [ -z "$head_from" ] || ! v_verify_topgit_branch tghead HEAD -f || [ "$tghead" != "$name" ] || noskipiw=1
185 [ -z "$noskipiw" ] || v_get_tmopt tm_opt "$head_from"
186 read -r bkind subj <<EOT
187 $(eval run_awk_topmsg_header "-kind${tm_opt:+ $tm_opt}" '"$name"')
189 [ "$bkind" = "3" ] || printf "Subject: %s\n" "$subj"
191 if [ "${verbose:-0}" -ge 1 ]; then
192 scratch="$(get_temp scratch)"
193 printf '%s\n' "$name" >"$scratch"
194 dependents="$(get_temp dependents_list)"
195 tg --no-pager summary $head_from --tgish-only --deps | sort -k2,2 | join -1 2 - "$scratch" | cut -d ' ' -f 2 | sort -u >"$dependents"
196 if ! [ -s "$dependents" ]; then
197 echo "Dependents: [none]"
198 else
199 if [ "${verbose:-0}" -le 1 ]; then
200 sed '1{ s/^/Dependents: /; n; }; s/^/ /;' <"$dependents"
201 else
202 minwidth=0
203 while read -r endent; do
204 [ ${#endent} -le $minwidth ] || minwidth=${#endent}
205 done <"$dependents"
206 prefix="Dependents:"
207 while read -r endent; do
208 ood=
209 contained_by "refs/heads/$name" "refs/$topbases/$endent^0" || ood=1
210 if [ -n "$ood" ]; then
211 printf '%s %-*s [needs merge]\n' "$prefix" $minwidth "$endent"
212 else
213 printf '%s %s\n' "$prefix" "$endent"
215 prefix=" "
216 done <"$dependents"
221 if [ "$bkind" = "3" ]; then
222 echo "* No commits."
223 exit 0
226 echo "Base: $base_rev"
227 branch_contains "refs/heads/$name" "refs/$topbases/$name" ||
228 echo "* Base is newer than head! Please run \`$tgdisplay update\`."
230 if has_remote "$name"; then
231 echo "Remote Mate: $base_remote/$name"
232 # has_remote only checks the single ref it's passed therefore
233 # check to see if the remote base is present especially since remote
234 # bases in the old location do not automatically fetched by default
235 if ref_exists "refs/remotes/$base_remote/${topbases#heads/}/$name"; then
236 branch_contains "refs/$topbases/$name" "refs/remotes/$base_remote/${topbases#heads/}/$name" ||
237 echo "* Local base is out of date wrt. the remote base."
238 else
239 echo "* Remote base ($base_remote/${topbases#heads/}/$name) is missing."
241 branch_contains "refs/heads/$name" "refs/remotes/$base_remote/$name" ||
242 echo "* Local head is out of date wrt. the remote head."
243 branch_contains "refs/remotes/$base_remote/$name" "refs/heads/$name" ||
244 echo "* Local head is ahead of the remote head."
247 cat_file "refs/heads/$name:.topdeps" ${noskipiw:+$head_from} 2>/dev/null |
248 awk 'NR == 1 {print "Depends: " $0} NR != 1 {print " " $0}'
250 depcheck="$(get_temp tg-depcheck)"
251 missing_deps=
252 v_get_tdopt with_deps_opts "$head_from"
253 needs_update "$name" >"$depcheck" || :
254 if [ -n "$missing_deps" ]; then
255 echo "MISSING: $missing_deps"
257 depcheck2="$(get_temp tg-depcheck2)"
258 sed '/^!/d' <"$depcheck" >"$depcheck2"
259 if [ -s "$depcheck2" ]; then
260 echo "Needs update from:"
261 # 's/ [^ ]* *$//' -- last is $name
262 # 's/^[:] /::/' -- don't distinguish base updates
263 <"$depcheck2" sed -e 's/ [^ ]* *$//' -e 's/^[:] /:/' |
264 while read dep chain; do
265 extradep=
266 case "$dep" in
267 ::*)
268 dep="${dep#::}"
269 fulldep="refs/heads/$dep"
270 extradep="refs/$topbases/$dep"
273 dep="${dep#:}"
274 fulldep="$dep"
277 fulldep="refs/heads/$dep"
279 esac
280 printf '%s' "$dep "
281 [ -n "$chain" ] && printf '%s' "(<= $(echol "$chain" | sed 's/ / <= /')) "
282 printf '%s' "($(eval measure_branch '"$fulldep"' '"refs/heads/$name"' ${extradep:+\"\$extradep\"}))"
283 echo
284 done | sed 's/^/ /'
285 else
286 echo "Up-to-date${missing_deps:+ (except for missing dependencies)}."