| #!/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 |