topgit: version 0.19.13
[topgit/pro.git] / awk / topgit_deps.awk
blob84647298da8fe4ef6ec0ddd0ed1b9e9a97198455
1 #!/usr/bin/awk -f
3 # topgit_deps - TopGit awk utility script used by tg--awksome
4 # Copyright (C) 2017,2019 Kyle J. McKay <mackyle@gmail.com>
5 # All rights reserved.
6 # License GPLv2
8 # topgit_deps
10 # variable arguments (-v):
12 # brfile if non-empty, read TopGit branch names from here
13 # rmbr if true run system rm on brfile (after reading) if non-empty brfile
14 # anfile if non-empty, annihilated branch names are read from here
15 # rman if true run system rm on anfile (after reading) if non-empty anfile
16 # withan if true, mostly pretend anfile was empty (this is a convenience knob)
17 # withbr if true, output an "edge to self" for each input branch
18 # tgonly if true only emit deps listed in brfile
19 # rev if true, reverse each dep and the order from each .topdeps file
20 # exclbr whitespace separated list of names to exclude
21 # inclbr whitespace separated list of names to include
23 # if inclbr is non-empty only edges with at least one end listed in inclbr
24 # will appear on stdout
26 # if a branch name appears in exclbr any edge with either end listed in exclbr
27 # will be omitted from stdout trumping inclbr
29 # when withbr is true, the "edge to self" undergoes inclbr/exclbr processing
30 # too which can end up suppressing it from the output
32 # if a branch is listed in exclbr then all edges going to/from that branch
33 # are necessarily omitted which means effectively that any branch listed in
34 # exclbr has its .topdeps file treated as though it were empty
36 # note that if tgonly is true then effectively all non-tgish branches are
37 # considered to be implicitly listed in exclbr
39 # input must be result of the git --batch output as described for
40 # awk_topgit_deps_prepare
42 # output is 0 or more dependency "edges" from the .topdeps blob files output
43 # in the same order they appear on the input except that if rev is true
44 # the lines from each individual .topdeps blob are processed in reverse order
45 # and the edges themselves are output in opposite order
47 # each output line has this format:
49 # <TopGit_branch_name> <TopGit_branch_name>
51 # both branch names are guaranteed to be non-empty and non-matching unless
52 # withbr is true (but even then same-named branches from within the .topdeps
53 # content itself will still be suppressed) but other kinds of loops are not
54 # detected (until awk_topgit_recurse runs on the output)
56 # if the same branch name is listed more than once within the same .topdeps
57 # file, all occurences after the first will be ignored
59 # if withbr is true then a line will be output with the branch name as both
60 # the first and second fields (i.e. the edge points to itself) and this line
61 # will be output after all of the .topdeps content for the branch unless rev
62 # is true in which case it's output before any of the branch's .topdeps content
64 # Some valid TopGit branches may not have a .topdeps file and annihilated
65 # branches certainly do not so setting withbr true will only give good results
66 # if awk_topgit_deps_prepare was passed the empty blob's hash for its "missing"
67 # variable
69 # note that there can be duplicate output lines, especially when multiple
70 # patch series are present in the same repository and they share some of the
71 # patches
73 # note that anfile, if non-empty, is not read until after the first line of
74 # input is read, so the same file name passed to awk_topgit_deps_prepare can
75 # just be passed here as well without problem and, as a convenience, if rman
76 # is true the system "rm -f" command will be run on it after it's been read
77 # or if it's non-empty and withan is true
79 # using the withan knob, the same filename can always be passed as the anfile
80 # argument but it will be ignored when withan ("with annihilated in output")
81 # is set to true; this is done as a convenience as the same effect is always
82 # achieved by not passing any anfile value (or passing an empty string)
84 # any incoming branch names from the --batch .topdeps content that are not
85 # valid git ref names are silently discarded without notice
88 BEGIN { exitcode = "" }
89 function exitnow(e) { exitcode=e; exit e }
90 END { if (exitcode != "") exit exitcode }
92 BEGIN {
93 inconly = 0
94 cnt = split(inclbr, scratch, " ")
95 if (cnt) {
96 inconly = 1
97 for (i = 1; i <= cnt; ++i) incnames[scratch[i]] = 1
99 cnt = split(exclbr, scratch, " ")
100 for (i = 1; i <= cnt; ++i) excnames[scratch[i]] = 1
103 function quotevar(v) {
104 gsub(/\047/, "\047\\\047\047", v)
105 return "\047" v "\047"
108 function rmfiles() {
109 rmlist = ""
110 if (rmbr && brfile != "") rmlist = rmlist " " quotevar(brfile)
111 if (rman && anfile != "") rmlist = rmlist " " quotevar(anfile)
112 if (rmlist != "") {
113 system("rm -f" rmlist)
114 rmbr = 0
115 brfile = ""
116 rman = 0
117 anfile = ""
120 END { rmfiles() }
122 function init(abranch, _e) {
123 if (brfile != "") {
124 if (tgonly) {
125 while ((_e = (getline abranch <brfile)) > 0) {
126 if (abranch != "") tgish[abranch] = 1
128 close(brfile)
129 if (_e < 0) exitnow(2)
132 if (!withan && anfile != "") {
133 while ((_e = (getline abranch <anfile)) > 0) {
134 if (abranch != "") ann[abranch] = 1
136 close(anfile)
137 if (_e < 0) exitnow(2)
139 rmfiles()
142 function excluded(abranch) {
143 return abranch in excnames || (tgonly && !(abranch in tgish))
146 function inclself(abranch) {
147 return !excluded(abranch) &&
148 (!inconly || abranch in incnames)
151 function incledge(b1, b2) {
152 return !excluded(b1) && !excluded(b2) &&
153 (!inconly || b1 in incnames || b2 in incnames)
156 function validbr(branchname) {
157 return "/" tolower(branchname) "/" !~ \
158 "//|\\.\\.|@\\173|/\\.|\\./|\\.lock/|[\\001-\\040\\177~^:\\\\*?\\133]"
161 NR == 1 {init()}
163 NF == 3 && $2 != "missing" && $1 != "" && $2 ~ /^[0-9]+$/ && validbr($3) {
164 bn = $3
165 bnann = bn in ann;
166 isann = $1 != "blob" || bnann || excluded(bn)
167 datalen = $2 + 1
168 curlen = 0
169 split("", seen, ":")
170 seen[bn] = 1
171 if (withbr && rev && !bnann && inclself(bn)) print bn " " bn
172 cnt = 0
173 err = 0
174 while (curlen < datalen && (err = getline) > 0) {
175 curlen += length($0) + 1
176 sub(/\r$/, "", $1)
177 if (NF != 1 || $1 == "" || $1 in seen || !validbr($1)) continue
178 seen[$1] = 1
179 if (!isann && !($1 in ann) && incledge(bn,$1)) {
180 if (rev)
181 items[++cnt] = $1 " " bn
182 else
183 print bn " " $1
186 if (err < 0) exitnow(2)
187 for (i=cnt; i>0; --i) print items[i]
188 if (withbr && !rev && !bnann && inclself(bn)) print bn " " bn