3 # (C) 2015 Kyle J. McKay <mackyle@gmail.com>
6 lf
="$(printf '\n.')" && lf
="${lf%?}"
7 tab
="$(printf '\t.')" && tab
="${tab%?}"
8 USAGE
="Usage: ${tgname:-tg} [...] tag [-s | -u <key-id>] [-f] [--no-edit] [-m <msg> | -F <file>] (<tagname> | --refs) [<branch>...]"
9 USAGE
="$USAGE$lf Or: ${tgname:-tg} [...] tag (-g | --reflog) [--reflog-message] [--no-type] [-n <number> | -number] [<tagname>]"
13 if [ "${1:-0}" != 0 ]; then
14 printf '%s\n' "$USAGE" >&2
16 printf '%s\n' "$USAGE"
42 [ -n "$1" ] ||
return 1
54 while [ $# -gt 0 ]; do case "$1" in
75 -u|
--local-user|
--local-user=*)
76 case "$1" in --local-user=*)
79 set -- --local-user "${x#--local-user=}" "$@"
82 echo "The $1 option requires an argument" >&2
103 -m|
--message|
--message=*)
104 case "$1" in --message=*)
107 set -- --message "${x#--message=}" "$@"
109 if [ $# -lt 2 ]; then
110 echo "The $1 option requires an argument" >&2
117 case "$1" in --file=*)
120 set -- --file "${x#--file=}" "$@"
122 if [ $# -lt 2 ]; then
123 echo "The $1 option requires an argument" >&2
129 -n|
--max-count|
--max-count=*|
-[1-9]*)
130 case "$1" in --max-count=*)
133 set -- --max-count "${x#--max-count=}" "$@"
135 case "$1" in -[1-9]*)
140 if [ $# -lt 2 ]; then
141 echo "The $1 option requires an argument" >&2
155 if [ -n "$reflog" ]; then
156 case "$2" in -[1-9]*)
161 set -- "$x2" "$x1" "$@"
170 echo "Unknown option: $1" >&2
174 if [ -n "$reflog" ]; then
175 case "$2" in -[1-9]*)
180 set -- "$x2" "$x1" "$@"
188 [ -z "$stash" -o -n "$reflog" ] ||
{ outofdateok
=1; force
=1; defnoedit
=1; }
189 [ -n "$noedit" ] || noedit
="$defnoedit"
190 [ "$noedit" != "0" ] || noedit
=
191 [ -z "$reflog" -o -z "$signed$keyid$force$msg$msgfile$noedit$refsonly$outofdateok" ] || usage
1
192 [ -n "$reflog" -o -z "$setreflogmsg$notype$maxcount" ] || usage
1
193 [ -z "$maxcount" ] || is_numeric
"$maxcount" || die
"invalid count: $maxcount"
194 [ -z "$maxcount" ] ||
[ $maxcount -gt 0 ] || die
"invalid count: $maxcount"
195 [ -z "$msg" -o -z "$msgfile" ] || die
"only one -F or -m option is allowed."
196 [ -z "$refsonly" ] ||
set -- refs..only
"$@"
197 [ $# -gt 0 -o -z "$reflog" ] ||
set -- --stash
198 [ -n "$1" ] ||
{ echo "Tag name required" >&2; usage
1; }
201 [ "$tagname" != "--stash" ] || tagname
=refs
/tgstash
203 case "$refname" in HEAD|refs
/*) :;; *) refname
="refs/tags/$refname"; esac
205 case "$refname" in refs
/tags
/*) tagname
="${refname#refs/tags/}";; *) reftype
=ref
; esac
206 [ -z "$reflog" -o $# -eq 0 ] || usage
1
207 if [ -n "$reflog" ]; then
208 [ "$tagname" != "refs/tgstash" -o -n "$setreflogmsg" ] || reflogmsg
=1
209 git rev-parse
--verify --quiet "$refname" -- >/dev
/null || \
210 die
"no such ref: $refname"
211 showref
="$(git rev-parse --abbrev-ref=strict "$refname")"
212 [ -s "$git_dir/logs/$refname" ] || \
213 die
"no reflog present for ref: $refname"
216 if git config
--get-colorbool color.tgtag
; then
217 metacolor
="$(git config --get-color color.tgtag.meta)"
218 [ -n "$metacolor" ] || metacolor
="$(git config --get-color color.diff.meta "bold
")"
219 hashcolor
="$(git config --get-color color.tgtag.commit)"
220 [ -n "$hashcolor" ] || hashcolor
="$(git config --get-color color.diff.commit "yellow
")"
221 datecolor
="$(git config --get-color color.tgtag.date "bold blue
")"
222 timecolor
="$(git config --get-color color.tgtag.time "green
")"
223 resetcolor
="$(git config --get-color "" reset)"
226 sed 's/[^ ][^ ]* //' <"$git_dir/logs/$refname" | \
227 awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--]}' | \
228 git cat-file
--batch-check='%(objectname) %(objecttype) %(rest)' | \
232 while read -r newrev
type rest
; do
233 stashnum
=$
(( $stashnum + 1 ))
234 [ "$type" != "missing" ] ||
continue
235 IFS
="$tab" read -r cmmttr msg
<<-~EOT~
243 obj="${newrev#????????}"
246 [ "$type" = "tag" -o -n "$notype" ] || \
247 extra="$hashcolor($metacolor$type$resetcolor$hashcolor)$resetcolor "
248 if [ -z "$reflogmsg" -o -z "$msg" ]; then
250 if [ "$type" = "tag" ]; then
251 objmsg="$(git cat-file tag "$obj" | \
252 sed '1,/^$/d' | sed '/^$/,$d')"
253 elif [ "$type" = "commit" ]; then
254 objmsg="$(git log -n 1 --format='format:%s' "$obj" --)"
256 [ -z "$objmsg" ] || msg="$objmsg"
258 read newdate newtime <<-EOT
259 $(strftime "%Y-%m-%d %H:%M:%S" "$es")
261 if [ "$lastdate" != "$newdate" ]; then
262 printf '%s=== %s ===%s\n' "$datecolor" "$newdate" "$resetcolor"
265 printf '%s %s %s%s@{%s}: %s\n' "$hashcolor$obj$reseutcolor" \
266 "$timecolor$newtime$resetcolor" \
267 "$extra" "$showref" "$stashnum" "$msg"
268 if [ -n "$maxcount" ]; then
269 maxcount=$(( $maxcount - 1 ))
270 [ $maxcount -gt 0 ] ||
break
276 [ -z "$signed" -o "$reftype" = "tag" ] || die
"signed tags must be under refs/tags"
277 [ $# -gt 0 ] ||
set -- $defbranch
278 if [ $# -eq 1 ] && [ "$1" = "--all" ]; then
279 set -- $
(git for-each-ref
--format="%(refname)" refs
/top-bases |
280 sed -e 's,^refs/top-bases/,,')
282 [ $# -ge 1 ] || die
"no TopGit branches found"
286 bname
="$(verify_topgit_branch "$b")"
287 case " ${branches:-..} " in *" $bname "*) :;; *)
288 branches
="${branches:+$branches }$bname"
292 ! git rev-parse
--verify --quiet "$refname" -- >/dev
/null ||
293 die
"$reftype '$tagname' already exists"
296 if [ -z "$outofdateok" ]; then
297 for b
in $branches; do
298 if ! needs_update
"$b" >/dev
/null
; then
300 echo "branch not up-to-date: $b"
304 [ -z "$out_of_date" ] || die
"all branches to be tagged must be up-to-date"
308 _editor
="$GIT_EDITOR"
309 [ -n "$_editor" ] || _editor
="$(git var GIT_EDITOR)" ||
return $?
310 eval "$_editor" '"$@"'
315 case " $seen_deps " in *" $_dep "*) return 0; esac
316 seen_deps
="${seen_deps:+$seen_deps }$_dep"
317 printf 'refs/heads/%s refs/heads/%s\n' "$_dep" "$_dep"
318 [ -z "$_dep_is_tgish" ] || \
319 printf 'refs/top-bases/%s refs/top-bases/%s\n' "$_dep" "$_dep"
325 recurse_deps_exclude
=
328 _dep
="$_b"; _dep_is_tgish
=1; show_dep
329 recurse_deps show_dep
"$_b"
330 recurse_deps_exclude
="$recurse_deps_exclude $seen_deps"
336 printf '%s\n' '-----BEGIN TOPGIT REFS-----'
337 show_deps
$branches | LC_ALL
=C
sort -u | \
338 git cat-file
--batch-check='%(objectname) %(rest)' |
grep -v ' missing$'
339 printf '%s\n' '-----END TOPGIT REFS-----'
342 if [ -n "$refsonly" ]; then
348 if [ -n "$msgfile" ]; then
349 if [ "$msgfile" = "-" ]; then
350 git stripspace
>"$git_dir/TAG_EDITMSG"
352 git stripspace
<"$msgfile" >"$git_dir/TAG_EDITMSG"
354 elif [ -n "$msg" ]; then
355 printf '%s\n' "$msg" | git stripspace
>"$git_dir/TAG_EDITMSG"
359 if [ ${#branches} -le 60 ]; then
360 printf '%s\n' "tag tg branches $branches"
361 printf '%s\n' "$updmsg"
363 printf '%s\n' "tag $(( $(printf '%s' "$branches" | wc -w) )) tg branches" ""
364 for b
in $branches; do
370 printf '%s\n' "tag tg branch $branches"
372 esac | git stripspace
>"$git_dir/TAG_EDITMSG"
373 if [ -z "$noedit" ]; then
377 # Please enter a message for tg tag:
379 # Lines starting with '#' will be ignored.
381 # tg branches to be tagged:
384 for b
in $branches; do
387 } >>"$git_dir/TAG_EDITMSG"
389 run_editor
"$git_dir/TAG_EDITMSG" || \
390 die
"there was a problem with the editor '$_editor'"
393 git stripspace
${stripcomments:+ --strip-comments} \
394 <"$git_dir/TAG_EDITMSG" >"$git_dir/TGTAG_FINALMSG"
395 [ -s "$git_dir/TGTAG_FINALMSG" ] || die
"no tag message?"
396 echo "" >>"$git_dir/TGTAG_FINALMSG"
397 get_refs
>>"$git_dir/TGTAG_FINALMSG"
402 parents
="$(git merge-base --independent \
403 $(printf 'refs/heads/%s^0 ' $branches))" || \
404 die
"failed: git merge-base --independent"
405 mttree
="$(git hash-object -t tree -w --stdin </dev/null)"
406 tagtarget
="$(printf '%s\n' "tg tag branch consolidation
" "" $branches | \
407 git commit-tree $mttree $(printf -- '-p %s ' $parents))"
410 tagtarget
="refs/heads/$branches^0"
414 if [ -n "$logrefupdates" ]; then
415 mkdir
-p "$git_dir/logs/$(dirname "$refname")" 2>/dev
/null ||
:
416 { >>"$git_dir/logs/$refname" ||
:; } 2>/dev
/null
418 if [ "$reftype" = "tag" -a -n "$signed" ]; then
419 git tag
-F "$git_dir/TGTAG_FINALMSG" ${signed:+-s} ${force:+-f} \
420 ${keyid:+-u} ${keyid} "$tagname" "$tagtarget"
422 obj
="$(git rev-parse --verify --quiet "$tagtarget" --)" || \
423 die
"invalid object name: $tagtarget"
424 typ
="$(git cat-file -t "$tagtarget" 2>/dev/null)" || \
425 die
"invalid object name: $tagtarget"
426 id
="$(git var GIT_COMMITTER_IDENT 2>/dev/null)" || \
427 die
"could not get GIT_COMMITTER_IDENT"
429 printf '%s\n' "object
$obj" "type $typ" "tag
$tagname" \
431 cat "$git_dir/TGTAG_FINALMSG
"
432 } | git mktag)" || die
"git mktag failed"
433 old
="$(git rev-parse --verify --short --quiet "$refname" || :)"
437 if [ ${#branches} -le 100 ]; then
438 updmsg
="$(printf '%s\n' "tgtag
: $branches")"
440 updmsg
="$(printf '%s\n' "tgtag
: $
(( $
(printf '%s' "$branches" |
wc -w) )) branches
")"
444 updmsg
="$(printf '%s\n' "tgtag
: $branches")"
447 git update-ref
-m "$updmsg" "$refname" "$newtag"
448 [ -z "$old" ] ||
printf "Updated $reftype '%s' (was %s)\n" "$tagname" "$old"
450 rm -f "$git_dir/TAG_EDITMSG" "$git_dir/TGTAG_FINALMSG"