When you issue git tag
it will show you all tags of a repository sorted in alphabetical order. But actually it makes much more sense to see tags sorted by tagging date. Unfortunately there is currently no such git sub command that accomplishes this easily. So we are going to write our own:
- Git like a pro
- Sort git tags by date
- Rewrite author history
1. The Basics
The most simple form to sort tags by date is shown below:
git for-each-ref --sort=taggerdate --format '%(tag)'
...
v1.51
v1.52
v1.52.1
v1.53
v1.54
But you could also display some more information instead of just the tag itself.
2. Verbose Output
As you have probably guessed it already, the parameter –format is responsible to extend the information. For a full list of all possible values, have a look at man git-for-each-ref.
For now, we are using: the tag name, the tagging date, the name of the tagger and the tag message:
git for-each-ref --sort=taggerdate --format '%(tag) %(taggerdate:raw) %(taggername) %(subject)' refs/tags
...
v1.51 1438592208 +0200 FirstName LastName Release v1.51
v1.52 1439215948 +0200 Jane Doe Release v1.52
v1.52.1 1439907306 +0200 John Doe Release v1.52.1
v1.53 1440673885 +0200 Cytopia Release v1.53
v1.54 1442223780 +0200 Cytopia Release v1.54
We now have some more information, but it is not very cleary arranged.
3. Prettify
Before we can start to apply our command line-fu on the above output, we will set a clear goal:
- Make each column aligned vertically
- Show a human readable date
For the vertical alignment there are also a few problems that might arise:
- The tagname can have a variable length
- The tagger name is also separated by white spaces
Another thing is that the the output separation can currently not be done via whitespace as the tagger name can have multiple words separated by spaces itself.
So the first thing is to separate everything else by something else than space which is sort of unique:
git for-each-ref --sort=taggerdate --format '%(tag)_,,,_%(taggerdate:raw)_,,,_%(taggername)_,,,_%(subject)' refs/tags
...
v1.51_,,,_1438592208 +0200_,,,_FirstName LastName_,,,_Release v1.51
v1.52_,,,_1439215948 +0200_,,,_Jane Doe Plocke_,,,_Release v1.52
v1.52.1_,,,_1439907306 +0200_,,,_John Doe_,,,_Hotfix Release v1.52.1
v1.53_,,,_1440673885 +0200_,,,_Cytopia_,,,_Release v1.53
v1.54_,,,_1442223780 +0200_,,,_Cytopia_,,,_Release v1.54
Looks more machine readable. And now we can apply some awk magic on it:
git for-each-ref --sort=taggerdate --format '%(tag)_,,,_%(taggerdate:raw)_,,,_%(taggername)_,,,_%(subject)' refs/tags \
| awk 'BEGIN { FS = "_,,,_" } ; { printf "%-20s %-18s %-25s %s\n", $2, $1, $4, $3 }'
...
1438592208 +0200 v1.51 Release v1.51 FirstName LastName
1439215948 +0200 v1.52 Release v1.52 Jane Doe
1439907306 +0200 v1.52.1 Hotfix Release v1.52.1 John Doe
1440673885 +0200 v1.54 Release v1.53 Cytopia
1442223780 +0200 v1.55 Release v1.54 Cytopia
So what does it do?
with FS we are setting the field separator for awk to the one we have added to the –format section in git.
'BEGIN { FS = "_,,,_" }'...
No we re-order and print the columns. The printf command applies proper spacings between the actual columns. Feel free to adjust them as desired.
... '{ printf "%-20s %-18s %-25s %s\n", $2, $1, $4, $3 }'
The last thing that is missing is to get a nice readable date.
4. Format Date
awk has a toolkit to convert a timestamp to a readable date: strftime.
git for-each-ref --sort=taggerdate --format '%(tag)_,,,_%(taggerdate:raw)_,,,_%(taggername)_,,,_%(subject)' refs/tags \
| awk 'BEGIN { FS = "_,,,_" } ; { t=strftime("%Y-%m-%d %H:%M",$2); printf "%-20s %-18s %-25s %s\n", t, $1, $4, $3 }'
...
2015-08-03 10:56 v1.51 Release v1.51 FirstName LastName
2015-08-10 16:12 v1.52 Release v1.52 Jane Doe
2015-08-18 16:15 v1.52.1 Hotfix Release v1.52.1 John Doe
2015-08-27 13:11 v1.54 Release v1.53 Cytopia
2015-09-14 11:43 v1.55 Release v1.54 Cytopia
So what does it do?
We first set a new variable t
and assign it with a formatted date value from column 2.
... '{ t=strftime("%Y-%m-%d %H:%M",$2);'...
Note that in the printf part we are using the variable t
again to show up as our first column.
... 'printf "%-20s %-18s %-25s %s\n", t, $1, $4, $3 }'
Te output looks much better now. The only problem I see is that I don’t want to enter such a long command everytime I want to have a quick look at the tags of a repository. So this has to go into the global gitconfig.
5. Gitconfig Alias
Inside your ~/.gitconfig
create a section [alias]
and paste the following command.
Note that there is some escaping inside awk.
[alias]
# Show tags sorted by date
tags = !"git for-each-ref \
--sort=taggerdate \
--format '%(tag)_,,,_%(taggerdate:raw)_,,,_%(taggername)_,,,_%(subject)' refs/tags \
| awk 'BEGIN { FS = \"_,,,_\" } ; { t=strftime(\"%Y-%m-%d %H:%M\",$2); printf \"%-20s %-18s %-25s %s\\n\", t, $1, $4, $3 }'"
From now on we are able to just issue git tags
inside a repository
git tags
...
2015-08-03 10:56 v1.51 Release v1.51 FirstName LastName
2015-08-10 16:12 v1.52 Release v1.52 Jane Doe
2015-08-18 16:15 v1.52.1 Hotfix Release v1.52.1 John Doe
2015-08-27 13:11 v1.54 Release v1.53 Cytopia
2015-09-14 11:43 v1.55 Release v1.54 Cytopia
6. Summary
So here is the final command:
git for-each-ref --sort=taggerdate --format '%(tag)_,,,_%(taggerdate:raw)_,,,_%(taggername)_,,,_%(subject)' refs/tags \
| awk 'BEGIN { FS = "_,,,_" } ; { t=strftime("%Y-%m-%d %H:%M",$2); printf "%-20s %-18s %-25s %s\n", t, $1, $4, $3 }'
And also what you need to add to your gitconfig:
tags = !"git for-each-ref \
--sort=taggerdate \
--format '%(tag)_,,,_%(taggerdate:raw)_,,,_%(taggername)_,,,_%(subject)' refs/tags \
| awk 'BEGIN { FS = \"_,,,_\" } ; { t=strftime(\"%Y-%m-%d %H:%M\",$2); printf \"%-20s %-18s %-25s %s\\n\", t, $1, $4, $3 }'"
If anybody knows a simpler or more elegant way to achieve this, let me know. I am always looking for better git aliases.
5 comments on “Git like a pro: sort git tags by date”