topgit: version 0.19.13
[topgit/pro.git] / tg-contains.sh
blob709a6b152d57630a910232defe0656634c69cf07
1 #!/bin/sh
2 # TopGit contains command
3 # (C) 2017 Kyle J. McKay <mackyle@gmail.com>
4 # All rights reserved
5 # GPLv2
7 USAGE="\
8 Usage: ${tgname:-tg} [...] contains [-v] [-r] [--ann] [--no-strict] [--] <committish>"
10 usage()
12 if [ "${1:-0}" != 0 ]; then
13 printf '%s\n' "$USAGE" >&2
14 else
15 printf '%s\n' "$USAGE"
17 exit ${1:-0}
20 verbose=
21 remotes=
22 strict=1
23 annok=
25 while [ $# -gt 0 ]; do case "$1" in
26 -h|--help)
27 usage
29 -r|--remotes)
30 remotes=1
32 --ann|--annihilated|--annihilated-ok|--annihilated-okay)
33 annok=1
35 --heads)
36 echo "Did you mean --verbose (-v) instead of --heads?" >&2
37 usage 1
39 -v|--verbose)
40 verbose=$(( ${verbose:-0} + 1 ))
42 -vv|-vvv|-vvvv|-vvvvv)
43 verbose=$(( ${verbose:-0} + ${#1} - 1 ))
45 --strict)
46 strict=1
48 --no-strict)
49 strict=
51 --)
52 shift
53 break
55 -?*)
56 echo "Unknown option: $1" >&2
57 usage 1
60 break
62 esac; shift; done
63 [ $# = 1 ] || usage 1
64 [ "$1" != "@" ] || set -- HEAD
66 set -e
67 findrev="$(git rev-parse --verify "$1"^0 --)" || exit 1
69 # $1 => return correct $topbases value in here on success
70 # $2 => remote name
71 # $3 => remote branch name
72 # succeeds if both refs/remotes/$2/$3 and refs/remotes/$2/${$1#heads/}/$3 exist
73 v_is_remote_tgbranch()
75 git rev-parse --quiet --verify "refs/remotes/$2/$3^0" -- >/dev/null || return 1
76 if git rev-parse --quiet --verify "refs/remotes/$2/${topbases#heads/}/$3^0" -- >/dev/null; then
77 [ -z "$1" ] || eval "$1="'"$topbases"'
78 return 0
80 git rev-parse --quiet --verify "refs/remotes/$2/${oldbases#heads/}/$3^0" -- >/dev/null || return 1
81 if [ -z "$annok" ]; then
82 rmb="$(git merge-base "refs/remotes/$2/${oldbases#heads/}/$3^0" "refs/remotes/$2/$3^0" 2>/dev/null)" || :
83 if [ -n "$rmb" ]; then
84 rmbtree="$(git rev-parse --quiet --verify "$rmb^{tree}" --)" || :
85 rbrtree=
86 [ -z "$rmbtree" ] ||
87 rbrtree="$(git rev-parse --quiet --verify "refs/remotes/$2/$3^{tree}" --)" || :
88 [ -z "$rmbtree" ] || [ -z "$rbrtree" ] || [ "$rmbtree" != "$rbrtree" ] || return 1
91 [ -z "$1" ] || eval "$1="'"$oldbases"'
94 process_dep()
96 if [ -n "$_dep_is_tgish" ] && [ -z "$_dep_missing$_dep_annihilated" ]; then
97 printf '%s\n' "$_dep ${_depchain##* }"
101 depslist=
102 make_deps_list()
104 no_remotes=1
105 base_remote=
106 depslist="$(get_temp depslist)" || exit 1
107 tg summary --topgit-heads |
108 while read -r onetghead; do
109 printf '%s %s\n' "$onetghead" "$onetghead"
110 recurse_deps process_dep "$onetghead"
111 done | sort -u >"$depslist"
114 localcnt=
115 remotecnt=
116 localb="$(get_temp localb)" || exit 1
117 localwide=0
118 remoteb=
119 remotewide=0
120 [ -z "$remotes" ] || remoteb="$(get_temp remoteb)" || exit 1
121 process_branches() {
122 while read -r branch bremote && [ -n "$branch" ]; do case "$branch" in
123 "refs/$topbases"/?*)
124 continue
126 "refs/heads"/?*)
127 [ -z "$bremote" ] && v_verify_topgit_branch "" "$branch" -f || continue
128 branch="${branch#refs/heads/}"
129 [ -n "$annok" ] || ! branch_annihilated "$branch" || continue
130 if contained_by "$findrev" "refs/$topbases/$branch"; then
131 [ -z "$strict" ] || continue
132 depth="$(git rev-list --count --ancestry-path \
133 "refs/$topbases/$branch" --not "$findrev")"
134 depth=$(( ${depth:-0} + 1 ))
135 else
136 depth=0
138 localcnt=$(( ${localcnt:-0} + 1 ))
139 [ ${#branch} -le $localwide ] || localwide=${#branch}
140 printf '%s %s\n' "$depth" "$branch" >>"$localb"
141 remotecnt=
144 [ -n "$bremote" ] && [ -n "$remotes" ] && [ -z "$localcnt" ] &&
145 [ "${branch#refs/remotes/}" != "$branch" ] || continue
146 rbranch="${branch#refs/remotes/$bremote/}"
147 [ "refs/remotes/$bremote/$rbranch" = "$branch" ] || continue
148 v_is_remote_tgbranch rtopbases "$bremote" "$rbranch" || continue
149 if contained_by "$findrev" "refs/remotes/$bremote/${rtopbases#heads/}/$rbranch"; then
150 [ -z "$strict" ] || continue
151 depth="$(git rev-list --count --ancestry-path \
152 "refs/remotes/$bremote/${rtopbases#heads/}/$rbranch" --not "$findrev")"
153 depth=$(( ${depth:-0} + 1 ))
154 else
155 depth=0
157 remotecnt=$(( ${remotecnt:-0} + 1 ))
158 branch="${branch#refs/}"
159 [ ${#branch} -le $remotewide ] || remotewide=${#branch}
160 [ -n "$remoteb" ] || remoteb="$(get_temp remoteb)" || exit 1
161 printf '%s %s\n' "$depth" "remotes/$bremote/$rbranch" >>"$remoteb"
163 esac; done
165 if [ -z "$remotes" ]; then
166 process_branches <<EOT || exit 1
167 $(fer_branch_contains "$findrev")
169 else
170 ferlist="$(get_temp ferlist)" || exit 1
171 fer_branch_contains -a "$findrev" >"$ferlist" || exit 1
172 process_branches <<EOT
173 $( awk -v tb="${topbases#heads/}" -v ob="${oldbases#heads/}" <"$ferlist" '
174 function join(a,b,e,_j,_r) {_r=""
175 for (_j=b;_j<=e;++_j)_r=_r"/"a[_j];return substr(_r,2)}
176 !/^refs\/remotes\/./ {next}
178 n=split(substr($0,14),c,/\//)
179 if (n<2) next
180 for (i=1;i<n;++i) {
181 r=join(c,1,i); t=join(c,i+1,n)
182 print "refs/remotes/"r"/"tb"/"t" "r
183 print "refs/remotes/"r"/"ob"/"t" "r
186 ' | git cat-file $gcfbopt --batch-check='%(objectname) %(objecttype) %(rest)' |
187 awk -v f="$ferlist" '
188 function join(a,b,e,_j,_r) {_r=""
189 for (_j=b;_j<=e;++_j)_r=_r"/"a[_j];return substr(_r,2)}
190 !($2=="commit"&&$3!=""&&$1~/^[0-9a-f][0-9a-f][0-9a-f][0-9a-f]+$/){next}
191 {p[$3]=1}
192 END {
193 while((e=(getline b<f))>0) {
194 if(b!~/^refs\/remotes\/./){print b;continue}
195 n=split(substr(b,14),c,/\//)
196 if (n<2) continue
198 for (i=1;!d&&i<n;++i) {
199 r=join(c,1,i)
200 if(r in p) {d=1;t=join(c,i+1,n)
201 print "refs/remotes/"r"/"t" "r}
204 close(f)
205 if(e<0)exit 1
206 exit 0;
211 [ -n "$localcnt$remotecnt" ] || exit 1
212 [ -z "$localcnt" ] || [ ${verbose:-0} -le 0 ] || make_deps_list
213 if [ -n "$localcnt" ]; then
214 process="$localb"
215 minwide=$localwide
216 else
217 process="$remoteb"
218 minwide=$remotewide
221 sort -k1,1n "$process" |
222 while read -r depth ref; do
223 [ -n "$mindepth" ] || mindepth="$depth"
224 [ $depth -le $mindepth ] || continue
225 printf '%s\n' "$ref"
226 done | sort -u |
227 while read -r oneresult; do
228 headinfo=
229 isann=
230 [ -z "$annok" ] || [ -z "$depslist" ] || ! branch_annihilated "$oneresult" || isann=1
231 [ -z "$depslist" ] || [ -n "$isann" ] ||
232 headinfo="$(printf '%s\n' "$oneresult" | join -o 2.2 - "$depslist" |
233 sort -u | paste -d , -s - | sed -e 's/,/, /g')"
234 [ -z "$annok" ] || [ -z "$depslist" ] || [ -z "$isann" ] || headinfo=":annihilated:"
235 if [ -z "$headinfo" ]; then
236 printf '%s\n' "$oneresult"
237 else
238 printf '%-*s [%s]\n' $minwide "$oneresult" "$headinfo"
240 done
241 exit 0