Short Note: auto-tag commits on main branch

In case you use some tools (like goreleaser) that depend on running in the case of tagged commits only, but you wish at the same time not to bother with manually tagging your commits but just to push to master… I found a nice & easy way to auto-tag commits locally with a “post commit” Git hook.

TL;DR: If you wish to use this approach, here is the post commit hook:

#!/bin/sh

COMMIT=$(git rev-parse --short HEAD)
DATE=$(git log -1 --format=%cd --date=format:"%Y%m%d")
git tag -a "$DATE-$COMMIT" -m "Tagging $DATE-$COMMIT"
echo "Code commited and tagged as $DATE-$COMMIT"

echo "Removing deprecated un-pushed tags..."
git show-ref --tags | \
  grep -v -F "$(git ls-remote --tags origin | grep -v '\^{}' | cut -f 2)" | \
  grep -v "$(git rev-parse --short HEAD)" | \
  awk -F'/' '{print $3}' | \
  xargs -I{} bash -c "git tag --delete {}"

Installation of the hook

To install it, you need to save this file as .git/hooks/post-commit file and make it executable with chmod +x .git/hooks/post-commit.

What does it actually do:

Step 1 - create tag on each commit

The following part will generate a reasonable tag value, like 20240921-4318a7c, by combining current date with the short value of the last commit (the one that has just created, and for which the post-commit hook was triggered).

COMMIT=$(git rev-parse --short HEAD)
DATE=$(git log -1 --format=%cd --date=format:"%Y%m%d")
git tag -a "$DATE-$COMMIT" -m "Tagging $DATE-$COMMIT"
echo "Code commited and tagged as $DATE-$COMMIT"

Step 2 - remove “deprecated” local tags

In case you created locally 3 commits, you most probably want to only tag the last one, right? That’s why we need to list all the tags that are present only locally and to remove all the other tags previosly locally created by this post commit hook

echo "Removing deprecated un-pushed tags..."
git show-ref --tags | \
  grep -v -F "$(git ls-remote --tags origin | grep -v '\^{}' | cut -f 2)" | \
  grep -v "$(git rev-parse --short HEAD)" | \
  awk -F'/' '{print $3}' | \
  xargs -I{} bash -c "git tag --delete {}"

What does this part exactly do:

  1. git show-ref --tags - list all the known tags

  2. grep -v -F "$(git ls-remote --tags origin | grep -v '\^{}' | cut -f 2)" - find the tags that are known on the origin remote (you might want to replace origin with the name of your remote in case you don’t use the default one), and create a negative regex filter for those for the incoming stream

  3. grep -v "$(git rev-parse --short HEAD)" - identify the current tag (which we want to keep since we’ve just made it) and create a negative filter to exclude it from the incoming stream

  4. awk -F'/' '{print $3}' - keep only the last part of the tag names only by removing all the text before the last / character

  5. xargs -I{} bash -c "git tag --delete {}" - run the tag deletion of the tags that remain