tg-contains.sh: new command to show containing TopGit branch(es)
[topgit/pro.git] / tg-contains.sh
blob4c8b0b60ce7d45237f66580fcb22af8b78f537ac
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] [--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
24 while [ $# -gt 0 ]; do case "$1" in
25 -h|--help)
26 usage
28 -r|--remotes)
29 remotes=1
31 --heads)
32 echo "Did you mean --verbose (-v) instead of --heads?" >&2
33 usage 1
35 -v|--verbose)
36 verbose=$(( ${verbose:-0} + 1 ))
38 -vv|-vvv|-vvvv|-vvvvv)
39 verbose=$(( ${verbose:-0} + ${#1} - 1 ))
41 --strict)
42 strict=1
44 --no-strict)
45 strict=
47 --)
48 shift
49 break
51 -?*)
52 echo "Unknown option: $1" >&2
53 usage 1
56 break
58 esac; shift; done
59 [ $# = 1 ] || usage 1
60 [ "$1" != "@" ] || set -- HEAD
62 set -e
63 findrev="$(git rev-parse --verify "$1"^0 --)" || exit 1
65 # true if $1 is contained by (or the same as) $2
66 contained_by()
68 [ "$(git rev-list --count --max-count=1 "$1" --not "$2" --)" = "0" ]
71 # $1 => return correct $topbases value in here on success
72 # $2 => remote name
73 # $3 => remote branch name
74 # succeeds if both refs/remotes/$2/$3 and refs/remotes/$2/${$1#heads/}/$3 exist
75 v_is_remote_tgbranch()
77 git rev-parse --quiet --verify "refs/remotes/$2/$3^0" -- >/dev/null || return 1
78 if git rev-parse --quiet --verify "refs/remotes/$2/${topbases#heads/}/$3^0" -- >/dev/null; then
79 [ -z "$1" ] || eval "$1="'"$topbases"'
80 return 0
82 git rev-parse --quiet --verify "refs/remotes/$2/${oldbases#heads/}/$3^0" -- >/dev/null || return 1
83 [ -z "$1" ] || eval "$1="'"$oldbases"'
86 process_dep()
88 if [ -n "$_dep_is_tgish" ] && [ -z "$_dep_missing$_dep_annihilated" ]; then
89 printf '%s\n' "$_dep ${_depchain##* }"
93 depslist=
94 make_deps_list()
96 no_remotes=1
97 base_remote=
98 depslist="$(get_temp depslist)"
99 $tg summary --topgit-heads |
100 while read -r onetghead; do
101 printf '%s %s\n' "$onetghead" "$onetghead"
102 recurse_deps process_dep "$onetghead"
103 done | LC_ALL=C sort -u >"$depslist"
106 localcnt=
107 remotecnt=
108 localb="$(get_temp localb)"
109 localwide=0
110 remoteb=
111 remotewide=0
112 [ -z "$remotes" ] || remoteb="$(get_temp remoteb)"
113 while IFS= read -r branch && [ -n "$branch" ]; do
114 branch="${branch#??}"
115 if v_verify_topgit_branch "" "$branch" -f; then
116 if contained_by "$findrev" "refs/$topbases/$branch"; then
117 [ -z "$strict" ] || continue
118 depth="$(git rev-list --count --ancestry-path "refs/$topbases/$branch" --not "$findrev")"
119 depth=$(( ${depth:-0} + 1 ))
120 else
121 depth=0
123 localcnt=$(( ${localcnt:-0} + 1 ))
124 [ ${#branch} -le $localwide ] || localwide=${#branch}
125 printf '%s %s\n' "$depth" "$branch" >>"$localb"
126 remotecnt=
127 else
128 [ -n "$remotes" ] && [ -z "$localcnt" ] && [ "${branch#remotes/}" != "$branch" ] || continue
129 rbranch="${branch#remotes/}"
130 rremote="${rbranch%%/*}"
131 rbranch="${rbranch#*/}"
132 [ "remotes/$rremote/$rbranch" = "$branch" ] || continue
133 v_is_remote_tgbranch rtopbases "$rremote" "$rbranch" || continue
134 if contained_by "$findrev" "refs/remotes/$rremote/${rtopbases#heads/}/$rbranch"; then
135 [ -z "$strict" ] || continue
136 depth="$(git rev-list --count --ancestry-path "refs/remotes/$rremote/${rtopbases#heads/}/$rbranch" --not "$findrev")"
137 depth=$(( ${depth:-0} + 1 ))
138 else
139 depth=0
141 remotecnt=$(( ${remotecnt:-0} + 1 ))
142 [ ${#branch} -le $remotewide ] || remotewide=${#branch}
143 [ -n "$remoteb" ] || remoteb="$(get_temp remoteb)"
144 printf '%s %s\n' "$depth" "remotes/$rremote/$rbranch" >>"$remoteb"
146 done <<EOT
147 $(git branch ${remotes:+-a} --contains "$findrev")
149 [ -n "$localcnt$remotecnt" ] || exit 1
150 [ -z "$localcnt" ] || [ ${verbose:-0} -le 0 ] || make_deps_list
151 if [ -n "$localcnt" ]; then
152 process="$localb"
153 minwide=$localwide
154 else
155 process="$remoteb"
156 minwide=$remotewide
159 LC_ALL=C sort -k1,1n "$process" |
160 while read -r depth ref; do
161 [ -n "$mindepth" ] || mindepth="$depth"
162 [ $depth -le $mindepth ] || continue
163 printf '%s\n' "$ref"
164 done | LC_ALL=C sort -u |
165 while read -r oneresult; do
166 headinfo=
167 [ -z "$depslist" ] ||
168 headinfo="$(printf '%s\n' "$oneresult" | join -o 2.2 - "$depslist" |
169 LC_ALL=C sort -u | paste -d , -s - | sed -e 's/,/, /g')"
170 if [ -z "$headinfo" ]; then
171 printf '%s\n' "$oneresult"
172 else
173 printf '%-*s [%s]\n' $minwide "$oneresult" "$headinfo"
175 done
176 exit 0