# Function to retrieve and filter tag names tags() { case $1 in remote) git -C "$repo" show-ref --tags ;; local) git -C "$repo" ls-remote --quiet --refs --tags ;; *) return 2 ;; esac | while read -r _ tag ; do tag=${tag#refs/tags/} printf '%s\n' "$tag" done } # 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 if [ "$#" -eq 0 ] ; then set -- "$PWD" fi # Iterate through each repo in a subshell in parallel for repo ; do ( # Make a temporary directory with a hash in its name for uniqueness name=$(printf '%s' "$repo" | sed 's:/:_:g') cksum=$(printf '%s' "$repo" | cksum | sed 's:[^0-9].*::') sd=$td/$name.$cksum mkdir -- "$sd" "$sd"/tags || 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 tags local "$repo" > tags/local || exit tags remote "$repo" > tags/remote || exit # Write new tags to file LC_COLLATE=C comm -13 -- tags/local tags/remote > tags/new # Attempt to quietly fetch new tags so that we don't notify about the same # ones next time [ -s tags/new ] || exit git -C "$repo" fetch --quiet --tags ) & done # Wait for all of those to finish wait # Iterate through the temp dirs in order for dir in "$td"/* ; do ( cd -- "$dir" || exit # Look for non-zero "new" files (at least one new tag) [ -s tags/new ] || exit # Print repository path and new tags cat path while read -r tag ; do printf '* %s\n' "$tag" done < tags/new ) ; done # Haven't yet decided on exit value semantics; for the moment, if it completes, # exit success exit 0