tg-update.sh: allow multiple branch names
[topgit/pro.git] / tg-tag.sh
blob23ad85c398b5a87c0d90276cf7f6a055cc209e6c
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="Usage: ${tgname:-tg} [...] tag [-s | -u <key-id>] [-f] [-q] [--no-edit] [-m <msg> | -F <file>] (<tagname> | --refs) [<branch>...]"
8 USAGE="$USAGE$lf Or: ${tgname:-tg} [...] tag (-g | --reflog) [--reflog-message | --commit-message] [--no-type] [-n <number> | -number] [<tagname>]"
9 USAGE="$USAGE$lf Or: ${tgname:-tg} [...] tag (--clear | --delete) <tagname>"
10 USAGE="$USAGE$lf Or: ${tgname:-tg} [...] tag --drop <tagname>@{n}"
12 usage()
14 if [ "${1:-0}" != 0 ]; then
15 printf '%s\n' "$USAGE" >&2
16 else
17 printf '%s\n' "$USAGE"
19 exit ${1:-0}
22 ## Parse options
24 signed=
25 keyid=
26 force=
27 msg=
28 msgfile=
29 noedit=
30 defnoedit=
31 refsonly=
32 maxcount=
33 reflog=
34 outofdateok=
35 anyrefok=
36 defbranch=HEAD
37 stash=
38 reflogmsg=
39 notype=
40 setreflogmsg=
41 quiet=0
42 noneok=
43 clear=
44 delete=
45 drop=
47 is_numeric()
49 [ -n "$1" ] || return 1
50 while [ -n "$1" ]; do
51 case "$1" in
52 [0-9]*)
53 set -- "${1#?}";;
55 break;;
56 esac
57 done
58 [ -z "$1" ]
61 while [ $# -gt 0 ]; do case "$1" in
62 -h|--help)
63 usage
65 -q|--quiet)
66 quiet=$(( $quiet + 1 ))
68 --none-ok)
69 noneok=1
71 --clear)
72 clear=1
74 --delete)
75 delete=1
77 --drop)
78 drop=1
80 -g|--reflog|--walk-reflogs)
81 reflog=1
83 --reflog-message)
84 reflogmsg=1
85 setreflogmsg=1
87 --no-reflog-message|--commit-message)
88 reflogmsg=
89 setreflogmsg=1
91 --no-type)
92 notype=1
94 -s|--sign)
95 signed=1
97 -u|--local-user|--local-user=*)
98 case "$1" in --local-user=*)
99 x="$1"
100 shift
101 set -- --local-user "${x#--local-user=}" "$@"
102 esac
103 if [ $# -lt 2 ]; then
104 echo "The $1 option requires an argument" >&2
105 usage 1
107 shift
108 keyid="$1"
110 -f|--force)
111 force=1
113 --no-edit)
114 noedit=1
116 --edit)
117 noedit=0
119 --allow-outdated)
120 outofdateok=1
122 --allow-any)
123 anyrefok=1
125 --refs|--refs-only)
126 refsonly=1
128 -m|--message|--message=*)
129 case "$1" in --message=*)
130 x="$1"
131 shift
132 set -- --message "${x#--message=}" "$@"
133 esac
134 if [ $# -lt 2 ]; then
135 echo "The $1 option requires an argument" >&2
136 usage 1
138 shift
139 msg="$1"
141 -F|--file|--file=*)
142 case "$1" in --file=*)
143 x="$1"
144 shift
145 set -- --file "${x#--file=}" "$@"
146 esac
147 if [ $# -lt 2 ]; then
148 echo "The $1 option requires an argument" >&2
149 usage 1
151 shift
152 msgfile="$1"
154 -n|--max-count|--max-count=*|-[1-9]*)
155 case "$1" in --max-count=*)
156 x="$1"
157 shift
158 set -- --max-count "${x#--max-count=}" "$@"
159 esac
160 case "$1" in -[1-9]*)
161 x="${1#-}"
162 shift
163 set -- -n "$x" "$@"
164 esac
165 if [ $# -lt 2 ]; then
166 echo "The $1 option requires an argument" >&2
167 usage 1
169 shift
170 maxcount="$1"
173 shift
174 break
176 --all)
177 break
179 --stash|--stash"@{"*"}")
180 if [ -n "$reflog" ]; then
181 case "$2" in -[1-9]*)
182 x1="$1"
183 x2="$2"
184 shift
185 shift
186 set -- "$x2" "$x1" "$@"
187 continue
188 esac
190 stash=1
191 defbranch=--all
192 break
194 -?*)
195 echo "Unknown option: $1" >&2
196 usage 1
199 if [ -n "$reflog" ]; then
200 case "$2" in -[1-9]*)
201 x1="$1"
202 x2="$2"
203 shift
204 shift
205 set -- "$x2" "$x1" "$@"
206 continue
207 esac
209 break
211 esac; shift; done
213 [ -z "$stash" -o -n "$reflog$drop$clear$delete" ] || { outofdateok=1; force=1; defnoedit=1; }
214 [ -n "$noedit" ] || noedit="$defnoedit"
215 [ "$noedit" != "0" ] || noedit=
216 [ -z "$reflog" -o -z "$drop$clear$delete$signed$keyid$force$msg$msgfile$noedit$refsonly$outofdateok" ] || usage 1
217 [ -n "$reflog" -o -z "$setreflogmsg$notype$maxcount" ] || usage 1
218 [ -z "$drop$clear$delete" -o -z "$setreflogmsg$notype$maxcount$signed$keyid$force$msg$msgfile$noedit$refsonly$outofdateok" ] || usage 1
219 [ -z "$reflog$drop$clear$delete" -o "$reflog$drop$clear$delete" = "1" ] || usage 1
220 [ -z "$maxcount" ] || is_numeric "$maxcount" || die "invalid count: $maxcount"
221 [ -z "$maxcount" ] || [ $maxcount -gt 0 ] || die "invalid count: $maxcount"
222 [ -z "$msg" -o -z "$msgfile" ] || die "only one -F or -m option is allowed."
223 [ -z "$refsonly" ] || set -- refs..only "$@"
224 [ $# -gt 0 -o -z "$reflog" ] || set -- --stash
225 [ -n "$1" ] || { echo "Tag name required" >&2; usage 1; }
226 tagname="$1"
227 shift
228 [ "$tagname" != "--stash" ] || tagname=refs/tgstash
229 case "$tagname" in --stash"@{"*"}")
230 strip="${tagname#--stash??}"
231 strip="${strip%?}"
232 tagname="refs/tgstash@{$strip}"
233 esac
234 refname="$tagname"
235 sfx=
236 case "$refname" in [!@]*"@{"*"}")
237 sfx="$refname"
238 refname="${refname%@*}"
239 sfx="${sfx#$refname}"
240 esac
241 case "$refname" in HEAD|refs/*);;*)
242 if reftest="$(git rev-parse --revs-only --symbolic-full-name "$refname" -- 2>/dev/null)" &&
243 [ -n "$reftest" ]; then
244 if [ -n "$reflog$drop$clear$delete" ]; then
245 refname="$reftest"
246 else
247 case "$reftest" in
248 refs/tags/*|refs/tgstash)
249 refname="$reftest"
252 refname="refs/tags/$refname"
253 esac
255 else
256 refname="refs/tags/$refname"
258 esac
259 refname="$refname$sfx"
260 reftype=tag
261 case "$refname" in refs/tags/*) tagname="${refname#refs/tags/}";; *) reftype=ref; tagname="$refname"; esac
262 [ -z "$reflog$drop$clear$delete" -o $# -eq 0 ] || usage 1
263 if [ -n "$drop$clear$delete" ]; then
264 if [ -n "$sfx" ]; then
265 [ -z "$clear$delete" ] || die "invalid ref name ($sfx suffix not allowed): $refname"
266 else
267 [ -z "$drop" ] || die "invalid reflog name (@{n} suffix required): $refname"
269 old="$(git rev-parse --verify --quiet --short "${refname%$sfx}" --)" || die "no such ref: ${refname%$sfx}"
270 if [ -n "$delete" ]; then
271 git update-ref -d "$refname" || die "git update-ref -d failed"
272 printf "Deleted $reftype '%s' (was %s)\n" "$tagname" "$old"
273 exit 0
274 elif [ -n "$clear" ]; then
275 [ -f "$git_dir/logs/$refname" ] || die "no reflog found for: $refname"
276 [ -s "$git_dir/logs/$refname" ] || die "empty reflog found for: $refname"
277 cp -p "$git_dir/logs/$refname" "$git_dir/logs/$refname^-+" || die "cp failed"
278 sed -n '$p' <"$git_dir/logs/$refname^-+" >"$git_dir/logs/$refname" || die "reflog clear failed"
279 rm -f "$git_dir/logs/$refname^-+"
280 printf "Cleared $reftype '%s' reflog to single @{0} entry\n" "$tagname"
281 exit 0
282 else
283 old="$(git rev-parse --verify --short "$refname" --)" || exit 1
284 git reflog delete --rewrite --updateref "$refname" || die "reflog drop failed"
285 printf "Dropped $reftype '%s' reflog entry (was %s)\n" "$tagname" "$old"
286 exit 0
289 if [ -n "$reflog" ]; then
290 [ "$refname" = "refs/tgstash" -o -n "$setreflogmsg" ] || reflogmsg=1
291 git rev-parse --verify --quiet "$refname" -- >/dev/null ||
292 die "no such ref: $refname"
293 [ -s "$git_dir/logs/$refname" ] ||
294 die "no reflog present for $reftype: $tagname"
295 showref="$(git rev-parse --revs-only --abbrev-ref=strict "$refname" --)"
296 hashcolor=
297 resetcolor=
298 if git config --get-colorbool color.tgtag; then
299 metacolor="$(git config --get-color color.tgtag.meta)"
300 [ -n "$metacolor" ] || metacolor="$(git config --get-color color.diff.meta "bold")"
301 hashcolor="$(git config --get-color color.tgtag.commit)"
302 [ -n "$hashcolor" ] || hashcolor="$(git config --get-color color.diff.commit "yellow")"
303 datecolor="$(git config --get-color color.tgtag.date "bold blue")"
304 timecolor="$(git config --get-color color.tgtag.time "green")"
305 resetcolor="$(git config --get-color "" reset)"
307 setup_strftime
308 output()
310 sed 's/[^ ][^ ]* //' <"$git_dir/logs/$refname" |
311 awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--]}' |
312 git cat-file --batch-check='%(objectname) %(objecttype) %(rest)' |
314 stashnum=-1
315 lastdate=
316 while read -r newrev type rest; do
317 stashnum=$(( $stashnum + 1 ))
318 [ "$type" != "missing" ] || continue
319 IFS="$tab" read -r cmmttr msg <<-~EOT~
320 $rest
321 ~EOT~
322 ne="${cmmttr% *}"
323 ne="${ne% *}"
324 es="${cmmttr#$ne}"
325 es="${es% *}"
326 es="${es# }"
327 obj="$(git rev-parse --verify --quiet --short "$newrev" --)"
328 extra=
329 [ "$type" = "tag" -o -n "$notype" ] ||
330 extra="$hashcolor($metacolor$type$resetcolor$hashcolor)$resetcolor "
331 if [ -z "$reflogmsg" -o -z "$msg" ]; then
332 objmsg=
333 if [ "$type" = "tag" ]; then
334 objmsg="$(git cat-file tag "$obj" |
335 sed '1,/^$/d' | sed '/^$/,$d')"
336 elif [ "$type" = "commit" ]; then
337 objmsg="$(git log -n 1 --format='format:%s' "$obj" --)"
339 [ -z "$objmsg" ] || msg="$objmsg"
341 read newdate newtime <<-EOT
342 $(strftime "%Y-%m-%d %H:%M:%S" "$es")
344 if [ "$lastdate" != "$newdate" ]; then
345 printf '%s=== %s ===%s\n' "$datecolor" "$newdate" "$resetcolor"
346 lastdate="$newdate"
348 printf '%s %s %s%s@{%s}: %s\n' "$hashcolor$obj$reseutcolor" \
349 "$timecolor$newtime$resetcolor" \
350 "$extra" "$showref" "$stashnum" "$msg"
351 if [ -n "$maxcount" ]; then
352 maxcount=$(( $maxcount - 1 ))
353 [ $maxcount -gt 0 ] || break
355 done
358 page output
359 exit 0
361 [ -z "$signed" -o "$reftype" = "tag" ] || die "signed tags must be under refs/tags"
362 [ $# -gt 0 ] || set -- $defbranch
363 all=
364 if [ $# -eq 1 ] && [ "$1" = "--all" ]; then
365 eval set -- $(git for-each-ref --shell --format="%(refname)" "refs/$topbases")
366 outofdateok=1
367 all=1
368 if [ $# -eq 0 ]; then
369 if [ "$quiet" -gt 0 -a -n "$noneok" ]; then
370 exit 0
371 else
372 die "no TopGit branches found"
376 ensure_ident_available
377 branches=
378 allrefs=
379 extrarefs=
380 tgbranches=
381 tgcount=0
382 othercount=0
383 ignore=
384 newlist=
385 while read -r obj typ ref && [ -n "$obj" -a -n "$typ" ]; do
386 [ -n "$ref" -o "$typ" != "missing" ] || die "no such ref: ${obj%???}"
387 case " $ignore " in *" $ref "*) continue; esac
388 if [ "$typ" != "commit" -a "$typ" != "tag" ]; then
389 [ -n "$anyrefok" ] || die "not a committish (is a '$typ') ref: $ref"
390 [ "$quiet" -ge 2 ] || warn "ignoring non-committish (is a '$typ') ref: $ref"
391 ignore="${ignore:+$ignore }$ref"
392 continue
394 case " $newlist " in *" $ref "*);;*)
395 newlist="${newlist:+$newlist }$ref"
396 esac
397 if [ "$typ" = "tag" ]; then
398 [ "$quiet" -ge 2 ] || warn "storing as lightweight tag instead of 'tag' object: $ref"
399 ignore="${ignore:+$ignore }$ref"
401 done <<-EOT
403 printf '%s\n' "$@" | sed 's/^\(.*\)$/\1^{} \1/'
404 printf '%s\n' "$@" | sed 's/^\(.*\)$/\1 \1/'
406 git cat-file --batch-check='%(objectname) %(objecttype) %(rest)' 2>/dev/null ||
409 set -- $newlist
410 for b; do
411 sfn="$b"
412 [ -n "$all" ] ||
413 sfn="$(git rev-parse --revs-only --symbolic-full-name "$b" -- 2>/dev/null)" || :
414 [ -n "$sfn" ] || {
415 [ -n "$anyrefok" ] || die "no such symbolic ref name: $b"
416 fullhash="$(git rev-parse --verify --quiet "$b" --)" || die "no such ref: $b"
417 case " $extrarefs " in *" $b "*);;*)
418 [ "$quiet" -ge 2 ] || warn "including non-symbolic ref only in parents calculation: $b"
419 extrarefs="${extrarefs:+$extrarefs }$fullhash"
420 esac
421 continue
423 case "$sfn" in
424 refs/"$topbases"/*)
425 added=
426 tgish=1
427 ref_exists "refs/heads/${sfn#refs/$topbases/}" || tgish=
428 [ -n "$anyrefok" ] || [ -n "$tgish" ] || [ "$quiet" -ge 2 ] ||
429 warn "including TopGit base that's missing its head: $sfn"
430 case " $allrefs " in *" $sfn "*);;*)
431 allrefs="${allrefs:+$allrefs }$sfn"
432 esac
433 case " $branches " in *" ${sfn#refs/$topbases/} "*);;*)
434 branches="${branches:+$branches }${sfn#refs/$topbases/}"
435 added=1
436 esac
437 if [ -n "$tgish" ]; then
438 case " $allrefs " in *" refs/heads/${sfn#refs/$topbases/} "*);;*)
439 allrefs="${allrefs:+$allrefs }refs/heads/${sfn#refs/$topbases/}"
440 esac
441 case " $tgbranches " in *" ${sfn#refs/$topbases/} "*);;*)
442 tgbranches="${tgbranches:+$tgbranches }${sfn#refs/$topbases/}"
443 added=1
444 esac
445 [ -z "$added" ] || tgcount=$(( $tgcount + 1 ))
446 else
447 [ -z "$added" ] || othercount=$(( $othercount + 1 ))
450 refs/heads/*)
451 added=
452 tgish=1
453 ref_exists "refs/$topbases/${sfn#refs/heads/}" || tgish=
454 [ -n "$anyrefok" ] || [ -n "$tgish" ] ||
455 die "not a TopGit branch: ${sfn#refs/heads/} (use --allow-any option)"
456 case " $allrefs " in *" $b "*);;*)
457 allrefs="${allrefs:+$allrefs }$sfn"
458 esac
459 case " $branches " in *" ${sfn#refs/heads/} "*);;*)
460 branches="${branches:+$branches }${sfn#refs/heads/}"
461 added=1
462 esac
463 if [ -n "$tgish" ]; then
464 case " $allrefs " in *" refs/$topbases/${sfn#refs/heads/} "*);;*)
465 allrefs="${allrefs:+$allrefs }refs/$topbases/${sfn#refs/heads/}"
466 esac
467 case " $tgbranches " in *" ${sfn#refs/heads/} "*);;*)
468 tgbranches="${tgbranches:+$tgbranches }${sfn#refs/heads/}"
469 added=1
470 esac
471 [ -z "$added" ] || tgcount=$(( $tgcount + 1 ))
472 else
473 [ -z "$added" ] || othercount=$(( $othercount + 1 ))
477 [ -n "$anyrefok" ] || die "refusing to include without --allow-any: $sfn"
478 case " $allrefs " in *" $sfn "*);;*)
479 allrefs="${allrefs:+$allrefs }$sfn"
480 esac
481 case " $branches " in *" ${sfn#refs/} "*);;*)
482 branches="${branches:+$branches }${sfn#refs/}"
483 othercount=$(( $othercount + 1 ))
484 esac
486 esac
487 done
489 [ -n "$force" ] ||
490 ! git rev-parse --verify --quiet "$refname" -- >/dev/null ||
491 die "$reftype '$tagname' already exists"
493 desc="tg branch"
494 descpl="tg branches"
495 if [ $othercount -gt 0 ]; then
496 if [ $tgcount -eq 0 ]; then
497 desc="ref"
498 descpl="refs"
499 else
500 descpl="$descpl and refs"
504 get_dep() {
505 case " $seen_deps " in *" $_dep "*) return 0; esac
506 seen_deps="${seen_deps:+$seen_deps }$_dep"
507 printf 'refs/heads/%s\n' "$_dep"
508 [ -z "$_dep_is_tgish" ] || printf 'refs/%s/%s\n' "$topbases" "$_dep"
511 get_deps_internal()
513 no_remotes=1
514 recurse_deps_exclude=
515 for _b; do
516 case " $recurse_deps_exclude " in *" $_b "*) continue; esac
517 seen_deps=
518 _dep="$_b"; _dep_is_tgish=1; get_dep
519 recurse_deps get_dep "$_b"
520 recurse_deps_exclude="$recurse_deps_exclude $seen_deps"
521 done
524 get_deps()
526 get_deps_internal "$@" | LC_ALL=C sort -u
529 out_of_date=
530 if [ -n "$outofdateok" ]; then
531 if [ -n "$tgbranches" ]; then
532 while read -r dep && [ -n "$dep" ]; do
533 case " $allrefs " in *" $dep "*);;*)
534 ! ref_exists "$dep" ||
535 allrefs="${allrefs:+$allrefs }$dep"
536 esac
537 done <<-EOT
538 $(get_deps $tgbranches)
541 else
542 for b in $tgbranches; do
543 if ! needs_update "$b" >/dev/null; then
544 out_of_date=1
545 echo "branch not up-to-date: $b"
547 done
549 [ -z "$out_of_date" ] || die "all branches to be tagged must be up-to-date"
551 get_refs()
553 printf '%s\n' '-----BEGIN TOPGIT REFS-----'
555 printf '%s\n' $allrefs
556 [ -n "$outofdateok" ] || get_deps $tgbranches
557 } | LC_ALL=C sort -u | sed 's/^\(.*\)$/\1^0 \1/' |
558 git cat-file --batch-check='%(objectname) %(rest)' 2>/dev/null |
559 grep -v ' missing$' || :
560 printf '%s\n' '-----END TOPGIT REFS-----'
563 if [ -n "$refsonly" ]; then
564 get_refs
565 exit 0
568 stripcomments=
569 if [ -n "$msgfile" ]; then
570 if [ "$msgfile" = "-" ]; then
571 git stripspace >"$git_dir/TAG_EDITMSG"
572 else
573 git stripspace <"$msgfile" >"$git_dir/TAG_EDITMSG"
575 elif [ -n "$msg" ]; then
576 printf '%s\n' "$msg" | git stripspace >"$git_dir/TAG_EDITMSG"
577 else
578 case "$branches" in
579 *" "*)
580 if [ ${#branches} -le 60 ]; then
581 printf '%s\n' "tag $descpl $branches"
582 printf '%s\n' "$updmsg"
583 else
584 printf '%s\n' "tag $(( $(printf '%s' "$branches" | wc -w) )) $descpl" ""
585 for b in $branches; do
586 printf '%s\n' "$b"
587 done
591 printf '%s\n' "tag $desc $branches"
593 esac | git stripspace >"$git_dir/TAG_EDITMSG"
594 if [ -z "$noedit" ]; then
596 cat <<EOT
598 # Please enter a message for tg tag:
599 # $tagname
600 # Lines starting with '#' will be ignored.
602 # $descpl to be tagged:
605 for b in $branches; do
606 printf '%s\n' "# $b"
607 done
608 } >>"$git_dir/TAG_EDITMSG"
609 stripcomments=1
610 run_editor "$git_dir/TAG_EDITMSG" ||
611 die "there was a problem with the editor '$tg_editor'"
614 git stripspace ${stripcomments:+ --strip-comments} \
615 <"$git_dir/TAG_EDITMSG" >"$git_dir/TGTAG_FINALMSG"
616 [ -s "$git_dir/TGTAG_FINALMSG" ] || die "no tag message?"
617 echo "" >>"$git_dir/TGTAG_FINALMSG"
618 get_refs >>"$git_dir/TGTAG_FINALMSG"
620 tagtarget=
621 case "$allrefs${extrarefs:+ $extrarefs}" in
622 *" "*)
623 parents="$(git merge-base --independent \
624 $(printf '%s^0 ' $allrefs $extrarefs))" ||
625 die "failed: git merge-base --independent"
626 if [ $(printf '%s\n' "$parents" | wc -l) -eq 1 ]; then
627 tagtarget="$parents"
628 else
629 mttree="$(git hash-object -t tree -w --stdin </dev/null)"
630 tagtarget="$(printf '%s\n' "tg tag branch consolidation" "" $branches |
631 git commit-tree $mttree $(printf -- '-p %s ' $parents))"
635 tagtarget="$allrefs^0"
637 esac
639 init_reflog "$refname"
640 if [ "$reftype" = "tag" -a -n "$signed" ]; then
641 [ "$quiet" -eq 0 ] || exec >/dev/null
642 git tag -F "$git_dir/TGTAG_FINALMSG" ${signed:+-s} ${force:+-f} \
643 ${keyid:+-u} ${keyid} "$tagname" "$tagtarget"
644 else
645 obj="$(git rev-parse --verify --quiet "$tagtarget" --)" ||
646 die "invalid object name: $tagtarget"
647 typ="$(git cat-file -t "$tagtarget" 2>/dev/null)" ||
648 die "invalid object name: $tagtarget"
649 id="$(git var GIT_COMMITTER_IDENT 2>/dev/null)" ||
650 die "could not get GIT_COMMITTER_IDENT"
651 newtag="$({
652 printf '%s\n' "object $obj" "type $typ" "tag $tagname" \
653 "tagger $id" ""
654 cat "$git_dir/TGTAG_FINALMSG"
655 } | git mktag)" || die "git mktag failed"
656 old="$(git rev-parse --verify --short --quiet "$refname" --)" || :
657 updmsg=
658 case "$branches" in
659 *" "*)
660 if [ ${#branches} -le 100 ]; then
661 updmsg="$(printf '%s\n' "tgtag: $branches")"
662 else
663 updmsg="$(printf '%s\n' "tgtag: $(( $(printf '%s' "$branches" | wc -w) )) ${descpl#tg }")"
667 updmsg="$(printf '%s\n' "tgtag: $branches")"
669 esac
670 git update-ref -m "$updmsg" "$refname" "$newtag"
671 [ -z "$old" -o "$quiet" -gt 0 ] || printf "Updated $reftype '%s' (was %s)\n" "$tagname" "$old"
673 rm -f "$git_dir/TAG_EDITMSG" "$git_dir/TGTAG_FINALMSG"