2 # TopGit awk scripts and related utility functions
3 # Copyright (C) 2017,2019 Kyle J. McKay <mackyle@gmail.com>
7 ## Several awk scripts are used to quickly process TopGit branches and
8 ## their associated .topdeps files and with the exception of $gcfbo
9 ## (which can be left unset) and the get_temp function, can be used
10 ## independently; a suitable and simple get_temp function can be just:
12 ## get_temp() { mktemp "${TMPDIR:-/tmp}/${1:-temp}-XXXXXX"; }
14 ## The utility functions all require use of the '%(rest)' atom format
15 ## introduced with Git v1.8.5 for use with git cat-file --batch and
16 ## --batch-check options. Although it would be possible to simulate
17 ## that functionality it would require a lot of extra processing that
18 ## use of '%(rest)' eliminates so no attempt is made to support earlier
21 ## Note that the $gcfbopt variable may contain the value "--buffer"
22 ## if Git is version 2.6.0 or later for increased acceleration
23 ## (Git Cat-File Buffer OPTion)
25 ## NOTE: This file contains only function definitions, variable assignments
26 # and checks for the existence of the corresponding script files but
27 # has no other non-function shell code than that
29 [ -n "$TG_INST_AWKDIR" ] ||
{
30 TG_INST_AWKDIR
="${TG_INST_CMDDIR%/}"
31 [ -n "$TG_INST_AWKDIR" ] ||
32 case "$0" in *"/"*[!/]) TG_INST_AWKDIR
="${0%/*}"; esac
33 [ -z "$TG_INST_AWKDIR" ] || TG_INST_AWKDIR
="$TG_INST_AWKDIR/"
34 TG_INST_AWKDIR
="${TG_INST_AWKDIR}awk"
37 # die with a fatal programmer error bug
40 printf 'fatal: [BUG] programmer error\n%s%s\n' \
41 'fatal: [BUG] ' "$*" >&2
45 # verify an awk script exists, is a file and is readable or die
48 while [ $# -gt 0 ]; do
49 [ -f "$TG_INST_AWKDIR/$1" ] && [ -r "$TG_INST_AWKDIR/$1" ] ||
{
50 printf '%s\n' "fatal: awksome: missing awk script '$TG_INST_AWKDIR/$1'" >&2
57 [ -n "$mtblob" ] || run_awk_bug
"mtblob must be set before sourcing awksome"
59 # run_awk_ref_match [opts] [<for-each-ref-pattern>...]
61 # --sort=<key> sort key (repeatable) last is primary same names as --format
62 # --count=<max> stop after this many matches
63 # --format=<fmt> for-each-ref style format but only objname and refname ok
64 # -p input is packed-refs style rather than "<ref> <hash>"
66 # input is one ref + hash table entry per line either packed-refs style or
67 # ref first then hash where nonsensical lines are ignored
69 # output is one "format expansion" per match but stopping after --count (if given)
71 # the <fmt> works like for-each-ref except that only the "refname" and "objectname"
72 # (and %-escapes) get expanded; well, actually, %(objecttype) is expeanded too, but
73 # always to the constant string "object" (unquoted) rather than a real type
75 # the sort keys work like for-each-ref too except that keys other than refname
76 # and/or objectname are ignored but reverse (leading "-") IS supported
78 run_awk_require
"ref_match"
85 while [ $# -gt 0 ]; do case "$1" in
87 --sort=*) _ra_sortkey
="${_ra_sortkey:+$_ra_sortkey,}${1#--sort=}";;
88 --count=*) _ra_maxout
="${1#--count=}";;
89 --format=*) _ra_matchfmt
="${1#--format=}";;
91 -?
*) run_awk_bug
"unknown run_awk_ref_match option: $1";;
94 awk -f "$TG_INST_AWKDIR/ref_match" \
95 -v "pckdrefs=$_ra_pckdrefs" \
96 -v "sortkey=$_ra_sortkey" \
97 -v "maxout=$_ra_maxout" \
98 -v "matchfmt=$_ra_matchfmt" \
102 # run_awk_ref_prefixes [opts] <prefix1> <prefix2> [<prefixh>]
104 # -p input is packed-refs format rather than full ref in first field
105 # -e on error when both prefixes present use the default (prefix1) instead
106 # -n no default instead error out
108 # input is one fully qualified ref name per line either in the first
109 # field (default) or in packed-refs format (second field) with -p
110 # (input need not be sorted in any particular order even with a third argument)
112 # on success output is <prefix1> or <prefix2) (with trailing slashes stripped)
113 # on error (both prefixes found and status 65) there's no output but -e will
114 # change that to be success with <prefix1> instead; using -n will cause an
115 # exit with status 66 if neither prefix is found rather than using <prefix1>
117 run_awk_require
"ref_prefixes"
118 run_awk_ref_prefixes
()
123 while [ $# -gt 0 ]; do case "$1" in
128 -?
*) run_awk_bug
"unknown run_awk_ref_prefixes option: $1";;
131 [ $# -ge 2 ] && [ $# -le 3 ] ||
132 run_awk_bug
"run_awk_ref_prefixes requires exactly two or three non-option arguments"
133 awk -f "$TG_INST_AWKDIR/ref_prefixes" \
134 -v "pckdrefs=$_ra_pckdrefs" \
135 -v "noerr=$_ra_noerr" \
136 -v "nodef=$_ra_nodef" \
142 # run_awk_topgit_branches [opts] <bases-prefix> [<for-each-ref-pattern>...]
144 # -n omit annihilated branches from output and -b=<file> (but not -a=<file>)
145 # -h=<head> full ref prefix of heads location (default usually works fine)
146 # -a=<file> write the TopGit branch names one per line of annihilated branches
147 # -b=<file> write a copy of the stdout into here
148 # -p=<file> write a copy of the ref prepare output into here
149 # -r=<file> read substitute refs list from here
150 # -p refs list is in packed-refs format rather than "<ref> <hash>"
151 # -rmr remove -r= <file> after it's been read (convenience knob)
152 # -i=<inbr> whitespace separated list of branch names to include (unless in -x)
153 # -x=<exbr> whitespace separated list of branch names to exclude
154 # -db enable output of .topdeps blob lines
155 # -mb enable output of .topmsg blob lines
156 # -td=<tdbr> must be <branch>:<hash> to use <hash>^{blob} as <branch>'s .topdeps
157 # -tm=<tmbr> must be <branch>:<hash> to use <hash>^{blob} as <branch>'s .topmsg
159 # If given, the -a and -b files are always truncated if there are no matching
162 # input is a full top-bases prefix (i.e. "refs/top-bases" or
163 # "refs/remotes/origin/top-bases" or similar) and zero or more for-each-ref
164 # patterns the default being <bases-prefix> if no patterns are given
166 # note that <bases-prefix> is required because this file is independent and
167 # there is no universal default available
169 # output is one TopGit branch name per line (not output until after any -a or -b
170 # files have been fully written and closed)
172 # if -n is used annihilated branches will be omitted from the output (and any
175 # If -b=<file> is used it will get a copy of the output being truncated if the
178 # If -a=<file> is used it will get a list of annihilated branches (regardless of
179 # whether or not -n is used) and will be truncated if there are none
181 # Note that since git for-each-ref sorts its results the output will always be
182 # sorted by branch name (including the -a=<file> output)
184 # Note that this function does NOT call get_temp
186 run_awk_require
"ref_match" "ref_prepare" "topgit_branches"
187 run_awk_topgit_branches
()
203 while [ $# -gt 0 ]; do case "$1" in
206 -h=*) _ra_headbase
="${1#-h=}";;
207 -a=*) _ra_anfile
="${1#-a=}";;
208 -b=*) _ra_brfile
="${1#-b=}";;
209 -i=*) _ra_inclbr
="${1#-i=}";;
210 -x=*) _ra_exclbr
="${1#-x=}";;
211 -db) _ra_depsblob
=1;;
213 -td=*) _ra_topdeps
="${1#-td=}";;
214 -tm=*) _ra_topmsg
="${1#-tm=}";;
215 -p=*) _ra_teeout
="${1#-p=}";;
216 -r=*) _ra_refsfile
="${1#-r=}";;
219 -?
*) run_awk_bug
"unknown run_awk_topgit_branches option: $1";;
222 [ -n "$1" ] || run_awk_bug
"run_awk_topgit_branches missing <bases-prefix>"
225 [ $# -gt 0 ] ||
set -- "$_ra_topbases"
227 if [ -n "$_ra_refsfile" ]; then
228 run_awk_ref_match
${_ra_pckdrefs:+-p} <"$_ra_refsfile" \
229 --format="%(refname)" "$@"
231 git for-each-ref
--format="%(refname)" "$@"
234 awk -f "$TG_INST_AWKDIR/ref_prepare" \
235 -v "topbases=$_ra_topbases" \
236 -v "headbase=$_ra_headbase" \
237 -v "rmrf=$_ra_rmrf" \
238 -v "refsfile=$_ra_refsfile" \
239 -v "chkblob=$mtblob" \
240 -v "depsblob=$_ra_depsblob" \
241 -v "msgblob=$_ra_msgblob" \
242 -v "topdeps=$_ra_topdeps" \
243 -v "topmsg=$_ra_topmsg" \
244 -v "pckdrefs=$_ra_pckdrefs" \
245 -v "teeout=$_ra_teeout" |
246 git cat-file
$gcfbopt --batch-check='%(objectname) %(objecttype) %(rest)' |
247 awk -f "$TG_INST_AWKDIR/topgit_branches" \
248 -v "noann=$_ra_noann" \
249 -v "anfile=$_ra_anfile" \
250 -v "brfile=$_ra_brfile" \
251 -v "inclbr=$_ra_inclbr" \
252 -v "exclbr=$_ra_exclbr"
255 # run_awk_topgit_msg [opts] <bases-prefix> [<for-each-ref-pattern>...]
257 # -c do simple column formatting if more than one output column
258 # -n omit annihilated branches from output and -b=<file> (but not -a=<file>)
259 # -nokind omit the kind column from the output
260 # -mt=<mt> empty branch treatment ("" = like ann; true = include; false not)
261 # -h=<head> full ref prefix of heads location (default usually works fine)
262 # -a=<file> write the TopGit branch names one per line of annihilated branches
263 # -b=<file> write the TopGit branch names one per line into here
264 # -p=<file> write a copy of the ref prepare output into here
265 # -r=<file> read substitute refs list from here
266 # -p refs list is in packed-refs format rather than "<ref> <hash>"
267 # -rmr remove -r= <file> after it's been read (convenience knob)
268 # -i=<inbr> whitespace separated list of branch names to include (unless in -x)
269 # -x=<exbr> whitespace separated list of branch names to exclude
270 # -db ignored (output of .topdeps blob lines is always enabled)
271 # -mb ignored (output of .topmsg blob lines is always enabled)
272 # -td=<tdbr> must be <branch>:<hash> to use <hash>^{blob} as <branch>'s .topdeps
273 # -tm=<tmbr> must be <branch>:<hash> to use <hash>^{blob} as <branch>'s .topmsg
274 # --list a convenience macro that sets -c -n -mt=1 -nokind
276 # If given, the -a and -b files are always truncated if there are no matching
279 # input is a full top-bases prefix (i.e. "refs/top-bases" or
280 # "refs/remotes/origin/top-bases" or similar) and zero or more for-each-ref
281 # patterns the default being <bases-prefix> if no patterns are given
283 # note that <bases-prefix> is required because this file is independent and
284 # there is no universal default available
286 # output is 0 or more branch lines with .topmsg "Subject:" descriptions
287 # in the same order they appear on the input in this format:
289 # <TopGit_branch_name> K description of the TopGit branch
291 # But if nokind is true the "K" field will be omitted; K can take these values:
293 # 0 non-annihilated, non-empty branch WITH a .topmsg file
294 # 1 non-annihilated, non-empty branch WITHOUT a .topmsg file
295 # 2 annihilated branch
296 # 3 empty branch (an empty branch has the same branch and base commit hash)
297 # 4 bare branch (same as 1 AND also WITHOUT a .topdeps file)
299 # output does not appear until after any -a or -b files have been fully written
302 # if -n is used annihilated branches will be omitted from the output (and any
303 # -b=<file> if used), but if -a=<file> is NOT used then get_temp WILL be called
304 # but the file will be subsequently removed before returning
306 # Note that using -a=<file> does NOT exclude annihilated branches (but does
307 # put them into that file); only the -n option will exclude annihilated branches
309 # If -b=<file> is used it will get a copy of the output being truncated if the
312 # If -a=<file> is used it will get a list of annihilated branches (regardless of
313 # whether or not -n is used) and will be truncated if there are none
315 # Note that since git for-each-ref sorts its results the output will always be
316 # sorted by branch name (always for the -a=<file> and -b=<file> output) but
317 # that will only be the case for the stdout results for the first (second with
318 # -r) branch name on each stdout line
320 # Note that this function WILL call get_temp if -a= is NOT given AND -n is used
321 # (but it will remove the temp file before returning)
323 run_awk_require
"ref_match" "ref_prepare" "topgit_msg_prepare" "topgit_msg"
343 while [ $# -gt 0 ]; do case "$1" in
347 -mt=*) _ra_withmt
="${1#-mt=}";;
348 -nokind) _ra_nokind
=1;;
349 -h=*) _ra_headbase
="${1#-h=}";;
350 -a=*) _ra_anfile
="${1#-a=}";;
351 -b=*) _ra_brfile
="${1#-b=}";;
352 -r=*) _ra_refsfile
="${1#-r=}";;
354 -i=*) _ra_inclbr
="${1#-i=}";;
355 -x=*) _ra_exclbr
="${1#-x=}";;
358 -tm=*) _ra_topmsg
="${1#-tm=}";;
359 -td=*) _ra_topdeps
="${1#-td=}";;
360 -p=*) _ra_teeout
="${1#-p=}";;
368 -?
*) run_awk_bug
"unknown run_awk_topgit_msg option: $1";;
371 [ -n "$1" ] || run_awk_bug
"run_awk_topgit_msg missing <bases-prefix>"
374 [ $# -gt 0 ] ||
set -- "$_ra_topbases"
375 if [ -z "$_ra_withan" ] && [ -z "$_ra_anfile" ]; then
376 _ra_anfile
="$(get_temp rawk_annihilated)" ||
return 2
379 if [ -n "$tgbin" ] && [ -x "$tgbin" ]; then
380 TG_BIN_ABS
="$tgbin" && export TG_BIN_ABS
381 _ra_misscmd
='eval "$TG_BIN_ABS" --make-empty-blob'
384 if [ -n "$_ra_refsfile" ]; then
385 run_awk_ref_match
${_ra_pckdrefs:+-p} <"$_ra_refsfile" \
386 --format="%(refname)" "$@"
388 git for-each-ref
--format="%(refname)" "$@"
391 awk -f "$TG_INST_AWKDIR/ref_prepare" \
392 -v "topbases=$_ra_topbases" \
393 -v "headbase=$_ra_headbase" \
394 -v "rmrf=$_ra_rmrf" \
395 -v "refsfile=$_ra_refsfile" \
396 -v "chkblob=$mtblob" \
399 -v "topdeps=$_ra_topdeps" \
400 -v "topmsg=$_ra_topmsg" \
401 -v "pckdrefs=$_ra_pckdrefs" \
402 -v "teeout=$_ra_teeout" |
403 git cat-file
$gcfbopt --batch-check='%(objectname) %(objecttype) %(rest)' |
404 awk -f "$TG_INST_AWKDIR/topgit_msg_prepare" \
405 -v "withan=$_ra_withan" \
406 -v "withmt=$_ra_withmt" \
408 -v "anfile=$_ra_anfile" \
409 -v "brfile=$_ra_brfile" \
410 -v "missing=$mtblob" \
411 -v "misscmd=$_ra_misscmd" |
412 git cat-file
$gcfbopt --batch='%(objecttype) %(objectsize) %(rest)' |
tr '\0' '\27' |
413 awk -f "$TG_INST_AWKDIR/topgit_msg" \
414 -v "withan=$_ra_withan" \
415 -v "anfile=$_ra_anfile" \
416 -v "rman=$_ra_rman" \
417 -v "withmt=$_ra_withmt" \
418 -v "brfile=$_ra_brfile" \
419 -v "rmbr=$_ra_rmbr" \
420 -v "nokind=$_ra_nokind" \
421 -v "colfmt=$_ra_colfmt" \
422 -v "inclbr=$_ra_inclbr" \
423 -v "exclbr=$_ra_exclbr"
426 # run_awk_topmsg_header [-r=<regex>] [--stdin] [opts] <branch_name> [blobish]
428 # -kind include the kind field just before the description (normally omitted)
429 # -name include the branch name field as a first field (normally omitted)
430 # -c do simple column formatting if more than one output column
431 # -r=<regx> auto-anchored case-insenstive keyword match (default is "subject")
432 # --stdin fake stdin being a blob and use that
433 # -tm=<blb> blob to use as branch's .topmsg (any leading <foo>: is stripped off)
435 # with --stdin, the contents of a .topmsg file should be on stdin
436 # otherwise if blobish is given it provides the input otherwise
437 # refs/heads/<branch_name>:.topdeps^{blob} does (or -tm=<blb> if used)
439 # --stdin and -tm= are incompatible
441 # with -r=<regex> a case-insensitive keyword regular expression (always
442 # has to match the entire keyword name) instead of "Subject" can be used
443 # to extract other headers ("Subject" is the default though) and if it starts
444 # with a "+" all matches including their "pretty" keyword prefix will be output
446 # output will be the subject (or a suitable description if there is no .topmsg
447 # or blobish or the input is empty); for non-subject keywords if the header
448 # is not present just the empty string is output use -r="(Subject)" to do the
449 # same thing for the subject header and avoid descriptive "missing" text
451 # see run_awk_topgit_msg description for description of output when -kind,
452 # -name and/or -c are used
454 # "<branch_name>" is always required because it's used in the output message
455 # whenever the "Subject:" header line is not present (for whatever reason)
457 run_awk_require
"topgit_msg_prepare" "topgit_msg"
458 run_awk_topmsg_header
()
468 while [ $# -gt 0 ]; do case "$1" in
472 -r=*) _ra_kwregex
="${1#-r=}";;
473 --stdin) _usestdin
=1;;
474 -tm=*) _ra_topmsg
="${1#-tm=}"; _ra_topmsg
="${_ra_topmsg#*:}";;
476 -?
*) run_awk_bug
"unknown run_awk_topmsg_header option: $1";;
479 if [ -n "$_usestdin" ]; then
480 [ $# -eq 1 ] || run_awk_bug
"run_awk_topmsg_header --stdin requires exactly 1 arg"
481 [ -n "$1" ] || run_awk_bug
"run_awk_topmsg_header --stdin requires a branch name"
482 [ -z "$_ra_topmsg" ] || run_awk_bug
"run_awk_topmsg_header --stdin prohibits -tm=<blb>"
484 [ $# -le 2 ] || run_awk_bug
"run_awk_topmsg_header allows at most 2 args"
485 [ $# -ge 1 ] && [ -n "$1" ] ||
486 run_awk_bug
"run_awk_topmsg_header requires a branch name"
488 if [ -n "$_usestdin" ]; then
489 printf 'blob 32767 0 %s\n' "$1"
492 if [ -n "$tgbin" ] && [ -x "$tgbin" ]; then
493 TG_BIN_ABS
="$tgbin" && export TG_BIN_ABS
494 _ra_misscmd
='eval "$TG_BIN_ABS" --make-empty-blob'
496 if [ -n "$_ra_topmsg" ]; then
497 _ra_topmsg
="$_ra_topmsg^{blob}"
500 _ra_topdeps
='"refs/heads/$1^{tree}:.topdeps"'
501 _ra_topmsg
="refs/heads/$1^{tree}:.topmsg"
503 eval printf '"%s\n"' '"$mtblob^{blob} check ?"' \
504 '"refs/$topbases/$1 $1 :" "refs/$topbases/$1^0"' \
505 '"refs/heads/$1^0" "refs/$topbases/$1^{tree}"' \
506 '"refs/heads/$1^{tree}"' "$_ra_topdeps" '"$_ra_topmsg"' |
507 git cat-file
--batch-check='%(objectname) %(objecttype) %(rest)' |
508 awk -f "$TG_INST_AWKDIR/topgit_msg_prepare" \
509 -v "depsblob=$_ra_depsblob" \
511 -v "missing=$mtblob" \
512 -v "misscmd=$_ra_misscmd" |
513 git cat-file
$gcfbopt --batch='%(objecttype) %(objectsize) %(rest)'
515 awk -f "$TG_INST_AWKDIR/topgit_msg" \
516 -v "noname=$_ra_noname" \
517 -v "nokind=$_ra_nokind" \
518 -v "kwregex=$_ra_kwregex" \
521 -v "colfmt=$_ra_colfmt"
524 # run_awk_topgit_deps [opts] <bases-prefix> [<for-each-ref-pattern>...]
526 # -n omit annihilated branches from output and -b=<file> (but not -a=<file>)
527 # -t only output tgish deps (i.e. dep is listed in -b=<file>)
528 # -r reverse the dependency graph
529 # -s include a link to itself for each branch
530 # -h=<head> full ref prefix of heads location (default usually works fine)
531 # -a=<file> write the TopGit branch names one per line of annihilated branches
532 # -b=<file> write the TopGit branch names one per line into here
533 # -p=<file> write a copy of the ref prepare output into here
534 # -r=<file> read substitute refs list from here
535 # -p refs list is in packed-refs format rather than "<ref> <hash>"
536 # -rmr remove -r= <file> after it's been read (convenience knob)
537 # -i=<inbr> whitespace separated list of branch names to include (unless in -x)
538 # -x=<exbr> whitespace separated list of branch names to exclude
539 # -db ignored (output of .topdeps blob lines is always enabled)
540 # -mb enable output of .topmsg blob lines
541 # -td=<tdbr> must be <branch>:<hash> to use <hash>^{blob} as <branch>'s .topdeps
542 # -tm=<tmbr> must be <branch>:<hash> to use <hash>^{blob} as <branch>'s .topmsg
544 # If given, the -a and -b files are always truncated if there are no matching
547 # input is a full top-bases prefix (i.e. "refs/top-bases" or
548 # "refs/remotes/origin/top-bases" or similar) and zero or more for-each-ref
549 # patterns the default being <bases-prefix> if no patterns are given
551 # note that <bases-prefix> is required because this file is independent and
552 # there is no universal default available
554 # output is one TopGit branch edge (i.e. two space-separated TopGit branch
555 # names) per line (not output until after any -a or -b files have been fully
556 # written and closed)
558 # if -n is used annihilated branches will be omitted from the output (and any
559 # -b=<file> if used), but if -a=<file> is NOT used then get_temp WILL be called
560 # but the file will be subsequently removed before returning
562 # Note that using -a=<file> does NOT exclude annihilated branches (but does
563 # put them into that file); only the -n option will exclude annihilated branches
565 # If -b=<file> is used it will get a copy of the output being truncated if the
568 # If -a=<file> is used it will get a list of annihilated branches (regardless of
569 # whether or not -n is used) and will be truncated if there are none
571 # Note that since git for-each-ref sorts its results the output will always be
572 # sorted by branch name (always for the -a=<file> and -b=<file> output) but
573 # that will only be the case for the stdout results for the first (second with
574 # -r) branch name on each stdout line
576 # With -s a "self" link is output for each branch after all the .topdeps
577 # entries (before with -r) have been output for that branch and the entries are
578 # output in .topdeps order (reverse order with -r)
580 # Note that using -s and omitting -n is enough to make a self link for
581 # annihilated branches appear in the output because the empty blob will be
582 # automatically used (and first created if necessary) for any missing .topdeps
585 # Each "link" line output is <branch_name> <.topdeps_entry> (reversed with -r)
587 # Note that this function WILL call get_temp if -a= is NOT given AND -n is used
588 # OR -b= is NOT given AND -t is used
589 # (but it will remove the temp file(s) before returning)
591 run_awk_require
"ref_match" "ref_prepare" "topgit_deps_prepare" "topgit_deps"
592 run_awk_topgit_deps
()
613 while [ $# -gt 0 ]; do case "$1" in
619 -h=*) _ra_headbase
="${1#-h=}";;
620 -a=*) _ra_anfile
="${1#-a=}";;
621 -b=*) _ra_brfile
="${1#-b=}";;
622 -r=*) _ra_refsfile
="${1#-r=}";;
624 -i=*) _ra_inclbr
="${1#-i=}";;
625 -x=*) _ra_exclbr
="${1#-x=}";;
628 -tm=*) _ra_topmsg
="${1#-tm=}";;
629 -td=*) _ra_topdeps
="${1#-td=}";;
630 -p=*) _ra_teeout
="${1#-p=}";;
632 -?
*) run_awk_bug
"unknown run_awk_topgit_deps option: $1";;
635 [ -n "$1" ] || run_awk_bug
"run_awk_topgit_deps missing <bases-prefix>"
638 [ $# -gt 0 ] ||
set -- "$_ra_topbases"
639 if [ -n "$_ra_noann" ] && [ -z "$_ra_anfile" ]; then
641 _ra_anfile
="$(get_temp rawk_annihilated)" ||
return 2
643 if [ -n "$_ra_tgonly" ] && [ -z "$_ra_brfile" ]; then
645 _ra_brfile
="$(get_temp rawk_branches)" ||
return 2
647 [ -n "$_ra_noann" ] || _ra_withan
=1
649 if [ -n "$tgbin" ] && [ -x "$tgbin" ]; then
650 TG_BIN_ABS
="$tgbin" && export TG_BIN_ABS
651 _ra_misscmd
='eval "$TG_BIN_ABS" --make-empty-blob'
654 if [ -n "$_ra_refsfile" ]; then
655 run_awk_ref_match
${_ra_pckdrefs:+-p} <"$_ra_refsfile" \
656 --format="%(refname)" "$@"
658 git for-each-ref
--format="%(refname)" "$@"
661 awk -f "$TG_INST_AWKDIR/ref_prepare" \
662 -v "topbases=$_ra_topbases" \
663 -v "headbase=$_ra_headbase" \
664 -v "rmrf=$_ra_rmrf" \
665 -v "refsfile=$_ra_refsfile" \
666 -v "chkblob=$mtblob" \
668 -v "msgblob=$_ra_msgblob" \
669 -v "topdeps=$_ra_topdeps" \
670 -v "topmsg=$_ra_topmsg" \
671 -v "pckdrefs=$_ra_pckdrefs" \
672 -v "teeout=$_ra_teeout" |
673 git cat-file
$gcfbopt --batch-check='%(objectname) %(objecttype) %(rest)' |
674 awk -f "$TG_INST_AWKDIR/topgit_deps_prepare" \
675 -v "noann=$_ra_noann" \
676 -v "anfile=$_ra_anfile" \
677 -v "brfile=$_ra_brfile" \
678 -v "missing=$mtblob" \
679 -v "misscmd=$_ra_misscmd" |
680 git cat-file
$gcfbopt --batch='%(objecttype) %(objectsize) %(rest)' |
tr '\0' '\27' |
681 awk -f "$TG_INST_AWKDIR/topgit_deps" \
682 -v "withan=$_ra_withan" \
683 -v "anfile=$_ra_anfile" \
684 -v "rman=$_ra_rman" \
685 -v "withbr=$_ra_withbr" \
686 -v "brfile=$_ra_brfile" \
687 -v "rmbr=$_ra_rmbr" \
688 -v "tgonly=$_ra_tgonly" \
690 -v "inclbr=$_ra_inclbr" \
691 -v "exclbr=$_ra_exclbr"
694 # run_awk_topgit_recurse [opts] <branch> [<name>...]
696 # -d output ":loop: some bad branch chain path" for any detected loops
697 # -n omit annihilated branches (L == 2) from output
698 # -f output branch first before dependencies (i.e. preorder not post)
699 # -s include a line for the starting node (recommended when using -m)
700 # -l only output L == 1 lines (leaves)
701 # -t only output T != 0 lines (tgish)
702 # -m activate multimode, any [<name>...] are more branches to process
703 # -e=<type> emit only dependency graph edges (2) or deps (1) (aka filter mode)
704 # -o=<once> only output node on 1st visit (1) or visit its deps only 1st (-1)
705 # -u=<remt> prefix for -r=<file> branch names (e.g. "refs/remotes/x/top-bases")
706 # -c=<hfld> use only field <hfld> from -h= and cut off leading "refs/heads/"
707 # -b=<file> file with list of TopGit branches (one per line, e.g. "t/foo")
708 # -rmb remove -b= <file> after it's been read (convenience knob)
709 # -a=<file> file with list of annihilated TopGit branches
710 # -rma remove -a= <file> after it's been read (convenience knob)
711 # -h=<file> file with list of existing "refs/heads/..." refs (see -c=)
712 # -rmh remove -h= <file> after it's been read (convenience knob)
713 # -r=<file> file with list of TopGit branches with a remote (one per line)
714 # -rmr remove -r= <file> after it's been read (convenience knob)
715 # -i=<inbr> whitespace separated list of branch names to include (unless in -x)
716 # -x=<exbr> whitespace separated list of branch names to exclude
717 # --series a convenience macro that sets -e=1 -o=1 -s -n -t options
719 # REQUIRED OPTIONS: -a=<file> -b=<file>
721 # But, if -h=<file> is omitted, get_temp WILL be called!
723 # Note that it is guaranteed to be safe to use the same name for the -a and -b
724 # options of a run_awk_topgit_deps command that is piping output into this
725 # command because they are guaranteed to be fully written and closed by that
726 # command before it outputs the first line and this command is guaranteed to
727 # not start reading them until after it receives the first line
729 # if the -c=<hfld> option is NOT used then the -h= <file> must contain only the
730 # branch name (e.g. "t/foo") on each line; but if -c=<hfld> IS used then the
731 # chosen field (use -c=2 for packed-refs) must be a full ref starting with the
732 # "refs/heads/" prefix; a file with one full refname per line (i.e. starts with
733 # "refs/heads/") instead of just the branch name should use -c=1 for it to work
735 # note that no matter what options are used, missing lines (i.e. M == 1) are
736 # always output when applicable (any branch name not found in the -h= <file>
737 # will always generate a missing line)
739 # if -r=<file> is omitted no remote lines will ever be generated and the
740 # -u=<remt> option will be ignored; if -r=<file> is used but -u=<remt> is
741 # omitted then remote base lines will never be generated, but any branch
742 # names in both -b=<file> AND -r=<file> but NOT -a=<file> will still have a
743 # T == 2 value in their output lines
745 # With -s a "starting" line is output for <branch> (and each additional <name>
746 # when using -m) either before (with -f) or after (the default) recursively
747 # processing all of that branch's dependencies
749 # if both -s AND -m are used AND at least two nodes are specified the effect
750 # on the output is as though a virtual starting node was passed that had a
751 # .topdeps file consisting of the nodes listed on the command line (in command
752 # line order) AND the -s flag was than not passed when recursing on that
753 # fictional virtual node; if -m and more than one node is used without the
754 # -s option it will not generally be possible to determine where the output
755 # from one node's recursion ends and the next begins in the output
757 # input is the output of run_awk_topgit_deps and despite the availability of
758 # the -n option here, if it was used on the run_awk_topgit_deps command line
759 # the links to annihilated branches will not be present in the input so the
760 # only possible L == 2 line in that case (if -n is NOT used) would be for a
761 # starting <branch> and then only if -s IS used; this is usually fine because
762 # in most cases the annihilated branches need to disappear anyway
764 # output lines look like this:
766 # M T L V <node> [<parent> <branch> <chain> <names>]
768 # where (see awk_topgit_recurse) M is 0 (not) or 1 (missing), T is 0 (not)
769 # or 1 (tgish) or 2 (with remote) and L is 0 (not) or 1 (leaf) or 2 (annihilated)
770 # and V is a non-negative integer number of excess visits to the node
771 # so the following is an example of a possible output line:
773 # 0 1 1 0 t/foo/leaf t/foo/int t/stage
775 # Note that unlike most of the other run_awk_... functions, this function is
776 # primarily just a convenience wrapper around the awk_topgit_recurse script
777 # (although it will provide the required -h=<file> if necessary) and
778 # really does not provide any additional and/or changed behavior so the script
779 # may also be used directly (by running awk) instead of via this function
781 # however, if "-o" mode is active instead of the above example output line
782 # this line would be output instead:
784 # t/foo/int t/foo/leaf
786 # note the reversed ordering as the first branch name is the branch with the
787 # .topdeps file that contains the second branch name
789 # Note that this function WILL call get_temp if -h=<file> is NOT given
791 run_awk_require
"topgit_recurse"
792 run_awk_topgit_recurse
()
815 while [ $# -gt 0 ]; do case "$1" in
823 --filter=*) _ra_filter
="${1#--filter=}";;
824 --emit=*) _ra_filter
="${1#--emit=}";;
825 -e=*) _ra_filter
="${1#-e=}";;
826 -o=*) _ra_once
="${1#-o=}";;
827 -u=*) _ra_usermt
="${1#-u=}";;
828 -c=*) _ra_cuthd
="${1#-c=}";;
829 -b=*) _ra_brfile
="${1#-b=}";;
830 -a=*) _ra_anfile
="${1#-a=}";;
831 -h=*) _ra_hdfile
="${1#-h=}";;
832 -r=*) _ra_rtfile
="${1#-r=}";;
837 -i=*) _ra_inclbr
="${1#-i=}";;
838 -x=*) _ra_exclbr
="${1#-x=}";;
839 --series|
--patch-series)
847 -?
*) run_awk_bug
"unknown run_awk_topgit_recurse option: $1";;
850 [ -z "${_ra_filter#[12]}" ] ||
851 run_awk_bug
"run_awk_topgit_recurse -e filter value must be 1 or 2"
852 [ -z "${_ra_once#[1]}" ] ||
[ "$_ra_once" = "-1" ] ||
853 run_awk_bug
"run_awk_topgit_recurse -o value must be 1 or -1"
854 [ -n "$_ra_brfile" ] || run_awk_bug
"run_awk_topgit_recurse missing required -b=<file>"
855 [ -n "$_ra_anfile" ] || run_awk_bug
"run_awk_topgit_recurse missing required -a=<file>"
856 [ -n "$1" ] || run_awk_bug
"run_awk_topgit_recurse missing <branch>"
857 if [ -z "$_ra_hdfile" ]; then
858 _ra_hdfile
="$(get_temp rawk_heads)" ||
return 2
860 git for-each-ref
--format="%(refname)" "refs/heads" >"$_ra_hdfile" ||
{
867 awk -f "$TG_INST_AWKDIR/topgit_recurse" \
868 -v "showlp=$_ra_showlp" \
869 -v "withan=$_ra_withan" \
870 -v "preord=$_ra_preord" \
871 -v "withbr=$_ra_withbr" \
872 -v "leaves=$_ra_leaves" \
873 -v "multib=$_ra_multib" \
874 -v "usermt=$_ra_usermt" \
875 -v "cuthd=$_ra_cuthd" \
876 -v "brfile=$_ra_brfile" \
877 -v "anfile=$_ra_anfile" \
878 -v "hdfile=$_ra_hdfile" \
879 -v "rtfile=$_ra_rtfile" \
880 -v "rmbr=$_ra_rmbr" \
881 -v "rman=$_ra_rman" \
882 -v "rmhd=$_ra_rmhd" \
883 -v "rmrt=$_rt_rmrt" \
884 -v "tgonly=$_ra_tgonly" \
885 -v "inclbr=$_ra_inclbr" \
886 -v "exclbr=$_ra_exclbr" \
888 -v "filter=$_ra_filter" \
892 # run_awk_topgit_navigate [opts] <branch> [<head>...]
894 # -d do loop detection even when not strictly necessary
895 # -n omit annihilated branches (default)
896 # -N do NOT omit annihilated branches from output
897 # -r reverse direction for movement
898 # -k keep last node when running off end
899 # -t include only TopGit branches in the output (ineffective without -b=)
900 # -1 omit all but the first field of each output line
901 # -s=<steps> how many steps to move
902 # -b=<file> file with list of TopGit branches (one per line, e.g. "t/foo")
903 # -rmb remove -b= <file> after it's been read (convenience knob)
904 # -a=<file> file with list of annihilated TopGit branches
905 # -rma remove -a= <file> after it's been read (convenience knob)
906 # -i=<inbr> whitespace separated list of branch names to include (unless in -x)
907 # -x=<exbr> whitespace separated list of branch names to exclude
909 # The [<head>...] list is actually a tree pruning list of nodes similar to
910 # git rev-list except that an isolated "^" replaces the "--not" functionality
911 # and if there are no positive nodes listed all nodes are considered positive
912 # before applying the negative node references
914 # input is the output of run_awk_topgit_deps and despite the availability of
915 # the -N option here, if -n was used on the run_awk_topgit_deps command line
916 # the links to annihilated branches will not be present in the input so they
917 # cannot be output in that case; to avoid missing hermit nodes
918 # run_awk_topgit_deps should be run with the -s otherwise those nodes will be
919 # invisible to navigation (which is occasionally useful)
921 # output nodes are written one per line in this format:
923 # <result_branch_name> <containing_topgit_head_branch_name>...
925 # where the second field will be empty only if navigating 1 or a negative
926 # number of steps from no node (i.e. ""); note that pruning IS allowed
927 # with the "no node" value but then the "no node" must be explicitly given
928 # as "" in order to avoid interpreting the first pruning ref as the starting
929 # node; and it's also possible to provide multiple space-separated starting
930 # nodes provided they are quoted together into the first argument
933 run_awk_require
"topgit_navigate"
934 run_awk_topgit_navigate
()
949 while [ $# -gt 0 ]; do case "$1" in
954 -k|
--pin) _ra_pin
=1;;
957 -s=*) _ra_steps
="${1#-s=}";;
958 -b=*) _ra_brfile
="${1#-b=}";;
959 -a=*) _ra_anfile
="${1#-a=}";;
962 -i=*) _ra_inclbr
="${1#-i=}";;
963 -x=*) _ra_exclbr
="${1#-x=}";;
965 -?
*) run_awk_bug
"unknown run_awk_topgit_navigate option: $1";;
968 [ -n "$_ra_steps" ] ||
969 run_awk_bug
"run_awk_topgit_navigate -s steps value must not be empty"
971 case "$_ra_steps" in -[0-9]*|
[0-9]*)
972 badsteps
= _t
="${_ra_steps#-}" && [ "$_t" = "${_t%%[!0-9]*}" ] || badsteps
=1
974 [ -z "$badsteps" ] ||
975 run_awk_bug
"run_awk_topgit_navigate -s steps value must be an integer"
977 [ $# -eq 0 ] ||
{ _ra_startb
="$1"; shift; }
978 awk -f "$TG_INST_AWKDIR/topgit_navigate" \
979 -v "chklps=$_ra_chklps" \
980 -v "withan=$_ra_withan" \
982 -v "steps=$_ra_steps" \
983 -v "brfile=$_ra_brfile" \
984 -v "anfile=$_ra_anfile" \
985 -v "rmbr=$_ra_rmbr" \
986 -v "rman=$_ra_rman" \
987 -v "tgonly=$_ra_tgonly" \
988 -v "fldone=$_ra_fldone" \
990 -v "inclbr=$_ra_inclbr" \
991 -v "exclbr=$_ra_exclbr" \
992 -v "startb=$_ra_startb" \