tg-log.sh: omit --no-merges option for [BASE] branches
[topgit/pro.git] / tg-contains.sh
blobd0c3227d28e2bbd4774247a7976339b1981ce2d1
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 # true if $1 is contained by (or the same as) $2
70 contained_by()
72 [ "$(git rev-list --count --max-count=1 "$1" --not "$2" --)" = "0" ]
75 # $1 => return correct $topbases value in here on success
76 # $2 => remote name
77 # $3 => remote branch name
78 # succeeds if both refs/remotes/$2/$3 and refs/remotes/$2/${$1#heads/}/$3 exist
79 v_is_remote_tgbranch()
81 git rev-parse --quiet --verify "refs/remotes/$2/$3^0" -- >/dev/null || return 1
82 if git rev-parse --quiet --verify "refs/remotes/$2/${topbases#heads/}/$3^0" -- >/dev/null; then
83 [ -z "$1" ] || eval "$1="'"$topbases"'
84 return 0
86 git rev-parse --quiet --verify "refs/remotes/$2/${oldbases#heads/}/$3^0" -- >/dev/null || return 1
87 if [ -z "$annok" ]; then
88 rmb="$(git merge-base "refs/remotes/$2/${oldbases#heads/}/$3^0" "refs/remotes/$2/$3^0" 2>/dev/null)" || :
89 if [ -n "$rmb" ]; then
90 rmbtree="$(git rev-parse --quiet --verify "$rmb^{tree}" --)" || :
91 rbrtree=
92 [ -z "$rmbtree" ] ||
93 rbrtree="$(git rev-parse --quiet --verify "refs/remotes/$2/$3^{tree}" --)" || :
94 [ -z "$rmbtree" ] || [ -z "$rbrtree" ] || [ "$rmbtree" != "$rbrtree" ] || return 1
97 [ -z "$1" ] || eval "$1="'"$oldbases"'
100 process_dep()
102 if [ -n "$_dep_is_tgish" ] && [ -z "$_dep_missing$_dep_annihilated" ]; then
103 printf '%s\n' "$_dep ${_depchain##* }"
107 depslist=
108 make_deps_list()
110 no_remotes=1
111 base_remote=
112 depslist="$(get_temp depslist)"
113 $tg summary --topgit-heads |
114 while read -r onetghead; do
115 printf '%s %s\n' "$onetghead" "$onetghead"
116 recurse_deps process_dep "$onetghead"
117 done | LC_ALL=C sort -u >"$depslist"
120 localcnt=
121 remotecnt=
122 localb="$(get_temp localb)"
123 localwide=0
124 remoteb=
125 remotewide=0
126 [ -z "$remotes" ] || remoteb="$(get_temp remoteb)"
127 while IFS= read -r branch && [ -n "$branch" ]; do
128 branch="${branch#??}"
129 if v_verify_topgit_branch "" "$branch" -f; then
130 [ -n "$annok" ] || ! branch_annihilated "$branch" || continue
131 if contained_by "$findrev" "refs/$topbases/$branch"; then
132 [ -z "$strict" ] || continue
133 depth="$(git rev-list --count --ancestry-path "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=
142 else
143 [ -n "$remotes" ] && [ -z "$localcnt" ] && [ "${branch#remotes/}" != "$branch" ] || continue
144 rbranch="${branch#remotes/}"
145 rremote="${rbranch%%/*}"
146 rbranch="${rbranch#*/}"
147 [ "remotes/$rremote/$rbranch" = "$branch" ] || continue
148 v_is_remote_tgbranch rtopbases "$rremote" "$rbranch" || continue
149 if contained_by "$findrev" "refs/remotes/$rremote/${rtopbases#heads/}/$rbranch"; then
150 [ -z "$strict" ] || continue
151 depth="$(git rev-list --count --ancestry-path "refs/remotes/$rremote/${rtopbases#heads/}/$rbranch" --not "$findrev")"
152 depth=$(( ${depth:-0} + 1 ))
153 else
154 depth=0
156 remotecnt=$(( ${remotecnt:-0} + 1 ))
157 [ ${#branch} -le $remotewide ] || remotewide=${#branch}
158 [ -n "$remoteb" ] || remoteb="$(get_temp remoteb)"
159 printf '%s %s\n' "$depth" "remotes/$rremote/$rbranch" >>"$remoteb"
161 done <<EOT
162 $(git branch ${remotes:+-a} --contains "$findrev")
164 [ -n "$localcnt$remotecnt" ] || exit 1
165 [ -z "$localcnt" ] || [ ${verbose:-0} -le 0 ] || make_deps_list
166 if [ -n "$localcnt" ]; then
167 process="$localb"
168 minwide=$localwide
169 else
170 process="$remoteb"
171 minwide=$remotewide
174 LC_ALL=C sort -k1,1n "$process" |
175 while read -r depth ref; do
176 [ -n "$mindepth" ] || mindepth="$depth"
177 [ $depth -le $mindepth ] || continue
178 printf '%s\n' "$ref"
179 done | LC_ALL=C sort -u |
180 while read -r oneresult; do
181 headinfo=
182 isann=
183 [ -z "$annok" ] || [ -z "$depslist" ] || ! branch_annihilated "$oneresult" || isann=1
184 [ -z "$depslist" ] || [ -n "$isann" ] ||
185 headinfo="$(printf '%s\n' "$oneresult" | join -o 2.2 - "$depslist" |
186 LC_ALL=C sort -u | paste -d , -s - | sed -e 's/,/, /g')"
187 [ -z "$annok" ] || [ -z "$depslist" ] || [ -z "$isann" ] || headinfo=":annihilated:"
188 if [ -z "$headinfo" ]; then
189 printf '%s\n' "$oneresult"
190 else
191 printf '%-*s [%s]\n' $minwide "$oneresult" "$headinfo"
193 done
194 exit 0