3 # Copyright (C) 2015,2017,2018 Kyle J. McKay <mackyle@gmail.com>
8 Usage: ${tgname:-tg} [...] tag [-s | -u <key-id>] [-f] [-q] [--no-edit] [-m <msg> | -F <file>] [--tree <treeish>] (<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}"
15 if [ "${1:-0}" != 0 ]; then
16 printf '%s\n' "$USAGE" >&2
18 printf '%s\n' "$USAGE"
54 [ -n "$1" ] ||
return 1
66 while [ $# -gt 0 ]; do case "$1" in
71 quiet
=$
(( $quiet + 1 ))
85 -g|
--reflog|
--walk-reflogs)
92 --no-reflog-message|
--commit-message)
102 -u|
--local-user|
--local-user=*)
103 case "$1" in --local-user=*)
106 set -- --local-user "${x#--local-user=}" "$@"
108 if [ $# -lt 2 ]; then
109 echo "The $1 option requires an argument" >&2
131 case "$1" in --tree=*)
134 set -- --tree "${x#--tree=}" "$@"
136 if [ $# -lt 2 ]; then
137 echo "The $1 option requires an argument" >&2
141 treeish
="$(git rev-parse --quiet --verify "$1^
{tree
}" --)" ||
{
142 echo "Not a valid treeish: $1" >&2
149 -m|
--message|
--message=*)
150 case "$1" in --message=*)
153 set -- --message "${x#--message=}" "$@"
155 if [ $# -lt 2 ]; then
156 echo "The $1 option requires an argument" >&2
163 case "$1" in --file=*)
166 set -- --file "${x#--file=}" "$@"
168 if [ $# -lt 2 ]; then
169 echo "The $1 option requires an argument" >&2
175 -n|
--max-count|
--max-count=*|
-[1-9]*)
176 case "$1" in --max-count=*)
179 set -- --max-count "${x#--max-count=}" "$@"
181 case "$1" in -[1-9]*)
186 if [ $# -lt 2 ]; then
187 echo "The $1 option requires an argument" >&2
201 --stash|
--stash"@{"*"}")
202 if [ -n "$reflog" ]; then
203 case "$2" in -[1-9]*)
208 set -- "$x2" "$x1" "$@"
212 if [ "$2" = "--all" ]; then
218 elif [ "$2" = "--" ]; then
236 echo "Unknown option: $1" >&2
240 if [ -n "$reflog" ]; then
241 case "$2" in -[1-9]*)
246 set -- "$x2" "$x1" "$@"
254 [ "$stash$anonymous" != "11" ] || usage
1
255 [ -z "$stash$anonymous" ] ||
[ -n "$reflog$drop$clear$delete" ] ||
{ outofdateok
=1; force
=1; defnoedit
=1; }
256 [ -z "$sawall" ] ||
[ $# -gt 0 ] ||
{ outofdateok
=1; force
=1; defnoedit
=1; }
257 [ -n "$noedit" ] || noedit
="$defnoedit"
258 [ "$noedit" != "0" ] || noedit
=
259 [ -z "$reflog" ] ||
[ -z "$drop$clear$delete$signed$keyid$force$msg$msgfile$noedit$treeish$refsonly$outofdateok$sawall" ] || usage
1
260 [ -n "$reflog" ] ||
[ -z "$setreflogmsg$notype$maxcount" ] || usage
1
261 [ -z "$drop$clear$delete" ] ||
[ -z "$setreflogmsg$notype$maxcount$signed$keyid$force$msg$msgfile$noedit$treeish$refsonly$outofdateok$sawall" ] || usage
1
262 [ -z "$reflog$drop$clear$delete" ] ||
[ "$reflog$drop$clear$delete" = "1" ] || usage
1
263 [ -z "$maxcount" ] || is_numeric
"$maxcount" || die
"invalid count: $maxcount"
264 [ -z "$maxcount" ] ||
[ $maxcount -gt 0 ] || die
"invalid count: $maxcount"
265 [ -z "$msg" ] ||
[ -z "$msgfile" ] || die
"only one -F or -m option is allowed."
266 [ -z "$refsonly" ] ||
set -- refs..only
"$@"
267 [ $# -gt 0 ] ||
[ -z "$reflog$sawall" ] ||
set -- --stash
268 [ -n "$1" ] ||
{ echo "Tag name required" >&2; usage
1; }
271 [ -z "$sawall" ] ||
[ $# -eq 0 ] || die
"branch names not allowed with --all"
272 [ "$tagname" != "--stash" ] || tagname
=refs
/tgstash
273 [ "$tagname" != "--anonymous" ] || tagname
=TG_STASH
274 case "$tagname" in --stash"@{"*"}")
275 strip
="${tagname#--stash??}"
277 tagname
="refs/tgstash@{$strip}"
282 case "$refname" in [!@
]*"@{"*"}")
284 _refonly
="${refname%%"$_pfx"*}"
285 sfx
="${refname#"$_refonly"}"
288 _numonly
="${_numonly%?}"
289 [ "${_numonly#[0-9]}" != "$_numonly" ] && [ "${_numonly#*[!0-9]}" = "$_numonly" ] || die
"invalid suffix: \"$sfx\""
290 if [ "${_numonly#*[!0]}" = "$_numonly" ]; then
291 # transform @{0000000} etc. into @{0}
295 # remove any leading zeros
296 _ld0
="${_numonly%%[!0]*}"
297 [ -z "$_ld0" ] || _numonly
="${_numonly#$_ld0}"
302 v_resolve_full_name
() {
303 # `git rev-parse --revs-only --symbolic-full-name` should do this
304 # but its behavior is inadequate in that it will not produce a result
305 # if the input is ambiguous even though `git rev-parse --verify` still
306 # will in that case. Nasty.
307 # Besides, we really don't want to follow symbolic refs anyway and
308 # we would need to do that ourselves because `git rev-parse` does not
309 # have a `--no-deref` option like `git update-ref` does. Ugly.
312 _normref
="$(git check-ref-format --normalize --allow-onelevel "$2" 2>/dev/null)" && [ -n "$_normref" ] ||
return 1
313 eval "$1="'"$_normref"'
314 git rev-parse
--verify --quiet "$_normref" -- >/dev
/null
2>&1 ||
return 0
315 case "$_normref" in refs
/?
*) return 0; esac
318 # see `git help revisions` for this DWIM list of interpretations
319 for _rprefix
in "refs" "refs/tags" "refs/heads" "refs/remotes"; do
320 ! git rev-parse
--verify --quiet "$_rprefix/$_normref" -- >/dev
/null
2>&1 ||
{ _found
=1; break; }
322 if [ -z "$_found" ]; then
324 ! git rev-parse
--verify --quiet "$_rprefix/$_normref$_rsuffix" -- >/dev
/null
2>&1 || _found
=1
326 [ -z "$_found" ] ||
eval "$1="'"$_rprefix/$_normref$_rsuffix"'
330 case "$refname" in [Hh
][Ee
][Aa
][Dd
]|
"@") refname
="HEAD"; esac
331 case "$refname" in [Tt
][Gg
]_
[Ss
][Tt
][Aa
][Ss
][Hh
]) refname
="TG_STASH"; esac
332 case "$refname" in HEAD|TG_STASH|refs
/*);;*)
333 if v_resolve_full_name reftest
"$refname" && [ -n "$reftest" ]; then
334 if [ -n "$reflog$drop$clear$delete" ]; then
338 refs
/tags
/*|refs
/tgstash
)
342 refname
="refs/tags/$refname"
346 refname
="refs/tags/$refname"
349 refname
="$refname$sfx"
351 case "$refname" in refs
/tags
/*) tagname
="${refname#refs/tags/}";; *) reftype
=ref
; tagname
="$refname"; esac
352 logbase
="$git_common_dir"
353 [ "${refname%$sfx}" != "HEAD" ] || logbase
="$git_dir"
354 [ -z "$reflog$drop$clear$delete" ] ||
[ $# -eq 0 ] || usage
1
355 if [ -n "$drop$clear$delete" ]; then
356 if [ -n "$sfx" ]; then
357 [ -z "$clear$delete" ] || die
"invalid ref name ($sfx suffix not allowed, try --drop): $refname"
359 [ -z "$drop" ] || die
"invalid reflog entry name (@{n} suffix required): $refname"
361 old
="$(git rev-parse --verify --quiet --short "${refname%$sfx}" --)" || die
"no such ref: ${refname%$sfx}"
362 if [ -n "$delete" ]; then
363 case "$refname" in [Hh
][Ee
][Aa
][Dd
])
365 ! symref
="$(git symbolic-ref -q --short HEAD 2>/dev/null)" ||
366 extra
=" (did you mean to delete \"$symref\"?)"
367 die
"HEAD may not be deleted$extra"
370 srh
="$(git symbolic-ref --quiet HEAD)" &&
371 [ -n "$srh" ] && [ "$srh" = "$refname" ]
373 orig
="$(git rev-parse --verify --quiet "$refname^
0" --)" || die
"no such committish ref: $refname"
374 [ "$quiet" -gt 0 ] || warn
"detaching HEAD to delete $refname"
375 git update-ref
-m "tgtag: detach HEAD to delete $refname" --no-deref HEAD
"$orig" || die
"detach failed"
376 [ "$quiet" -gt 0 ] || warn
"$(git --no-pager log -n 1 --format=format:'HEAD is now at %h... %s' HEAD)"
378 git update-ref
--no-deref -d "$refname" || die
"git update-ref --no-deref -d failed"
379 printf "Deleted $reftype '%s' (was %s)\n" "$tagname" "$old"
381 elif [ -n "$clear" ]; then
382 [ -f "$logbase/logs/$refname" ] || die
"no reflog found for: $refname"
383 [ -s "$logbase/logs/$refname" ] || die
"empty reflog found for: $refname"
384 cp -p "$logbase/logs/$refname" "$logbase/logs/$refname^-+" || die
"cp failed"
386 NF >= 5 { line[1] = $0 }
389 if (match(line[1], /^[0-9a-fA-F]+ /)) {
390 old = substr(line[1], 1, RLENGTH - 1)
391 rest = substr(line[1], RLENGTH)
392 gsub(/[0-9a-fA-F]/, "0", old)
398 ' <"$logbase/logs/$refname^-+" >"$logbase/logs/$refname" || die
"reflog clear failed"
399 rm -f "$logbase/logs/$refname^-+"
400 printf "Cleared $reftype '%s' reflog to single @{0} entry\n" "$tagname"
404 old
="$(git rev-parse --verify --short "$refname" -- 2>/dev/null)" ||
{
405 # if it failed, redo showing STDERR, otherwise suppress STDERR
406 git rev-parse
--verify --short "$refname" -- >/dev
/null ||
:
409 [ -z "$sfxis0" ] ||
[ "$ref" = "$old" ] || sfxis0
=
410 [ -z "$sfxis0" ] ||
! git symbolic-ref
-q "${refname%$sfx}" -- >/dev
/null
2>&1 || sfxis0
=
411 if [ -n "$sfxis0" ]; then
412 [ -f "$logbase/logs/${refname%$sfx}" ] || die
"no reflog found for: ${refname%$sfx}"
413 [ -s "$logbase/logs/${refname%$sfx}" ] || die
"empty reflog found for: ${refname%$sfx}"
414 # make sure @{1} is valid via pseudo stale-fix before using --updateref
415 cnt
="$(( $(wc -l <"$logbase/logs
/${refname%$sfx}") ))"
419 # avoid using --updateref if @{0} is the only entry (i.e. less than 2 lines in log)
420 [ $cnt -ge 2 ] && [ "$cnt" != "$lastcnt" ] &&
421 at1
="$(git rev-parse --verify --quiet "${refname%$sfx}@
{1}" -- 2>/dev/null)" &&
423 ! git rev-list
--no-walk --objects "$at1" -- >/dev
/null
2>&1
425 # poor man's --stale-fix that's faster and actually works reliably
426 git reflog delete
--rewrite "${refname%$sfx}@{1}" >/dev
/null
2>&1 ||
427 die
"pseudo stale-fix failed for broken ${refname%$sfx}@{1}"
430 cnt
="$(( $(wc -l <"$logbase/logs
/${refname%$sfx}") ))"
432 # avoid using --updateref if @{0} is the only entry (i.e. less than 2 lines in log)
433 [ -n "$at1" ] && [ $cnt -ge 2 ] || sfxis0
=
435 git reflog delete
--rewrite ${sfxis0:+--updateref} "$refname" || die
"reflog drop failed"
436 if [ -n "$sfxis0" ]; then
437 # check if we need to clean up
438 check
="$(git rev-parse --verify --quiet "${refname%$sfx}" --)" ||
:
439 [ "${check#*[!0]}" != "$check" ] || check
= # all 0's or empty is bad
440 # Git versions prior to 2.4.0 might need some clean up
441 [ -n "$check" ] || git update-ref
-d "${refname%$sfx}" >/dev
/null
2>&1 ||
:
443 printf "Dropped $reftype '%s' reflog entry (was %s)\n" "$tagname" "$old"
447 if [ -n "$reflog" ]; then
448 [ "$refname" = "refs/tgstash" ] ||
[ -n "$setreflogmsg" ] || reflogmsg
=1
449 git rev-parse
--verify --quiet "$refname" -- >/dev
/null ||
450 die
"no such ref: $refname"
451 [ -s "$logbase/logs/$refname" ] ||
452 die
"no reflog present for $reftype: $tagname"
454 [ "$refname" = "HEAD" ] || showref
="$(git rev-parse --revs-only --abbrev-ref=strict "$refname" --)"
457 if git config
--get-colorbool color.tgtag
; then
458 metacolor
="$(git config --get-color color.tgtag.meta)"
459 [ -n "$metacolor" ] || metacolor
="$(git config --get-color color.diff.meta "bold
")"
460 hashcolor
="$(git config --get-color color.tgtag.commit)"
461 [ -n "$hashcolor" ] || hashcolor
="$(git config --get-color color.diff.commit "yellow
")"
462 datecolor
="$(git config --get-color color.tgtag.date "bold blue
")"
463 timecolor
="$(git config --get-color color.tgtag.time "green
")"
464 resetcolor
="$(git config --get-color "" reset)"
469 sed 's/[^ ][^ ]* //' <"$logbase/logs/$refname" |
470 awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--]}' |
471 git cat-file
--batch-check='%(objectname) %(objecttype) %(rest)' |
475 while read -r newrev
type rest
; do
476 stashnum
=$
(( $stashnum + 1 ))
477 [ "$type" != "missing" ] ||
continue
478 IFS
="$tab" read -r cmmttr msg
<<-~EOT~
486 obj="$(git rev-parse --verify --quiet --short "$newrev" --)"
488 [ "$type" = "tag" ] || [ -n "$notype" ] ||
489 extra="$hashcolor($metacolor$type$resetcolor$hashcolor)$resetcolor "
490 if [ -z "$reflogmsg" ] || [ -z "$msg" ]; then
492 if [ "$type" = "tag" ]; then
493 objmsg="$(git cat-file tag "$obj" |
494 sed '1,/^$/d' | sed '/^$/,$d')"
495 elif [ "$type" = "commit" ]; then
496 objmsg="$(git --no-pager log -n 1 --format='format:%s' "$obj" --)"
498 [ -z "$objmsg" ] || msg="$objmsg"
500 read newdate newtime <<-EOT
501 $(strftime "%Y-%m-%d %H:%M:%S" "$es" 2>/dev/null)
503 if [ "$lastdate" != "$newdate" ]; then
504 printf '%s=== %s ===%s\n' "$datecolor" "$newdate" "$resetcolor"
507 printf '%s %s %s%s@{%s}: %s\n' "$hashcolor$obj$reseutcolor" \
508 "$timecolor$newtime$resetcolor" \
509 "$extra" "$showref" "$stashnum" "$msg"
510 if [ -n "$maxcount" ]; then
511 maxcount=$(( $maxcount - 1 ))
512 [ $maxcount -gt 0 ] ||
break
520 [ -z "$signed" ] ||
[ "$reftype" = "tag" ] || die
"signed tags must be under refs/tags"
521 [ $# -gt 0 ] ||
set -- $defbranch
523 if [ $# -eq 1 ] && [ "$1" = "--all" ]; then
524 eval set -- $
(git for-each-ref
--shell --format="%(refname)" ${anyrefok:+"refs/heads"} "refs/$topbases")
527 if [ $# -eq 0 ]; then
528 if [ "$quiet" -gt 0 ] && [ -n "$noneok" ]; then
532 [ -n "$anyrefok" ] || onlytg
=" TopGit"
533 die
"no$onlytg branches found"
537 [ -n "$refsonly" ] || ensure_ident_available
548 case "$arg" in "~"?
*)
549 [ -z "$firstprnt" ] || die
"only one first parent may be specified with ~"
550 firstprnt
="$(git rev-parse --verify --quiet "${arg#?}^
0" -- 2>/dev/null)" && [ -n "$firstprnt" ] ||
551 die
"not a commit-ish: ${arg#?}"
554 while read -r obj typ ref
&& [ -n "$obj" ] && [ -n "$typ" ]; do
555 [ -n "$ref" ] ||
[ "$typ" != "missing" ] || die
"no such ref: ${obj%???}"
556 case " $ignore " in *" $ref "*) continue; esac
557 if [ "$typ" != "commit" ] && [ "$typ" != "tag" ]; then
558 [ -n "$anyrefok" ] || die
"not a committish (is a '$typ') ref: $ref"
559 [ "$quiet" -ge 2 ] || warn
"ignoring non-committish (is a '$typ') ref: $ref"
560 ignore
="${ignore:+$ignore }$ref"
563 case " $newlist " in *" $ref "*);;*)
564 newlist
="${newlist:+$newlist }$ref"
566 if [ "$typ" = "tag" ]; then
567 [ "$quiet" -ge 2 ] || warn
"storing as lightweight tag instead of 'tag' object: $ref"
568 ignore
="${ignore:+$ignore }$ref"
572 printf '%s\n' "$@" | sed 's/^~//; s/^\(.*\)$/\1^{} \1/'
573 printf '%s\n' "$@" | sed 's/^~//; s/^\(.*\)$/\1 \1/'
575 git cat-file --batch-check='%(objectname) %(objecttype) %(rest)' 2>/dev/null ||
579 errtemp
="$(get_temp errs)"
582 if [ -z "$all" ]; then
583 [ "${b#-}" = "$b" ] || die
"branch names starting with '-' must be fully qualified: $b"
584 sfn
="$(git rev-parse --revs-only --symbolic-full-name "$b" -- 2>"$errtemp")" ||
:
585 [ -n "$sfn" ] ||
! [ -s "$errtemp" ] || sfn
="$(git rev-parse --revs-only --symbolic-full-name "refs
/heads
/$b" -- 2>/dev/null)" ||
:
588 if [ -z "$anyrefok" ] ||
[ -s "$errtemp" ]; then
589 ! [ -s "$errtemp" ] ||
tail -n 1 <"$errtemp" >&2
590 die
"invalid symbolic ref name: $b"
592 fullhash
="$(git rev-parse --verify --quiet "$b" --)" || die
"no such ref: $b"
593 case " $extrarefs " in *" $b "*);;*)
594 [ "$quiet" -ge 2 ] || warn
"including non-symbolic ref only in parents calculation: $b"
595 extrarefs
="${extrarefs:+$extrarefs }$fullhash"
603 ref_exists
"refs/heads/${sfn#refs/$topbases/}" || tgish
=
604 [ -n "$anyrefok" ] ||
[ -n "$tgish" ] ||
[ "$quiet" -ge 2 ] ||
605 warn
"including TopGit base that's missing its head: $sfn"
606 case " $allrefs " in *" $sfn "*);;*)
607 allrefs
="${allrefs:+$allrefs }$sfn"
609 case " $branches " in *" ${sfn#refs/$topbases/} "*);;*)
610 branches
="${branches:+$branches }${sfn#refs/$topbases/}"
613 if [ -n "$tgish" ]; then
614 case " $allrefs " in *" refs/heads/${sfn#refs/$topbases/} "*);;*)
615 allrefs
="${allrefs:+$allrefs }refs/heads/${sfn#refs/$topbases/}"
617 case " $tgbranches " in *" ${sfn#refs/$topbases/} "*);;*)
618 tgbranches
="${tgbranches:+$tgbranches }${sfn#refs/$topbases/}"
621 [ -z "$added" ] || tgcount
=$
(( $tgcount + 1 ))
623 [ -z "$added" ] || othercount
=$
(( $othercount + 1 ))
629 ref_exists
"refs/$topbases/${sfn#refs/heads/}" || tgish
=
630 [ -n "$anyrefok" ] ||
[ -n "$tgish" ] ||
631 die
"not a TopGit branch: ${sfn#refs/heads/} (use --allow-any option)"
632 case " $allrefs " in *" $b "*);;*)
633 allrefs
="${allrefs:+$allrefs }$sfn"
635 case " $branches " in *" ${sfn#refs/heads/} "*);;*)
636 branches
="${branches:+$branches }${sfn#refs/heads/}"
639 if [ -n "$tgish" ]; then
640 case " $allrefs " in *" refs/$topbases/${sfn#refs/heads/} "*);;*)
641 allrefs
="${allrefs:+$allrefs }refs/$topbases/${sfn#refs/heads/}"
643 case " $tgbranches " in *" ${sfn#refs/heads/} "*);;*)
644 tgbranches
="${tgbranches:+$tgbranches }${sfn#refs/heads/}"
647 [ -z "$added" ] || tgcount
=$
(( $tgcount + 1 ))
649 [ -z "$added" ] || othercount
=$
(( $othercount + 1 ))
653 [ -n "$anyrefok" ] || die
"refusing to include without --allow-any: $sfn"
654 case " $allrefs " in *" $sfn "*);;*)
655 allrefs
="${allrefs:+$allrefs }$sfn"
657 case " $branches " in *" ${sfn#refs/} "*);;*)
658 branches
="${branches:+$branches }${sfn#refs/}"
659 othercount
=$
(( $othercount + 1 ))
666 ! git rev-parse
--verify --quiet "$refname" -- >/dev
/null ||
667 die
"$reftype '$tagname' already exists"
671 if [ $othercount -gt 0 ]; then
672 if [ $tgcount -eq 0 ]; then
676 descpl
="$descpl and refs"
681 case " $seen_deps " in *" $_dep "*) return 0; esac
682 seen_deps
="${seen_deps:+$seen_deps }$_dep"
683 printf 'refs/heads/%s\n' "$_dep"
684 [ -z "$_dep_is_tgish" ] ||
printf 'refs/%s/%s\n' "$topbases" "$_dep"
690 recurse_deps_exclude
=
692 case " $recurse_deps_exclude " in *" $_b "*) continue; esac
694 _dep
="$_b"; _dep_is_tgish
=1; get_dep
695 recurse_deps get_dep
"$_b"
696 recurse_deps_exclude
="$recurse_deps_exclude $seen_deps"
702 get_deps_internal
"$@" |
sort -u
706 if [ -n "$outofdateok" ]; then
707 if [ -n "$tgbranches" ]; then
708 while read -r dep
&& [ -n "$dep" ]; do
709 case " $allrefs " in *" $dep "*);;*)
710 ! ref_exists
"$dep" ||
711 allrefs
="${allrefs:+$allrefs }$dep"
714 $(get_deps $tgbranches)
718 for b
in $tgbranches; do
719 if ! needs_update
"$b" >/dev
/null
; then
721 echo "branch not up-to-date: $b" >&2
725 [ -z "$out_of_date" ] || die
"all branches to be tagged must be up-to-date"
729 printf '%s\n' '-----BEGIN TOPGIT REFS-----'
731 printf '%s\n' $allrefs
732 [ -n "$outofdateok" ] || get_deps
$tgbranches
733 } |
sort -u |
sed 's/^\(.*\)$/\1^0 \1/' |
734 git cat-file
--batch-check='%(objectname) %(rest)' 2>/dev
/null |
735 grep -v ' missing$' ||
:
736 printf '%s\n' '-----END TOPGIT REFS-----'
739 if [ -n "$refsonly" ]; then
745 if [ -n "$msgfile" ]; then
746 if [ "$msgfile" = "-" ]; then
747 git stripspace
>"$git_dir/TAG_EDITMSG"
749 git stripspace
<"$msgfile" >"$git_dir/TAG_EDITMSG"
751 elif [ -n "$msg" ]; then
752 printf '%s\n' "$msg" | git stripspace
>"$git_dir/TAG_EDITMSG"
756 if [ ${#branches} -le 60 ]; then
757 printf '%s\n' "tag $descpl $branches"
758 printf '%s\n' "$updmsg"
760 printf '%s\n' "tag $(( $(printf '%s' "$branches" | wc -w) )) $descpl" ""
761 for b
in $branches; do
767 printf '%s\n' "tag $desc $branches"
769 esac | git stripspace
>"$git_dir/TAG_EDITMSG"
770 if [ -z "$noedit" ]; then
774 # Please enter a message for tg tag:
776 # Lines starting with '#' will be ignored.
778 # $descpl to be tagged:
781 for b
in $branches; do
784 } >>"$git_dir/TAG_EDITMSG"
786 run_editor
"$git_dir/TAG_EDITMSG" ||
787 die
"there was a problem with the editor '$tg_editor'"
790 git
-c core.commentchar
='#' stripspace
${stripcomments:+--strip-comments} \
791 <"$git_dir/TAG_EDITMSG" >"$git_dir/TGTAG_FINALMSG"
792 [ -s "$git_dir/TGTAG_FINALMSG" ] || die
"no tag message?"
793 echo "" >>"$git_dir/TGTAG_FINALMSG"
794 get_refs
>>"$git_dir/TGTAG_FINALMSG"
796 v_count_args
() { eval "$1="'$(( $# - 1 ))'; }
799 case "$allrefs${extrarefs:+ $extrarefs}" in
801 parents
="$(git merge-base --independent \
802 $(printf '%s^0 ' $allrefs $extrarefs))" ||
803 die
"failed: git merge-base --independent"
806 if [ -n "$firstprnt" ]; then
807 parents
="$(git rev-parse --quiet --verify "$allrefs^
0" --)" ||
808 die
"failed: git rev-parse $allrefs^0"
814 if [ -n "$firstprnt" ]; then
815 oldparents
="$parents"
817 for acmt
in $oldparents; do
818 [ "$acmt" = "$firstprnt" ] || parents
="$parents $acmt"
822 v_count_args pcnt
$parents
823 if [ $pcnt -eq 1 ]; then
826 [ "$(git rev-parse --quiet --verify "$tagtarget^
{tree
}" --)" = "$treeish" ] ||
829 if [ -z "$tagtarget" ]; then
830 tagtree
="${treeish:-$firstprnt}"
831 [ -n "$tagtree" ] || tagtree
="$(git mktree </dev/null)"
832 tagtarget
="$(printf '%s\n' "tg tag branch consolidation
" "" $branches |
833 git commit-tree $tagtree^{tree} $(printf -- '-p %s ' $parents))"
836 init_reflog
"$refname"
837 if [ "$reftype" = "tag" ] && [ -n "$signed" ]; then
838 [ "$quiet" -eq 0 ] ||
exec >/dev
/null
839 git tag
-F "$git_dir/TGTAG_FINALMSG" ${signed:+-s} ${force:+-f} \
840 ${keyid:+-u} ${keyid} "$tagname" "$tagtarget"
842 obj
="$(git rev-parse --verify --quiet "$tagtarget" --)" ||
843 die
"invalid object name: $tagtarget"
844 typ
="$(git cat-file -t "$tagtarget" 2>/dev/null)" ||
845 die
"invalid object name: $tagtarget"
846 id
="$(git var GIT_COMMITTER_IDENT 2>/dev/null)" ||
847 die
"could not get GIT_COMMITTER_IDENT"
849 printf '%s\n' "object
$obj" "type $typ" "tag
$tagname" \
851 cat "$git_dir/TGTAG_FINALMSG
"
852 } | git mktag)" || die
"git mktag failed"
853 old
="$(git rev-parse --verify --short --quiet "$refname" --)" ||
:
857 if [ ${#branches} -le 100 ]; then
858 updmsg
="$(printf '%s\n' "tgtag
: $branches")"
860 updmsg
="$(printf '%s\n' "tgtag
: $
(( $
(printf '%s' "$branches" |
wc -w) )) ${descpl#tg }")"
864 updmsg
="$(printf '%s\n' "tgtag
: $branches")"
867 [ "$refname" != "TG_STASH" ] ||
! [ -s "$git_dir/TG_STASH" ] ||
mv -f "$git_dir/TG_STASH" "$git_dir/ORIG_TG_STASH" >/dev
/null
2>&1 ||
:
868 git update-ref
-m "$updmsg" "$refname" "$newtag"
869 [ -z "$old" ] ||
[ "$quiet" -gt 0 ] ||
printf "Updated $reftype '%s' (was %s)\n" "$tagname" "$old"
871 rm -f "$git_dir/TAG_EDITMSG" "$git_dir/TGTAG_FINALMSG"