问题
git branch -avv
shows all branches (including remote ones), decorating them with the tracking branch, short-SHA and commit title. Is there a similar command to display all tags together with the commit they point to, optionally including whether it is pushed upstream, and additionally with the message if the tag was annotated?
The best I could get so far is:
git tag --format '%(color:green)%(refname:short)%09%(color:white)%(objectname:short) %(contents:subject)'
But this has several problems:
- Depending on tag-length, the alignment via
%09
(a\t
) fails - No way to see whether the tag is lightweight or not (
%(objecttype)
displays this as eithercommit
ortag
, but I'd prefer something like the tag annotation on the next line) %(objectname:short)
turns out to be the tag's SHA for annotated tags, whereas I want to know what they are pointing to.
These last two boil down to basically wanting what git show-refs --tags -d
shows, but that doesn't offer --format
.
回答1:
In a sufficiently modern Git (which you clearly have here), the listing varieties of git branch
and git tag
(even including --contains
and the like) are just specializations of git for-each-ref
, run over the refs/heads/
and refs/tags/
name spaces respectively.
Since git for-each-ref
is a plumbing command, though, you can use it to write a script that does anything you like. We will need this in a moment.
Most of what you want is directly encode-able in for-each-ref
's %
directives. Since your git tag
is modern enough to take --format
in the first place, you can just use git tag
directly for most of this anyway. In any case, though, it's worth carefully studying the git for-each-ref documentation as the --format
directives are surprisingly complex.
Instead of %(objectname:short)
, you can use %(*objectname:short)
to get the target of the tag (the *
action only applies to annotated tag objects, it's a no-op on other objects).
The one extra-difficult problem is column alignment. There is a %(align:position,width)
directive (since Git 2.8) that takes care of most of the issue. (You can spell this as %(align:position=num,width=num)
if you prefer.) Since your middle column, the shortened OID, is fixed width, we need only one %align
:
git tag --format '%(align:1,20)%(color:green)%(refname:short)%(end)
%(color:white)%(*objectname:short) %(contents:subject)'
(I broke this into two lines for display purposes). The one obvoius issue here is: Where did we get the magic constant 20?
The answer is, it was just a WAG. If you'd like to compute the correct number, we need two passes: one to count the maximum width of any tag, and a second to display the tags. The "count the max width" is where we really need git for-each-ref
, as we need a bit of shell scripting:
# Output the length of the longest tag. If there are no tags,
# print 0 (most logically correct but some callers might want 1;
# consider making a minimum output value an argument, which is
# trivial to do: initialize longest with "${1-0}" instead of just
# "0").
max_tag_len()
{
local longest=0 name len
git for-each-ref --format='%(refname:short)' refs/tags | {
while read name; do
len=${#name}
[ $len -gt $longest ] && longest=$len
done
echo $longest
}
}
(Note that, unlike some other cases, we don't need a trailing slash on the refs/tags
argument to git for-each-ref
. Not that one actually hurts here, it's just unnecessary.) Now we can do:
width=$(max_tag_len)
git tag --format "%(align:1,$width)%(color:green)..."
In this case you must insert an explicit space, since we have at least one tag that completely fills the column. A bit of shell arithmetic provides an alternative:
width=$(($(max_tag_len) + 1))
Use whichever you think is clearest.
来源:https://stackoverflow.com/questions/42156698/how-to-get-a-git-branch-avv-like-list-of-tags