tg: accomodate multiple git worktrees
[topgit/pro.git] / tg-tag.sh
blob9f809d7e5e9e615a57a782598653e5ee7af7ef98
1 #!/bin/sh
2 # TopGit tag command
3 # Copyright (C) 2015 Kyle J. McKay <mackyle@gmail.com>
4 # All rights reserved.
5 # GPLv2
7 USAGE="\
8 Usage: ${tgname:-tg} [...] tag [-s | -u <key-id>] [-f] [-q] [--no-edit] [-m <msg> | -F <file>] (<tagname> | --refs) [<branch>...]
9 Or: ${tgname:-tg} [...] tag (-g | --reflog) [--reflog-message | --commit-message] [--no-type] [-n <number> | -number] [<tagname>]
10 Or: ${tgname:-tg} [...] tag (--clear | --delete) <tagname>
11 Or: ${tgname:-tg} [...] tag --drop <tagname>@{n}"
13 usage()
15 if [ "${1:-0}" != 0 ]; then
16 printf '%s\n' "$USAGE" >&2
17 else
18 printf '%s\n' "$USAGE"
20 exit ${1:-0}
23 ## Parse options
25 signed=
26 keyid=
27 force=
28 msg=
29 msgfile=
30 noedit=
31 defnoedit=
32 refsonly=
33 maxcount=
34 reflog=
35 outofdateok=
36 anyrefok=
37 defbranch=HEAD
38 stash=
39 anonymous=
40 reflogmsg=
41 notype=
42 setreflogmsg=
43 quiet=0
44 noneok=
45 clear=
46 delete=
47 drop=
48 anonymous=
50 is_numeric()
52 [ -n "$1" ] || return 1
53 while [ -n "$1" ]; do
54 case "$1" in
55 [0-9]*)
56 set -- "${1#?}";;
58 break;;
59 esac
60 done
61 [ -z "$1" ]
64 while [ $# -gt 0 ]; do case "$1" in
65 -h|--help)
66 usage
68 -q|--quiet)
69 quiet=$(( $quiet + 1 ))
71 --none-ok)
72 noneok=1
74 --clear)
75 clear=1
77 --delete)
78 delete=1
80 --drop)
81 drop=1
83 -g|--reflog|--walk-reflogs)
84 reflog=1
86 --reflog-message)
87 reflogmsg=1
88 setreflogmsg=1
90 --no-reflog-message|--commit-message)
91 reflogmsg=
92 setreflogmsg=1
94 --no-type)
95 notype=1
97 -s|--sign)
98 signed=1
100 -u|--local-user|--local-user=*)
101 case "$1" in --local-user=*)
102 x="$1"
103 shift
104 set -- --local-user "${x#--local-user=}" "$@"
105 esac
106 if [ $# -lt 2 ]; then
107 echo "The $1 option requires an argument" >&2
108 usage 1
110 shift
111 keyid="$1"
113 -f|--force)
114 force=1
116 --no-edit)
117 noedit=1
119 --edit)
120 noedit=0
122 --allow-outdated)
123 outofdateok=1
125 --allow-any)
126 anyrefok=1
128 --refs|--refs-only)
129 refsonly=1
131 -m|--message|--message=*)
132 case "$1" in --message=*)
133 x="$1"
134 shift
135 set -- --message "${x#--message=}" "$@"
136 esac
137 if [ $# -lt 2 ]; then
138 echo "The $1 option requires an argument" >&2
139 usage 1
141 shift
142 msg="$1"
144 -F|--file|--file=*)
145 case "$1" in --file=*)
146 x="$1"
147 shift
148 set -- --file "${x#--file=}" "$@"
149 esac
150 if [ $# -lt 2 ]; then
151 echo "The $1 option requires an argument" >&2
152 usage 1
154 shift
155 msgfile="$1"
157 -n|--max-count|--max-count=*|-[1-9]*)
158 case "$1" in --max-count=*)
159 x="$1"
160 shift
161 set -- --max-count "${x#--max-count=}" "$@"
162 esac
163 case "$1" in -[1-9]*)
164 x="${1#-}"
165 shift
166 set -- -n "$x" "$@"
167 esac
168 if [ $# -lt 2 ]; then
169 echo "The $1 option requires an argument" >&2
170 usage 1
172 shift
173 maxcount="$1"
176 shift
177 break
179 --all)
180 break
182 --stash|--stash"@{"*"}")
183 if [ -n "$reflog" ]; then
184 case "$2" in -[1-9]*)
185 x1="$1"
186 x2="$2"
187 shift
188 shift
189 set -- "$x2" "$x1" "$@"
190 continue
191 esac
193 stash=1
194 defbranch=--all
195 break
197 --anonymous)
198 anonymous=1
199 defbranch=--all
200 quiet=2
201 break
203 -?*)
204 echo "Unknown option: $1" >&2
205 usage 1
208 if [ -n "$reflog" ]; then
209 case "$2" in -[1-9]*)
210 x1="$1"
211 x2="$2"
212 shift
213 shift
214 set -- "$x2" "$x1" "$@"
215 continue
216 esac
218 break
220 esac; shift; done
222 [ "$stash$anonymous" != "11" ] || usage 1
223 [ -z "$stash$anonymous" -o -n "$reflog$drop$clear$delete" ] || { outofdateok=1; force=1; defnoedit=1; }
224 [ -n "$noedit" ] || noedit="$defnoedit"
225 [ "$noedit" != "0" ] || noedit=
226 [ -z "$reflog" -o -z "$drop$clear$delete$signed$keyid$force$msg$msgfile$noedit$refsonly$outofdateok" ] || usage 1
227 [ -n "$reflog" -o -z "$setreflogmsg$notype$maxcount" ] || usage 1
228 [ -z "$drop$clear$delete" -o -z "$setreflogmsg$notype$maxcount$signed$keyid$force$msg$msgfile$noedit$refsonly$outofdateok" ] || usage 1
229 [ -z "$reflog$drop$clear$delete" -o "$reflog$drop$clear$delete" = "1" ] || usage 1
230 [ -z "$maxcount" ] || is_numeric "$maxcount" || die "invalid count: $maxcount"
231 [ -z "$maxcount" ] || [ $maxcount -gt 0 ] || die "invalid count: $maxcount"
232 [ -z "$msg" -o -z "$msgfile" ] || die "only one -F or -m option is allowed."
233 [ -z "$refsonly" ] || set -- refs..only "$@"
234 [ $# -gt 0 -o -z "$reflog" ] || set -- --stash
235 [ -n "$1" ] || { echo "Tag name required" >&2; usage 1; }
236 tagname="$1"
237 shift
238 [ "$tagname" != "--stash" ] || tagname=refs/tgstash
239 [ "$tagname" != "--anonymous" ] || tagname=TG_STASH
240 case "$tagname" in --stash"@{"*"}")
241 strip="${tagname#--stash??}"
242 strip="${strip%?}"
243 tagname="refs/tgstash@{$strip}"
244 esac
245 refname="$tagname"
246 sfx=
247 case "$refname" in [!@]*"@{"*"}")
248 sfx="$refname"
249 refname="${refname%@*}"
250 sfx="${sfx#$refname}"
251 esac
252 case "$refname" in HEAD|TG_STASH|refs/*);;*)
253 if reftest="$(git rev-parse --revs-only --symbolic-full-name "$refname" -- 2>/dev/null)" &&
254 [ -n "$reftest" ]; then
255 if [ -n "$reflog$drop$clear$delete" ]; then
256 refname="$reftest"
257 else
258 case "$reftest" in
259 refs/tags/*|refs/tgstash)
260 refname="$reftest"
263 refname="refs/tags/$refname"
264 esac
266 else
267 refname="refs/tags/$refname"
269 esac
270 refname="$refname$sfx"
271 reftype=tag
272 case "$refname" in refs/tags/*) tagname="${refname#refs/tags/}";; *) reftype=ref; tagname="$refname"; esac
273 logbase="$git_common_dir"
274 [ "$refname" != "HEAD" ] || logbase="$git_dir"
275 [ -z "$reflog$drop$clear$delete" -o $# -eq 0 ] || usage 1
276 if [ -n "$drop$clear$delete" ]; then
277 if [ -n "$sfx" ]; then
278 [ -z "$clear$delete" ] || die "invalid ref name ($sfx suffix not allowed): $refname"
279 else
280 [ -z "$drop" ] || die "invalid reflog name (@{n} suffix required): $refname"
282 old="$(git rev-parse --verify --quiet --short "${refname%$sfx}" --)" || die "no such ref: ${refname%$sfx}"
283 if [ -n "$delete" ]; then
284 git update-ref -d "$refname" || die "git update-ref -d failed"
285 printf "Deleted $reftype '%s' (was %s)\n" "$tagname" "$old"
286 exit 0
287 elif [ -n "$clear" ]; then
288 [ -f "$logbase/logs/$refname" ] || die "no reflog found for: $refname"
289 [ -s "$logbase/logs/$refname" ] || die "empty reflog found for: $refname"
290 cp -p "$logbase/logs/$refname" "$logbase/logs/$refname^-+" || die "cp failed"
291 sed -n '$p' <"$logbase/logs/$refname^-+" >"$logbase/logs/$refname" || die "reflog clear failed"
292 rm -f "$logbase/logs/$refname^-+"
293 printf "Cleared $reftype '%s' reflog to single @{0} entry\n" "$tagname"
294 exit 0
295 else
296 old="$(git rev-parse --verify --short "$refname" --)" || exit 1
297 git reflog delete --rewrite --updateref "$refname" || die "reflog drop failed"
298 printf "Dropped $reftype '%s' reflog entry (was %s)\n" "$tagname" "$old"
299 exit 0
302 if [ -n "$reflog" ]; then
303 [ "$refname" = "refs/tgstash" -o -n "$setreflogmsg" ] || reflogmsg=1
304 git rev-parse --verify --quiet "$refname" -- >/dev/null ||
305 die "no such ref: $refname"
306 [ -s "$logbase/logs/$refname" ] ||
307 die "no reflog present for $reftype: $tagname"
308 showref="$(git rev-parse --revs-only --abbrev-ref=strict "$refname" --)"
309 hashcolor=
310 resetcolor=
311 if git config --get-colorbool color.tgtag; then
312 metacolor="$(git config --get-color color.tgtag.meta)"
313 [ -n "$metacolor" ] || metacolor="$(git config --get-color color.diff.meta "bold")"
314 hashcolor="$(git config --get-color color.tgtag.commit)"
315 [ -n "$hashcolor" ] || hashcolor="$(git config --get-color color.diff.commit "yellow")"
316 datecolor="$(git config --get-color color.tgtag.date "bold blue")"
317 timecolor="$(git config --get-color color.tgtag.time "green")"
318 resetcolor="$(git config --get-color "" reset)"
320 setup_strftime
321 output()
323 sed 's/[^ ][^ ]* //' <"$logbase/logs/$refname" |
324 awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--]}' |
325 git cat-file --batch-check='%(objectname) %(objecttype) %(rest)' |
327 stashnum=-1
328 lastdate=
329 while read -r newrev type rest; do
330 stashnum=$(( $stashnum + 1 ))
331 [ "$type" != "missing" ] || continue
332 IFS="$tab" read -r cmmttr msg <<-~EOT~
333 $rest
334 ~EOT~
335 ne="${cmmttr% *}"
336 ne="${ne% *}"
337 es="${cmmttr#$ne}"
338 es="${es% *}"
339 es="${es# }"
340 obj="$(git rev-parse --verify --quiet --short "$newrev" --)"
341 extra=
342 [ "$type" = "tag" -o -n "$notype" ] ||
343 extra="$hashcolor($metacolor$type$resetcolor$hashcolor)$resetcolor "
344 if [ -z "$reflogmsg" -o -z "$msg" ]; then
345 objmsg=
346 if [ "$type" = "tag" ]; then
347 objmsg="$(git cat-file tag "$obj" |
348 sed '1,/^$/d' | sed '/^$/,$d')"
349 elif [ "$type" = "commit" ]; then
350 objmsg="$(git log -n 1 --format='format:%s' "$obj" --)"
352 [ -z "$objmsg" ] || msg="$objmsg"
354 read newdate newtime <<-EOT
355 $(strftime "%Y-%m-%d %H:%M:%S" "$es")
357 if [ "$lastdate" != "$newdate" ]; then
358 printf '%s=== %s ===%s\n' "$datecolor" "$newdate" "$resetcolor"
359 lastdate="$newdate"
361 printf '%s %s %s%s@{%s}: %s\n' "$hashcolor$obj$reseutcolor" \
362 "$timecolor$newtime$resetcolor" \
363 "$extra" "$showref" "$stashnum" "$msg"
364 if [ -n "$maxcount" ]; then
365 maxcount=$(( $maxcount - 1 ))
366 [ $maxcount -gt 0 ] || break
368 done
371 page output
372 exit 0
374 [ -z "$signed" -o "$reftype" = "tag" ] || die "signed tags must be under refs/tags"
375 [ $# -gt 0 ] || set -- $defbranch
376 all=
377 if [ $# -eq 1 ] && [ "$1" = "--all" ]; then
378 eval set -- $(git for-each-ref --shell --format="%(refname)" "refs/$topbases")
379 outofdateok=1
380 all=1
381 if [ $# -eq 0 ]; then
382 if [ "$quiet" -gt 0 -a -n "$noneok" ]; then
383 exit 0
384 else
385 die "no TopGit branches found"
389 ensure_ident_available
390 branches=
391 allrefs=
392 extrarefs=
393 tgbranches=
394 tgcount=0
395 othercount=0
396 ignore=
397 newlist=
398 while read -r obj typ ref && [ -n "$obj" -a -n "$typ" ]; do
399 [ -n "$ref" -o "$typ" != "missing" ] || die "no such ref: ${obj%???}"
400 case " $ignore " in *" $ref "*) continue; esac
401 if [ "$typ" != "commit" -a "$typ" != "tag" ]; then
402 [ -n "$anyrefok" ] || die "not a committish (is a '$typ') ref: $ref"
403 [ "$quiet" -ge 2 ] || warn "ignoring non-committish (is a '$typ') ref: $ref"
404 ignore="${ignore:+$ignore }$ref"
405 continue
407 case " $newlist " in *" $ref "*);;*)
408 newlist="${newlist:+$newlist }$ref"
409 esac
410 if [ "$typ" = "tag" ]; then
411 [ "$quiet" -ge 2 ] || warn "storing as lightweight tag instead of 'tag' object: $ref"
412 ignore="${ignore:+$ignore }$ref"
414 done <<-EOT
416 printf '%s\n' "$@" | sed 's/^\(.*\)$/\1^{} \1/'
417 printf '%s\n' "$@" | sed 's/^\(.*\)$/\1 \1/'
419 git cat-file --batch-check='%(objectname) %(objecttype) %(rest)' 2>/dev/null ||
422 set -- $newlist
423 for b; do
424 sfn="$b"
425 [ -n "$all" ] ||
426 sfn="$(git rev-parse --revs-only --symbolic-full-name "$b" -- 2>/dev/null)" || :
427 [ -n "$sfn" ] || {
428 [ -n "$anyrefok" ] || die "no such symbolic ref name: $b"
429 fullhash="$(git rev-parse --verify --quiet "$b" --)" || die "no such ref: $b"
430 case " $extrarefs " in *" $b "*);;*)
431 [ "$quiet" -ge 2 ] || warn "including non-symbolic ref only in parents calculation: $b"
432 extrarefs="${extrarefs:+$extrarefs }$fullhash"
433 esac
434 continue
436 case "$sfn" in
437 refs/"$topbases"/*)
438 added=
439 tgish=1
440 ref_exists "refs/heads/${sfn#refs/$topbases/}" || tgish=
441 [ -n "$anyrefok" ] || [ -n "$tgish" ] || [ "$quiet" -ge 2 ] ||
442 warn "including TopGit base that's missing its head: $sfn"
443 case " $allrefs " in *" $sfn "*);;*)
444 allrefs="${allrefs:+$allrefs }$sfn"
445 esac
446 case " $branches " in *" ${sfn#refs/$topbases/} "*);;*)
447 branches="${branches:+$branches }${sfn#refs/$topbases/}"
448 added=1
449 esac
450 if [ -n "$tgish" ]; then
451 case " $allrefs " in *" refs/heads/${sfn#refs/$topbases/} "*);;*)
452 allrefs="${allrefs:+$allrefs }refs/heads/${sfn#refs/$topbases/}"
453 esac
454 case " $tgbranches " in *" ${sfn#refs/$topbases/} "*);;*)
455 tgbranches="${tgbranches:+$tgbranches }${sfn#refs/$topbases/}"
456 added=1
457 esac
458 [ -z "$added" ] || tgcount=$(( $tgcount + 1 ))
459 else
460 [ -z "$added" ] || othercount=$(( $othercount + 1 ))
463 refs/heads/*)
464 added=
465 tgish=1
466 ref_exists "refs/$topbases/${sfn#refs/heads/}" || tgish=
467 [ -n "$anyrefok" ] || [ -n "$tgish" ] ||
468 die "not a TopGit branch: ${sfn#refs/heads/} (use --allow-any option)"
469 case " $allrefs " in *" $b "*);;*)
470 allrefs="${allrefs:+$allrefs }$sfn"
471 esac
472 case " $branches " in *" ${sfn#refs/heads/} "*);;*)
473 branches="${branches:+$branches }${sfn#refs/heads/}"
474 added=1
475 esac
476 if [ -n "$tgish" ]; then
477 case " $allrefs " in *" refs/$topbases/${sfn#refs/heads/} "*);;*)
478 allrefs="${allrefs:+$allrefs }refs/$topbases/${sfn#refs/heads/}"
479 esac
480 case " $tgbranches " in *" ${sfn#refs/heads/} "*);;*)
481 tgbranches="${tgbranches:+$tgbranches }${sfn#refs/heads/}"
482 added=1
483 esac
484 [ -z "$added" ] || tgcount=$(( $tgcount + 1 ))
485 else
486 [ -z "$added" ] || othercount=$(( $othercount + 1 ))
490 [ -n "$anyrefok" ] || die "refusing to include without --allow-any: $sfn"
491 case " $allrefs " in *" $sfn "*);;*)
492 allrefs="${allrefs:+$allrefs }$sfn"
493 esac
494 case " $branches " in *" ${sfn#refs/} "*);;*)
495 branches="${branches:+$branches }${sfn#refs/}"
496 othercount=$(( $othercount + 1 ))
497 esac
499 esac
500 done
502 [ -n "$force" ] ||
503 ! git rev-parse --verify --quiet "$refname" -- >/dev/null ||
504 die "$reftype '$tagname' already exists"
506 desc="tg branch"
507 descpl="tg branches"
508 if [ $othercount -gt 0 ]; then
509 if [ $tgcount -eq 0 ]; then
510 desc="ref"
511 descpl="refs"
512 else
513 descpl="$descpl and refs"
517 get_dep() {
518 case " $seen_deps " in *" $_dep "*) return 0; esac
519 seen_deps="${seen_deps:+$seen_deps }$_dep"
520 printf 'refs/heads/%s\n' "$_dep"
521 [ -z "$_dep_is_tgish" ] || printf 'refs/%s/%s\n' "$topbases" "$_dep"
524 get_deps_internal()
526 no_remotes=1
527 recurse_deps_exclude=
528 for _b; do
529 case " $recurse_deps_exclude " in *" $_b "*) continue; esac
530 seen_deps=
531 _dep="$_b"; _dep_is_tgish=1; get_dep
532 recurse_deps get_dep "$_b"
533 recurse_deps_exclude="$recurse_deps_exclude $seen_deps"
534 done
537 get_deps()
539 get_deps_internal "$@" | LC_ALL=C sort -u
542 out_of_date=
543 if [ -n "$outofdateok" ]; then
544 if [ -n "$tgbranches" ]; then
545 while read -r dep && [ -n "$dep" ]; do
546 case " $allrefs " in *" $dep "*);;*)
547 ! ref_exists "$dep" ||
548 allrefs="${allrefs:+$allrefs }$dep"
549 esac
550 done <<-EOT
551 $(get_deps $tgbranches)
554 else
555 for b in $tgbranches; do
556 if ! needs_update "$b" >/dev/null; then
557 out_of_date=1
558 echo "branch not up-to-date: $b"
560 done
562 [ -z "$out_of_date" ] || die "all branches to be tagged must be up-to-date"
564 get_refs()
566 printf '%s\n' '-----BEGIN TOPGIT REFS-----'
568 printf '%s\n' $allrefs
569 [ -n "$outofdateok" ] || get_deps $tgbranches
570 } | LC_ALL=C sort -u | sed 's/^\(.*\)$/\1^0 \1/' |
571 git cat-file --batch-check='%(objectname) %(rest)' 2>/dev/null |
572 grep -v ' missing$' || :
573 printf '%s\n' '-----END TOPGIT REFS-----'
576 if [ -n "$refsonly" ]; then
577 get_refs
578 exit 0
581 stripcomments=
582 if [ -n "$msgfile" ]; then
583 if [ "$msgfile" = "-" ]; then
584 git stripspace >"$git_dir/TAG_EDITMSG"
585 else
586 git stripspace <"$msgfile" >"$git_dir/TAG_EDITMSG"
588 elif [ -n "$msg" ]; then
589 printf '%s\n' "$msg" | git stripspace >"$git_dir/TAG_EDITMSG"
590 else
591 case "$branches" in
592 *" "*)
593 if [ ${#branches} -le 60 ]; then
594 printf '%s\n' "tag $descpl $branches"
595 printf '%s\n' "$updmsg"
596 else
597 printf '%s\n' "tag $(( $(printf '%s' "$branches" | wc -w) )) $descpl" ""
598 for b in $branches; do
599 printf '%s\n' "$b"
600 done
604 printf '%s\n' "tag $desc $branches"
606 esac | git stripspace >"$git_dir/TAG_EDITMSG"
607 if [ -z "$noedit" ]; then
609 cat <<EOT
611 # Please enter a message for tg tag:
612 # $tagname
613 # Lines starting with '#' will be ignored.
615 # $descpl to be tagged:
618 for b in $branches; do
619 printf '%s\n' "# $b"
620 done
621 } >>"$git_dir/TAG_EDITMSG"
622 stripcomments=1
623 run_editor "$git_dir/TAG_EDITMSG" ||
624 die "there was a problem with the editor '$tg_editor'"
627 git stripspace ${stripcomments:+ --strip-comments} \
628 <"$git_dir/TAG_EDITMSG" >"$git_dir/TGTAG_FINALMSG"
629 [ -s "$git_dir/TGTAG_FINALMSG" ] || die "no tag message?"
630 echo "" >>"$git_dir/TGTAG_FINALMSG"
631 get_refs >>"$git_dir/TGTAG_FINALMSG"
633 tagtarget=
634 case "$allrefs${extrarefs:+ $extrarefs}" in
635 *" "*)
636 parents="$(git merge-base --independent \
637 $(printf '%s^0 ' $allrefs $extrarefs))" ||
638 die "failed: git merge-base --independent"
639 if [ $(printf '%s\n' "$parents" | wc -l) -eq 1 ]; then
640 tagtarget="$parents"
641 else
642 mttree="$(git hash-object -t tree -w --stdin </dev/null)"
643 tagtarget="$(printf '%s\n' "tg tag branch consolidation" "" $branches |
644 git commit-tree $mttree $(printf -- '-p %s ' $parents))"
648 tagtarget="$allrefs^0"
650 esac
652 init_reflog "$refname"
653 if [ "$reftype" = "tag" -a -n "$signed" ]; then
654 [ "$quiet" -eq 0 ] || exec >/dev/null
655 git tag -F "$git_dir/TGTAG_FINALMSG" ${signed:+-s} ${force:+-f} \
656 ${keyid:+-u} ${keyid} "$tagname" "$tagtarget"
657 else
658 obj="$(git rev-parse --verify --quiet "$tagtarget" --)" ||
659 die "invalid object name: $tagtarget"
660 typ="$(git cat-file -t "$tagtarget" 2>/dev/null)" ||
661 die "invalid object name: $tagtarget"
662 id="$(git var GIT_COMMITTER_IDENT 2>/dev/null)" ||
663 die "could not get GIT_COMMITTER_IDENT"
664 newtag="$({
665 printf '%s\n' "object $obj" "type $typ" "tag $tagname" \
666 "tagger $id" ""
667 cat "$git_dir/TGTAG_FINALMSG"
668 } | git mktag)" || die "git mktag failed"
669 old="$(git rev-parse --verify --short --quiet "$refname" --)" || :
670 updmsg=
671 case "$branches" in
672 *" "*)
673 if [ ${#branches} -le 100 ]; then
674 updmsg="$(printf '%s\n' "tgtag: $branches")"
675 else
676 updmsg="$(printf '%s\n' "tgtag: $(( $(printf '%s' "$branches" | wc -w) )) ${descpl#tg }")"
680 updmsg="$(printf '%s\n' "tgtag: $branches")"
682 esac
683 git update-ref -m "$updmsg" "$refname" "$newtag"
684 [ -z "$old" -o "$quiet" -gt 0 ] || printf "Updated $reftype '%s' (was %s)\n" "$tagname" "$old"
686 rm -f "$git_dir/TAG_EDITMSG" "$git_dir/TGTAG_FINALMSG"