a7cf604dd0ee465ad3d8a874f105fe45c9e7c6e7
   1#!/bin/sh
   2#
   3# An example hook script to mail out commit update information.
   4# It can also blocks tags that aren't annotated.
   5# Called by git-receive-pack with arguments: refname sha1-old sha1-new
   6#
   7# To enable this hook, make this file executable by "chmod +x update".
   8#
   9# Config
  10# ------
  11# hooks.mailinglist
  12#   This is the list that all pushes will go to; leave it blank to not send
  13#   emails frequently.  The log email will list every log entry in full between
  14#   the old ref value and the new ref value.
  15# hooks.announcelist
  16#   This is the list that all pushes of annotated tags will go to.  Leave it
  17#   blank to just use the mailinglist field.  The announce emails list the
  18#   short log summary of the changes since the last annotated tag
  19# hooks.allowunannotated
  20#   This boolean sets whether unannotated tags will be allowed into the
  21#   repository.  By default they won't be.
  22#
  23# Notes
  24# -----
  25# All emails have their subjects prefixed with "[SCM]" to aid filtering.
  26# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
  27# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and info.
  28
  29# --- Constants
  30EMAILPREFIX="[SCM] "
  31LOGBEGIN="- Log -----------------------------------------------------------------"
  32LOGEND="-----------------------------------------------------------------------"
  33DATEFORMAT="%F %R %z"
  34
  35# --- Command line
  36refname="$1"
  37oldrev="$2"
  38newrev="$3"
  39
  40# --- Safety check
  41if [ -z "$GIT_DIR" ]; then
  42        echo "Don't run this script from the command line." >&2
  43        echo " (if you want, you could supply GIT_DIR then run" >&2
  44        echo "  $0 <ref> <oldrev> <newrev>)" >&2
  45        exit 1
  46fi
  47
  48if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
  49        echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
  50        exit 1
  51fi
  52
  53# --- Config
  54projectdesc=$(cat $GIT_DIR/description)
  55recipients=$(git-repo-config hooks.mailinglist)
  56announcerecipients=$(git-repo-config hooks.announcelist)
  57allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
  58
  59# --- Check types
  60newrev_type=$(git-cat-file -t "$newrev")
  61
  62case "$refname","$newrev_type" in
  63        refs/tags/*,commit)
  64                # un-annotated tag
  65                refname_type="tag"
  66                short_refname=${refname##refs/tags/}
  67                if [ $allowunannotated != "true" ]; then
  68                        echo "*** The un-annotated tag, $short_refname is not allowed in this repository" >&2
  69                        echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
  70                        exit 1
  71                fi
  72                ;;
  73        refs/tags/*,tag)
  74                # annotated tag
  75                refname_type="annotated tag"
  76                short_refname=${refname##refs/tags/}
  77                # change recipients
  78                if [ -n "$announcerecipients" ]; then
  79                        recipients="$announcerecipients"
  80                fi
  81                ;;
  82        refs/heads/*,commit)
  83                # branch
  84                refname_type="branch"
  85                short_refname=${refname##refs/heads/}
  86                ;;
  87        refs/remotes/*,commit)
  88                # tracking branch
  89                refname_type="tracking branch"
  90                short_refname=${refname##refs/remotes/}
  91                # Should this even be allowed?
  92                echo "*** Push-update of tracking branch, $refname.  No email generated." >&2
  93                exit 0
  94                ;;
  95        *)
  96                # Anything else (is there anything else?)
  97                echo "*** Update hook: unknown type of update, \"$newrev_type\", to ref $refname" >&2
  98                exit 1
  99                ;;
 100esac
 101
 102# Check if we've got anyone to send to
 103if [ -z "$recipients" ]; then
 104        # If the email isn't sent, then at least give the user some idea of what command
 105        # would generate the email at a later date
 106        echo "*** No recipients found - no email will be sent, but the push will continue" >&2
 107        echo "*** for $0 $1 $2 $3" >&2
 108        exit 0
 109fi
 110
 111# --- Email parameters
 112committer=$(git show --pretty=full -s $newrev | grep "^Commit: " | sed -e "s/^Commit: //")
 113describe=$(git describe $newrev 2>/dev/null)
 114if [ -z "$describe" ]; then
 115        describe=$newrev
 116fi
 117
 118# --- Email (all stdout will be the email)
 119(
 120# Generate header
 121cat <<-EOF
 122From: $committer
 123To: $recipients
 124Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname now at $describe
 125X-Git-Refname: $refname
 126X-Git-Reftype: $refname_type
 127X-Git-Oldrev: $oldrev
 128X-Git-Newrev: $newrev
 129
 130Hello,
 131
 132This is an automated email from the git hooks/update script, it was
 133generated because a ref change was pushed to the repository.
 134
 135Updating $refname_type, $short_refname,
 136EOF
 137
 138case "$refname_type" in
 139        "tracking branch"|branch)
 140                if expr "$oldrev" : '0*$' >/dev/null
 141                then
 142                        # If the old reference is "0000..0000" then this is a new branch
 143                        # and so oldrev is not valid
 144                        echo "  as a new  $refname_type"
 145                    echo "        to  $newrev ($newrev_type)"
 146                        echo ""
 147                        echo $LOGBEGIN
 148                        # This shows all log entries that are not already covered by
 149                        # another ref - i.e. commits that are now accessible from this
 150                        # ref that were previously not accessible
 151                        git-rev-parse --not --all | git-rev-list --stdin --pretty $newref
 152                        echo $LOGEND
 153                else
 154                        # oldrev is valid
 155                        oldrev_type=$(git-cat-file -t "$oldrev")
 156
 157                        # Now the problem is for cases like this:
 158                        #   * --- * --- * --- * (oldrev)
 159                        #          \
 160                        #           * --- * --- * (newrev)
 161                        # i.e. there is no guarantee that newrev is a strict subset
 162                        # of oldrev - (would have required a force, but that's allowed).
 163                        # So, we can't simply say rev-list $oldrev..$newrev.  Instead
 164                        # we find the common base of the two revs and list from there
 165                        baserev=$(git-merge-base $oldrev $newrev)
 166
 167                        # Commit with a parent
 168                        for rev in $(git-rev-list $newrev ^$baserev)
 169                        do
 170                                revtype=$(git-cat-file -t "$rev")
 171                                echo "       via  $rev ($revtype)"
 172                        done
 173                        if [ "$baserev" = "$oldrev" ]; then
 174                                echo "      from  $oldrev ($oldrev_type)"
 175                        else
 176                                echo "  based on  $baserev"
 177                                echo "      from  $oldrev ($oldrev_type)"
 178                                echo ""
 179                                echo "This ref update crossed a branch point; i.e. the old rev is not a strict subset"
 180                                echo "of the new rev.  This occurs, when you --force push a change in a situation"
 181                                echo "like this:"
 182                                echo ""
 183                                echo " * -- * -- B -- O -- O -- O ($oldrev)"
 184                                echo "            \\"
 185                                echo "             N -- N -- N ($newrev)"
 186                                echo ""
 187                                echo "Therefore, we assume that you've already had alert emails for all of the O"
 188                                echo "revisions, and now give you all the revisions in the N branch from the common"
 189                                echo "base, B ($baserev), up to the new revision."
 190                        fi
 191                        echo ""
 192                        echo $LOGBEGIN
 193                        git-rev-parse --not --all |
 194                        git-rev-list --stdin --pretty $newrev ^$baserev
 195                        echo $LOGEND
 196                        echo ""
 197                        echo "Diffstat:"
 198                        git-diff-tree --no-color --stat -M -C --find-copies-harder $newrev ^$baserev
 199                fi
 200                ;;
 201        "annotated tag")
 202                # Should we allow changes to annotated tags?
 203                if expr "$oldrev" : '0*$' >/dev/null
 204                then
 205                        # If the old reference is "0000..0000" then this is a new atag
 206                        # and so oldrev is not valid
 207                        echo "        to  $newrev ($newrev_type)"
 208                else
 209                        echo "        to  $newrev ($newrev_type)"
 210                        echo "      from  $oldrev"
 211                fi
 212
 213                # If this tag succeeds another, then show which tag it replaces
 214                prevtag=$(git describe $newrev^ 2>/dev/null | sed 's/-g.*//')
 215                if [ -n "$prevtag" ]; then
 216                        echo "  replaces  $prevtag"
 217                fi
 218
 219                # Read the tag details
 220                eval $(git cat-file tag $newrev | \
 221                        sed -n '4s/tagger \([^>]*>\)[^0-9]*\([0-9]*\).*/tagger="\1" ts="\2"/p')
 222                tagged=$(date --date="1970-01-01 00:00:00 +0000 $ts seconds" +"$DATEFORMAT")
 223
 224                echo " tagged by  $tagger"
 225                echo "        on  $tagged"
 226
 227                echo ""
 228                echo $LOGBEGIN
 229                echo ""
 230
 231                if [ -n "$prevtag" ]; then
 232                        git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
 233                else
 234                        git rev-list --pretty=short $newrev | git shortlog
 235                fi
 236
 237                echo $LOGEND
 238                echo ""
 239                ;;
 240        *)
 241                # By default, unannotated tags aren't allowed in; if
 242                # they are though, it's debatable whether we would even want an
 243                # email to be generated; however, I don't want to add another config
 244                # option just for that.
 245                #
 246                # Unannotated tags are more about marking a point than releasing
 247                # a version; therefore we don't do the shortlog summary that we
 248                # do for annotated tags above - we simply show that the point has
 249                # been marked, and print the log message for the marked point for
 250                # reference purposes
 251                #
 252                # Note this section also catches any other reference type (although
 253                # there aren't any) and deals with them in the same way.
 254                if expr "$oldrev" : '0*$' >/dev/null
 255                then
 256                        # If the old reference is "0000..0000" then this is a new tag
 257                        # and so oldrev is not valid
 258                        echo "  as a new  $refname_type"
 259                        echo "        to  $newrev ($newrev_type)"
 260                else
 261                        echo "        to  $newrev ($newrev_type)"
 262                        echo "      from  $oldrev"
 263                fi
 264                echo ""
 265                echo $LOGBEGIN
 266                git-show --no-color --root -s $newrev
 267                echo $LOGEND
 268                echo ""
 269                ;;
 270esac
 271
 272# Footer
 273cat <<-EOF
 274
 275hooks/update
 276---
 277Git Source Code Management System
 278$0 $1 \\
 279  $2 \\
 280  $3
 281EOF
 282#) | cat >&2
 283) | /usr/sbin/sendmail -t
 284
 285# --- Finished
 286exit 0