#!/bin/sh self=watch-git-tags # List sorted local tags lt() { git tag --list | LC_COLLATE=C sort } # List sorted remote tags rt() { { git ls-remote --quiet --refs --tags || printf >&2 'Failed to retrieve tags for repository %s\n' "$PWD" } | cut -d/ -f3 | LC_COLLATE=C sort } # Create a temporary directory with name in $td, and handle POSIX-ish traps to # remove it when the script exits; requires mktemp(1) (not POSIX) td=$(mktemp -d) || exit cleanup() { [ -n "$td" ] || return rm -fr -- "$td" } for sig in EXIT HUP INT TERM ; do # shellcheck disable=SC2064 trap "cleanup $sig" "$sig" done # Use current directory if no other arguments [ "$#" -gt 0 ] || set -- . # Iterate through each repo in a subshell in parallel for repo ; do ( # Make a temporary directory with a hash in its name for uniqueness df=$(printf %s "$repo" | sed s:/:_:g) cs=$(printf %s "$repo" | cksum) sd=$td/$df.${cs%% *} mkdir -- "$sd" || exit # Step in and write repo path to file cd -- "$sd" || exit printf '%s\n' "$repo" > path || exit # Write local and remote tags to files ( cd -- "$repo" || exit lt > "$sd"/a || exit rt > "$sd"/b ) || exit # Write new tags to file LC_COLLATE=C comm -13 -- [ab] > new # Attempt to quietly fetch new tags so that we don't notify about the same # ones next time if [ -s new ] ; then git fetch --quiet --tags fi ) & done # Wait for each of those to finish wait # Iterate through the temp dirs in order for dir in "$td"/* ; do ( cd -- "$dir" || exit 0 # Look for non-zero "new" files (at least one new tag) [ -s new ] || exit 0 # Print repository path and new tags sed '1!s/^/\t/' -- path new exit 1 ) ; done