blob: 555bd5f53249119c54f032137d7f59a859b7d2ce [file] [log] [blame]
#!/bin/sh
#
# An example hook script to mail out commit update information.
# It can also blocks tags that aren't annotated.
# Called by git-receive-pack with arguments: refname sha1-old sha1-new
#
# To enable this hook, make this file executable by "chmod +x update".
#
# Config
# ------
# hooks.mailinglist
# This is the list that all pushes will go to; leave it blank to not send
# emails frequently. The log email will list every log entry in full between
# the old ref value and the new ref value.
# hooks.announcelist
# This is the list that all pushes of annotated tags will go to. Leave it
# blank to just use the mailinglist field. The announce emails list the
# short log summary of the changes since the last annotated tag
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be.
#
# Notes
# -----
# All emails have their subjects prefixed with "[SCM]" to aid filtering.
# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and info.
# --- Constants
EMAILPREFIX="[SCM] "
LOGBEGIN="- Log -----------------------------------------------------------------"
LOGEND="-----------------------------------------------------------------------"
DATEFORMAT="%F %R %z"
# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"
# --- Safety check
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
exit 1
fi
# --- Config
projectdesc=$(cat $GIT_DIR/description)
recipients=$(git-repo-config hooks.mailinglist)
announcerecipients=$(git-repo-config hooks.announcelist)
allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
# --- Check types
newrev_type=$(git-cat-file -t "$newrev")
case "$refname","$newrev_type" in
refs/tags/*,commit)
# un-annotated tag
refname_type="tag"
short_refname=${refname##refs/tags/}
if [ "$allowunannotated" != "true" ]; then
echo "*** The un-annotated tag, $short_refname is not allowed in this repository" >&2
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
exit 1
fi
;;
refs/tags/*,tag)
# annotated tag
refname_type="annotated tag"
short_refname=${refname##refs/tags/}
# change recipients
if [ -n "$announcerecipients" ]; then
recipients="$announcerecipients"
fi
;;
refs/heads/*,commit)
# branch
refname_type="branch"
short_refname=${refname##refs/heads/}
;;
refs/remotes/*,commit)
# tracking branch
refname_type="tracking branch"
short_refname=${refname##refs/remotes/}
# Should this even be allowed?
echo "*** Push-update of tracking branch, $refname. No email generated." >&2
exit 0
;;
*)
# Anything else (is there anything else?)
echo "*** Update hook: unknown type of update, \"$newrev_type\", to ref $refname" >&2
exit 1
;;
esac
# Check if we've got anyone to send to
if [ -z "$recipients" ]; then
# If the email isn't sent, then at least give the user some idea of what command
# would generate the email at a later date
echo "*** No recipients found - no email will be sent, but the push will continue" >&2
echo "*** for $0 $1 $2 $3" >&2
exit 0
fi
# --- Email parameters
committer=$(git show --pretty=full -s $newrev | grep "^Commit: " | sed -e "s/^Commit: //")
describe=$(git describe $newrev 2>/dev/null)
if [ -z "$describe" ]; then
describe=$newrev
fi
# --- Email (all stdout will be the email)
(
# Generate header
cat <<-EOF
From: $committer
To: $recipients
Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname now at $describe
X-Git-Refname: $refname
X-Git-Reftype: $refname_type
X-Git-Oldrev: $oldrev
X-Git-Newrev: $newrev
Hello,
This is an automated email from the git hooks/update script, it was
generated because a ref change was pushed to the repository.
Updating $refname_type, $short_refname,
EOF
case "$refname_type" in
"tracking branch"|branch)
if expr "$oldrev" : '0*$' >/dev/null
then
# If the old reference is "0000..0000" then this is a new branch
# and so oldrev is not valid
echo " as a new $refname_type"
echo " to $newrev ($newrev_type)"
echo ""
echo $LOGBEGIN
# This shows all log entries that are not already covered by
# another ref - i.e. commits that are now accessible from this
# ref that were previously not accessible
git-rev-parse --not --all | git-rev-list --stdin --pretty $newref
echo $LOGEND
else
# oldrev is valid
oldrev_type=$(git-cat-file -t "$oldrev")
# Now the problem is for cases like this:
# * --- * --- * --- * (oldrev)
# \
# * --- * --- * (newrev)
# i.e. there is no guarantee that newrev is a strict subset
# of oldrev - (would have required a force, but that's allowed).
# So, we can't simply say rev-list $oldrev..$newrev. Instead
# we find the common base of the two revs and list from there
baserev=$(git-merge-base $oldrev $newrev)
# Commit with a parent
for rev in $(git-rev-list $newrev ^$baserev)
do
revtype=$(git-cat-file -t "$rev")
echo " via $rev ($revtype)"
done
if [ "$baserev" = "$oldrev" ]; then
echo " from $oldrev ($oldrev_type)"
else
echo " based on $baserev"
echo " from $oldrev ($oldrev_type)"
echo ""
echo "This ref update crossed a branch point; i.e. the old rev is not a strict subset"
echo "of the new rev. This occurs, when you --force push a change in a situation"
echo "like this:"
echo ""
echo " * -- * -- B -- O -- O -- O ($oldrev)"
echo " \\"
echo " N -- N -- N ($newrev)"
echo ""
echo "Therefore, we assume that you've already had alert emails for all of the O"
echo "revisions, and now give you all the revisions in the N branch from the common"
echo "base, B ($baserev), up to the new revision."
fi
echo ""
echo $LOGBEGIN
git-rev-list --pretty $newrev ^$baserev
echo $LOGEND
echo ""
echo "Diffstat:"
git-diff-tree --no-color --stat -M -C --find-copies-harder $newrev ^$baserev
fi
;;
"annotated tag")
# Should we allow changes to annotated tags?
if expr "$oldrev" : '0*$' >/dev/null
then
# If the old reference is "0000..0000" then this is a new atag
# and so oldrev is not valid
echo " to $newrev ($newrev_type)"
else
echo " to $newrev ($newrev_type)"
echo " from $oldrev"
fi
# If this tag succeeds another, then show which tag it replaces
prevtag=$(git describe $newrev^ 2>/dev/null | sed 's/-g.*//')
if [ -n "$prevtag" ]; then
echo " replaces $prevtag"
fi
# Read the tag details
eval $(git cat-file tag $newrev | \
sed -n '4s/tagger \([^>]*>\)[^0-9]*\([0-9]*\).*/tagger="\1" ts="\2"/p')
tagged=$(date --date="1970-01-01 00:00:00 +0000 $ts seconds" +"$DATEFORMAT")
echo " tagged by $tagger"
echo " on $tagged"
echo ""
echo $LOGBEGIN
echo ""
if [ -n "$prevtag" ]; then
git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
else
git rev-list --pretty=short $newrev | git shortlog
fi
echo $LOGEND
echo ""
;;
*)
# By default, unannotated tags aren't allowed in; if
# they are though, it's debatable whether we would even want an
# email to be generated; however, I don't want to add another config
# option just for that.
#
# Unannotated tags are more about marking a point than releasing
# a version; therefore we don't do the shortlog summary that we
# do for annotated tags above - we simply show that the point has
# been marked, and print the log message for the marked point for
# reference purposes
#
# Note this section also catches any other reference type (although
# there aren't any) and deals with them in the same way.
if expr "$oldrev" : '0*$' >/dev/null
then
# If the old reference is "0000..0000" then this is a new tag
# and so oldrev is not valid
echo " as a new $refname_type"
echo " to $newrev ($newrev_type)"
else
echo " to $newrev ($newrev_type)"
echo " from $oldrev"
fi
echo ""
echo $LOGBEGIN
git-show --no-color --root -s $newrev
echo $LOGEND
echo ""
;;
esac
# Footer
cat <<-EOF
hooks/update
---
Git Source Code Management System
$0 $1 \\
$2 \\
$3
EOF
#) | cat >&2
) | /usr/sbin/sendmail -t
# --- Finished
exit 0