You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
8.5 KiB
286 lines
8.5 KiB
#!/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-parse --not --all | git-rev-list --stdin $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-parse --not --all | |
|
git-rev-list --stdin --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
|
|
|