3 # Copyright (c) 2007 Andy Parkins
5 # An example hook script to mail out commit update information. This hook
6 # sends emails listing new revisions to the repository introduced by the
7 # change being reported. The rule is that (for branch updates) each commit
8 # will appear on one email and one email only.
10 # This hook is stored in the contrib/hooks directory. Your distribution
11 # will have put this somewhere standard. You should make this script
12 # executable then link to it in the repository you would like to use it in.
13 # For example, on debian the hook is stored in
14 # /usr/share/doc/git-core/contrib/hooks/post-receive-email:
16 # chmod a+x post-receive-email
17 # cd /path/to/your/repository.git
18 # ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive
20 # This hook script assumes it is enabled on the central repository of a
21 # project, with all users pushing only to it and not between each other. It
22 # will still work if you don't operate in that style, but it would become
23 # possible for the email to be from someone other than the person doing the
28 # hooks.post-receive-email.mailinglist
29 # This is the list that all pushes will go to; leave it blank to not send
30 # emails for every ref update.
31 # hooks.post-receive-email.announcelist
32 # This is the list that all pushes of annotated tags will go to. Leave it
33 # blank to default to the mailinglist field. The announce emails lists
34 # the short log summary of the changes since the last annotated tag.
35 # hooks.post-receive-email.envelopesender
36 # If set then the -f option is passed to sendmail to allow the envelope
37 # sender address to be set
41 # All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
42 # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and
43 # give information for debugging.
46 # ---------------------------- Functions
48 . $(dirname $0)/functions
51 # Top level email generation function. This decides what type of update
52 # this is and calls the appropriate body-generation routine after outputting
55 # Note this function doesn't actually generate any email output, that is
56 # taken care of by the functions it calls:
57 # - generate_email_header
58 # - generate_create_XXXX_email
59 # - generate_update_XXXX_email
60 # - generate_delete_XXXX_email
65 oldrev=$(git rev-parse $1)
66 newrev=$(git rev-parse $2)
73 # The revision type tells us what type the commit is, combined with
74 # the location of the ref we can decide between
79 case "$refname","$rev_type" in
84 short_refname=${refname##refs/tags/}
88 refname_type="annotated tag"
90 short_refname=${refname##refs/tags/}
92 if [ -n "$announcerecipients" ]; then
93 recipients="$announcerecipients"
100 short_refname=${refname##refs/heads/}
102 refs/remotes/*,commit)
104 refname_type="tracking branch"
105 short_refname=${refname##refs/remotes/}
106 echo >&2 "*** Push-update of tracking branch, $refname"
107 echo >&2 "*** - no email generated."
111 # Anything else (is there anything else?)
112 echo >&2 "*** Unknown type of update to $refname ($rev_type)"
113 echo >&2 "*** - no email generated"
118 # Check if we've got anyone to send to
119 if [ -z "$recipients" ]; then
120 case "$refname_type" in
122 config_name="hooks.post-receive-email.announcelist"
125 config_name="hooks.post-receive-email.mailinglist"
128 echo >&2 "*** $config_name is not set so no email will be sent"
129 echo >&2 "*** for $refname update $oldrev->$newrev"
133 generate_email_header
134 generate_${change_type}_${function}_email
137 generate_email_header()
139 # --- Email (all stdout will be the email)
142 From: ${USER}@payflex.com
144 Subject: ${emailprefix} $short_refname $refname_type ${change_type}d. $describe
145 X-Git-Refname: $refname
146 X-Git-Reftype: $refname_type
147 X-Git-Oldrev: $oldrev
148 X-Git-Newrev: $newrev
150 The $refname_type, $short_refname has been ${change_type}d
155 # --------------- Branches
158 # Called for the creation of a branch
160 generate_create_branch_email()
162 # This is a new branch and so oldrev is not valid
163 git rev-list --pretty=format:" at %h %s" --no-walk "$newrev" | grep -vP "^commit"
169 echo "$new_commits" | git rev-list --reverse --stdin | while read commit ; do
171 git rev-list --no-walk --pretty "$commit"
172 git diff-tree --cc "$commit"
177 oldest_new=$(echo "$new_commits" | git rev-list --stdin | tail -n 1)
178 if [ "$oldest_new" != "" ] ; then
180 echo "Summary of changes:"
181 git diff-tree --stat $oldest_new^..$newrev
186 # Called for the change of a pre-existing branch
188 generate_update_branch_email()
190 # List all of the revisions that were removed by this update (hopefully empty)
191 git rev-list --first-parent --pretty=format:" discards %h %s" $newrev..$oldrev | grep -vP "^commit"
193 # List all of the revisions that were added by this update
194 git rev-list --first-parent --pretty=format:" via %h %s" $oldrev..$newrev | grep -vP "^commit"
196 removed=$(git rev-list $newrev..$oldrev)
197 if [ "$removed" == "" ] ; then
198 git rev-list --no-walk --pretty=format:" from %h %s" $oldrev | grep -vP "^commit"
200 # Must be rewind, could be rewind+addition
203 # Find the common ancestor of the old and new revisions and compare it with newrev
204 baserev=$(git merge-base $oldrev $newrev)
206 if [ "$baserev" = "$newrev" ]; then
207 echo "This update discarded existing revisions and left the branch pointing at"
208 echo "a previous point in the repository history."
210 echo " * -- * -- N ($newrev)"
212 echo " O -- O -- O ($oldrev)"
214 echo "The removed revisions are not necessarilly gone - if another reference"
215 echo "still refers to them they will stay in the repository."
218 echo "This update added new revisions after undoing existing revisions. That is"
219 echo "to say, the old revision is not a strict subset of the new revision. This"
220 echo "situation occurs when you --force push a change and generate a repository"
221 echo "containing something like this:"
223 echo " * -- * -- B -- O -- O -- O ($oldrev)"
225 echo " N -- N -- N ($newrev)"
227 echo "When this happens we assume that you've already had alert emails for all"
228 echo "of the O revisions, and so we here report only the revisions in the N"
229 echo "branch from the common base, B."
234 if [ -z "$rewind_only" ]; then
235 echo "Those revisions listed above that are new to this repository have"
236 echo "not appeared on any other notification email; so we list those"
237 echo "revisions in full, below."
243 echo "$new_commits" | git rev-list --reverse --stdin | while read commit ; do
245 git rev-list --no-walk --pretty "$commit"
246 git diff-tree --cc "$commit"
251 # XXX: Need a way of detecting whether git rev-list actually
252 # outputted anything, so that we can issue a "no new
253 # revisions added by this update" message
255 echo "No new revisions were added by this update."
258 # Show the diffstat which is what really happened (new commits/whatever aside)
260 echo "Summary of changes:"
261 git diff-tree --stat --find-copies-harder $oldrev..$newrev
265 # Called for the deletion of a branch
267 generate_delete_branch_email()
272 git show -s --pretty=oneline $oldrev
276 # --------------- Annotated tags
279 # Called for the creation of an annotated tag
281 generate_create_atag_email()
283 echo " at $newrev ($newrev_type)"
288 # Called for the update of an annotated tag (this is probably a rare event
289 # and may not even be allowed)
291 generate_update_atag_email()
293 echo " to $newrev ($newrev_type)"
294 echo " from $oldrev (which is now obsolete)"
299 # Called when an annotated tag is created or changed
301 generate_atag_email()
303 # Use git for-each-ref to pull out the individual fields from the
305 eval $(git for-each-ref --shell --format='
306 tagobject=%(*objectname)
307 tagtype=%(*objecttype)
309 tagged=%(taggerdate)' $refname
312 echo " tagging $tagobject ($tagtype)"
315 # If the tagged object is a commit, then we assume this is a
316 # release, and so we calculate which tag this tag is replacing
317 prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
318 if [ -n "$prevtag" ]; then
319 echo " replaces $prevtag"
323 echo " length $(git cat-file -s $tagobject) bytes"
326 echo " tagged by $tagger"
332 # Show the content of the tag message; this might contain a change
333 # log or release notes so is worth displaying.
334 git cat-file tag $newrev | sed -e '1,/^$/d'
339 # Only commit tags make sense to have rev-list operations
341 if [ -n "$prevtag" ]; then
342 # Show changes since the previous release
343 git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
345 # No previous tag, show all the changes since time
347 git rev-list --pretty=short $newrev | git shortlog
351 # XXX: Is there anything useful we can do for non-commit
360 # Called for the deletion of an annotated tag
362 generate_delete_atag_email()
364 echo " was $oldrev ($oldrev_type)"
367 git show -s --pretty=oneline $oldrev
371 # --------------- General references
374 # Called when any other type of reference is created (most likely a
377 generate_create_ltag_email()
379 echo " at $newrev ($newrev_type)"
384 # Called when any other type of reference is updated (most likely a
387 generate_update_ltag_email()
389 echo " to $newrev ($newrev_type)"
390 echo " from $oldrev ($oldrev_type)"
395 # Called for creation or update of any other type of reference
397 generate_ltag_email()
399 # Unannotated tags are more about marking a point than releasing a
400 # version; therefore we don't do the shortlog summary that we do for
401 # annotated tags above - we simply show that the point has been
402 # marked, and print the log message for the marked point for
405 # Note this section also catches any other reference type (although
406 # there aren't any) and deals with them in the same way.
409 if [ "$newrev_type" = "commit" ]; then
411 git show --no-color --root -s --pretty=medium $newrev
414 # What can we do here? The tag marks an object that is not
415 # a commit, so there is no log for us to display. It's
416 # probably not wise to output git cat-file as it could be a
417 # binary blob. We'll just say how big it is
418 echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long."
423 # Called for the deletion of any other type of reference
425 generate_delete_ltag_email()
427 echo " was $oldrev ($oldrev_type)"
430 git show -s --pretty=oneline $oldrev
436 if [ -n "$envelopesender" ] ; then
437 /usr/sbin/sendmail -t -f "$envelopesender"
439 # /usr/sbin/sendmail -t
440 /home/BIPFS/shaberman/local/bin/msmtp -t
444 # ---------------------------- main()
447 LOGBEGIN="- Log -----------------------------------------------------------------"
448 LOGEND="-----------------------------------------------------------------------"
451 # Set GIT_DIR either from the working directory or the environment variable.
452 GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
453 if [ -z "$GIT_DIR" ]; then
454 echo >&2 "fatal: post-receive: GIT_DIR not set"
458 projectdesc=$(sed -ne '1p' "$GIT_DIR/description")
459 # Shorten the description if it's the default
460 if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null ; then
461 projectdesc="UNNAMED"
464 recipients=$(git config hooks.post-receive-email.mailinglist)
465 announcerecipients=$(git config hooks.post-receive-email.announcelist)
466 envelopesender=$(git config hooks.post-receive-email.envelopesender)
467 emailprefix="[$projectdesc]"
468 debug=$(git config hooks.post-receive-email.debug)
471 # Allow dual mode: run from the command line just like the update hook, or
472 # if no arguments are given then run as a hook script
473 if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
474 # Output to the terminal in command line mode - if someone wanted to
475 # resend an email; they could redirect the output to sendmail
477 PAGER= generate_email $2 $3 $1
479 while read oldrev newrev refname
481 if [ "$debug" == "true" ] ; then
482 generate_email $oldrev $newrev $refname > "${refname//\//.}.out"
484 generate_email $oldrev $newrev $refname | send_mail