Git like a pro: sort git tags by date

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:

Article series
Git like a pro

  1. Git like a pro
  2. Sort git tags by date
  3. 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”

  1. Motti Shneor Reply

    Please… I see you use the :raw option for interpolating dates. I know there is also :relative (because I saw it in some other example in stack overflow) but I cannot find the list of display options. git help for-each-ref says almost nothing about –format, and I’m desperately looking for extensive documentation of the formatting available within git. I appreciate your strftime trick, but I want to know better about git itself. Thanks!

  2. An Reply

    isn’t this enough?

    git log --tags --simplify-by-decoration --pretty="format:%ai %d" | sort

    • Anonymous Reply

      if I want to sort it in such a way that the latest files come first, what should I append to ‘ sort’ ? I have the following git command

      git ls-files -z | xargs -0 -n1 -I{} — git log -1 –format=”%ai {} %an” {} | sort > logtest.log

  3. Sherin Sunny Reply

    if I want to sort it in such a way that the latest files come first, what should I append to ‘ sort’ ? I have the following git command

    git ls-files -z | xargs -0 -n1 -I{} — git log -1 –format=”%ai {} %an” {} | sort > logtest.log

Leave a Reply

Your email address will not be published.