3 # Copyright (C) 2015 Kyle J. McKay <mackyle@gmail.com>
7 lf
="$(printf '\n.')" && lf
="${lf%?}"
8 tab
="$(printf '\t.')" && tab
="${tab%?}"
9 USAGE
="Usage: ${tgname:-tg} [...] tag [-s | -u <key-id>] [-f] [-q] [--no-edit] [-m <msg> | -F <file>] (<tagname> | --refs) [<branch>...]"
10 USAGE
="$USAGE$lf Or: ${tgname:-tg} [...] tag (-g | --reflog) [--reflog-message] [--no-type] [-n <number> | -number] [<tagname>]"
14 if [ "${1:-0}" != 0 ]; then
15 printf '%s\n' "$USAGE" >&2
17 printf '%s\n' "$USAGE"
44 [ -n "$1" ] ||
return 1
56 while [ $# -gt 0 ]; do case "$1" in
80 -u|
--local-user|
--local-user=*)
81 case "$1" in --local-user=*)
84 set -- --local-user "${x#--local-user=}" "$@"
87 echo "The $1 option requires an argument" >&2
108 -m|
--message|
--message=*)
109 case "$1" in --message=*)
112 set -- --message "${x#--message=}" "$@"
114 if [ $# -lt 2 ]; then
115 echo "The $1 option requires an argument" >&2
122 case "$1" in --file=*)
125 set -- --file "${x#--file=}" "$@"
127 if [ $# -lt 2 ]; then
128 echo "The $1 option requires an argument" >&2
134 -n|
--max-count|
--max-count=*|
-[1-9]*)
135 case "$1" in --max-count=*)
138 set -- --max-count "${x#--max-count=}" "$@"
140 case "$1" in -[1-9]*)
145 if [ $# -lt 2 ]; then
146 echo "The $1 option requires an argument" >&2
160 if [ -n "$reflog" ]; then
161 case "$2" in -[1-9]*)
166 set -- "$x2" "$x1" "$@"
175 echo "Unknown option: $1" >&2
179 if [ -n "$reflog" ]; then
180 case "$2" in -[1-9]*)
185 set -- "$x2" "$x1" "$@"
193 [ -z "$stash" -o -n "$reflog" ] ||
{ outofdateok
=1; force
=1; defnoedit
=1; }
194 [ -n "$noedit" ] || noedit
="$defnoedit"
195 [ "$noedit" != "0" ] || noedit
=
196 [ -z "$reflog" -o -z "$signed$keyid$force$msg$msgfile$noedit$refsonly$outofdateok" ] || usage
1
197 [ -n "$reflog" -o -z "$setreflogmsg$notype$maxcount" ] || usage
1
198 [ -z "$maxcount" ] || is_numeric
"$maxcount" || die
"invalid count: $maxcount"
199 [ -z "$maxcount" ] ||
[ $maxcount -gt 0 ] || die
"invalid count: $maxcount"
200 [ -z "$msg" -o -z "$msgfile" ] || die
"only one -F or -m option is allowed."
201 [ -z "$refsonly" ] ||
set -- refs..only
"$@"
202 [ $# -gt 0 -o -z "$reflog" ] ||
set -- --stash
203 [ -n "$1" ] ||
{ echo "Tag name required" >&2; usage
1; }
206 [ "$tagname" != "--stash" ] || tagname
=refs
/tgstash
208 case "$refname" in HEAD|refs
/*) :;; *)
209 if reftest
="$(git rev-parse --revs-only --symbolic-full-name "$refname" -- 2>/dev/null)" && \
210 [ -n "$reftest" ]; then
211 if [ -n "$reflog" ]; then
215 refs
/tags
/*|refs
/tgstash
)
219 refname
="refs/tags/$refname"
223 refname
="refs/tags/$refname"
227 case "$refname" in refs
/tags
/*) tagname
="${refname#refs/tags/}";; *) reftype
=ref
; tagname
="$refname"; esac
228 [ -z "$reflog" -o $# -eq 0 ] || usage
1
229 if [ -n "$reflog" ]; then
230 [ "$tagname" != "refs/tgstash" -o -n "$setreflogmsg" ] || reflogmsg
=1
231 git rev-parse
--verify --quiet "$refname" -- >/dev
/null || \
232 die
"no such ref: $refname"
233 [ -s "$git_dir/logs/$refname" ] || \
234 die
"no reflog present for $reftype: $tagname"
235 showref
="$(git rev-parse --revs-only --abbrev-ref=strict "$refname" --)"
238 if git config
--get-colorbool color.tgtag
; then
239 metacolor
="$(git config --get-color color.tgtag.meta)"
240 [ -n "$metacolor" ] || metacolor
="$(git config --get-color color.diff.meta "bold
")"
241 hashcolor
="$(git config --get-color color.tgtag.commit)"
242 [ -n "$hashcolor" ] || hashcolor
="$(git config --get-color color.diff.commit "yellow
")"
243 datecolor
="$(git config --get-color color.tgtag.date "bold blue
")"
244 timecolor
="$(git config --get-color color.tgtag.time "green
")"
245 resetcolor
="$(git config --get-color "" reset)"
250 sed 's/[^ ][^ ]* //' <"$git_dir/logs/$refname" | \
251 awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--]}' | \
252 git cat-file
--batch-check='%(objectname) %(objecttype) %(rest)' | \
256 while read -r newrev
type rest
; do
257 stashnum
=$
(( $stashnum + 1 ))
258 [ "$type" != "missing" ] ||
continue
259 IFS
="$tab" read -r cmmttr msg
<<-~EOT~
267 obj="$(git rev-parse --verify --quiet --short "$newrev" --)"
269 [ "$type" = "tag" -o -n "$notype" ] || \
270 extra="$hashcolor($metacolor$type$resetcolor$hashcolor)$resetcolor "
271 if [ -z "$reflogmsg" -o -z "$msg" ]; then
273 if [ "$type" = "tag" ]; then
274 objmsg="$(git cat-file tag "$obj" | \
275 sed '1,/^$/d' | sed '/^$/,$d')"
276 elif [ "$type" = "commit" ]; then
277 objmsg="$(git log -n 1 --format='format:%s' "$obj" --)"
279 [ -z "$objmsg" ] || msg="$objmsg"
281 read newdate newtime <<-EOT
282 $(strftime "%Y-%m-%d %H:%M:%S" "$es")
284 if [ "$lastdate" != "$newdate" ]; then
285 printf '%s=== %s ===%s\n' "$datecolor" "$newdate" "$resetcolor"
288 printf '%s %s %s%s@{%s}: %s\n' "$hashcolor$obj$reseutcolor" \
289 "$timecolor$newtime$resetcolor" \
290 "$extra" "$showref" "$stashnum" "$msg"
291 if [ -n "$maxcount" ]; then
292 maxcount=$(( $maxcount - 1 ))
293 [ $maxcount -gt 0 ] ||
break
301 [ -z "$signed" -o "$reftype" = "tag" ] || die
"signed tags must be under refs/tags"
302 [ $# -gt 0 ] ||
set -- $defbranch
303 if [ $# -eq 1 ] && [ "$1" = "--all" ]; then
304 set -- $
(git for-each-ref
--format="%(refname)" refs
/top-bases |
305 sed -e 's,^refs/top-bases/,,')
307 [ $# -ge 1 ] || die
"no TopGit branches found"
311 bname
="$(verify_topgit_branch "$b")"
312 case " ${branches:-..} " in *" $bname "*) :;; *)
313 branches
="${branches:+$branches }$bname"
317 ! git rev-parse
--verify --quiet "$refname" -- >/dev
/null ||
318 die
"$reftype '$tagname' already exists"
321 if [ -z "$outofdateok" ]; then
322 for b
in $branches; do
323 if ! needs_update
"$b" >/dev
/null
; then
325 echo "branch not up-to-date: $b"
329 [ -z "$out_of_date" ] || die
"all branches to be tagged must be up-to-date"
333 case " $seen_deps " in *" $_dep "*) return 0; esac
334 seen_deps
="${seen_deps:+$seen_deps }$_dep"
335 printf 'refs/heads/%s refs/heads/%s\n' "$_dep" "$_dep"
336 [ -z "$_dep_is_tgish" ] || \
337 printf 'refs/top-bases/%s refs/top-bases/%s\n' "$_dep" "$_dep"
343 recurse_deps_exclude
=
346 _dep
="$_b"; _dep_is_tgish
=1; show_dep
347 recurse_deps show_dep
"$_b"
348 recurse_deps_exclude
="$recurse_deps_exclude $seen_deps"
354 printf '%s\n' '-----BEGIN TOPGIT REFS-----'
355 show_deps
$branches | LC_ALL
=C
sort -u | \
356 git cat-file
--batch-check='%(objectname) %(rest)' |
grep -v ' missing$'
357 printf '%s\n' '-----END TOPGIT REFS-----'
360 if [ -n "$refsonly" ]; then
366 if [ -n "$msgfile" ]; then
367 if [ "$msgfile" = "-" ]; then
368 git stripspace
>"$git_dir/TAG_EDITMSG"
370 git stripspace
<"$msgfile" >"$git_dir/TAG_EDITMSG"
372 elif [ -n "$msg" ]; then
373 printf '%s\n' "$msg" | git stripspace
>"$git_dir/TAG_EDITMSG"
377 if [ ${#branches} -le 60 ]; then
378 printf '%s\n' "tag tg branches $branches"
379 printf '%s\n' "$updmsg"
381 printf '%s\n' "tag $(( $(printf '%s' "$branches" | wc -w) )) tg branches" ""
382 for b
in $branches; do
388 printf '%s\n' "tag tg branch $branches"
390 esac | git stripspace
>"$git_dir/TAG_EDITMSG"
391 if [ -z "$noedit" ]; then
395 # Please enter a message for tg tag:
397 # Lines starting with '#' will be ignored.
399 # tg branches to be tagged:
402 for b
in $branches; do
405 } >>"$git_dir/TAG_EDITMSG"
407 run_editor
"$git_dir/TAG_EDITMSG" || \
408 die
"there was a problem with the editor '$tg_editor'"
411 git stripspace
${stripcomments:+ --strip-comments} \
412 <"$git_dir/TAG_EDITMSG" >"$git_dir/TGTAG_FINALMSG"
413 [ -s "$git_dir/TGTAG_FINALMSG" ] || die
"no tag message?"
414 echo "" >>"$git_dir/TGTAG_FINALMSG"
415 get_refs
>>"$git_dir/TGTAG_FINALMSG"
420 parents
="$(git merge-base --independent \
421 $(printf 'refs/heads/%s^0 ' $branches))" || \
422 die
"failed: git merge-base --independent"
423 mttree
="$(git hash-object -t tree -w --stdin </dev/null)"
424 tagtarget
="$(printf '%s\n' "tg tag branch consolidation
" "" $branches | \
425 git commit-tree $mttree $(printf -- '-p %s ' $parents))"
428 tagtarget
="refs/heads/$branches^0"
432 if [ -n "$logrefupdates" ]; then
433 mkdir
-p "$git_dir/logs/$(dirname "$refname")" 2>/dev
/null ||
:
434 { >>"$git_dir/logs/$refname" ||
:; } 2>/dev
/null
436 if [ "$reftype" = "tag" -a -n "$signed" ]; then
437 [ -z "$quiet" ] ||
exec >/dev
/null
438 git tag
-F "$git_dir/TGTAG_FINALMSG" ${signed:+-s} ${force:+-f} \
439 ${keyid:+-u} ${keyid} "$tagname" "$tagtarget"
441 obj
="$(git rev-parse --verify --quiet "$tagtarget" --)" || \
442 die
"invalid object name: $tagtarget"
443 typ
="$(git cat-file -t "$tagtarget" 2>/dev/null)" || \
444 die
"invalid object name: $tagtarget"
445 id
="$(git var GIT_COMMITTER_IDENT 2>/dev/null)" || \
446 die
"could not get GIT_COMMITTER_IDENT"
448 printf '%s\n' "object
$obj" "type $typ" "tag
$tagname" \
450 cat "$git_dir/TGTAG_FINALMSG
"
451 } | git mktag)" || die
"git mktag failed"
452 old
="$(git rev-parse --verify --short --quiet "$refname" -- || :)"
456 if [ ${#branches} -le 100 ]; then
457 updmsg
="$(printf '%s\n' "tgtag
: $branches")"
459 updmsg
="$(printf '%s\n' "tgtag
: $
(( $
(printf '%s' "$branches" |
wc -w) )) branches
")"
463 updmsg
="$(printf '%s\n' "tgtag
: $branches")"
466 git update-ref
-m "$updmsg" "$refname" "$newtag"
467 [ -z "$old" -o -n "$quiet" ] ||
printf "Updated $reftype '%s' (was %s)\n" "$tagname" "$old"
469 rm -f "$git_dir/TAG_EDITMSG" "$git_dir/TGTAG_FINALMSG"