stephenh / git-central

scripts and hooks for use with a centralized git repo

git-central / server / post-receive-email
96d67e4b » stephenh 2008-06-19 Original script from git co... 1 #!/bin/sh
2 #
3 # Copyright (c) 2007 Andy Parkins
89afab0b » stephenh 2008-11-11 Prune contrib-specific docs. 4 # Copyright (c) 2008 Stephen Haberman
96d67e4b » stephenh 2008-06-19 Original script from git co... 5 #
89afab0b » stephenh 2008-11-11 Prune contrib-specific docs. 6 # This hook sends emails listing new revisions to the repository introduced by
7 # the change being reported. The rule is that (for branch updates) each commit
96d67e4b » stephenh 2008-06-19 Original script from git co... 8 # will appear on one email and one email only.
9 #
f123e6ac » stephenh 2008-11-12 Filling in docs. 10 # Differences from the contrib script (off the top of my head):
11 #
12 # * Sends combined diff output which is great for viewing merge commits
13 # * Changes order of commit listing to be oldest to newest
14 # * Configurable sendmail path
15 # * Use git describe --tags for the email subject to pick up commitnumbers
96d67e4b » stephenh 2008-06-19 Original script from git co... 16 #
17 # Config
18 # ------
969d385c » stephenh 2008-08-14 Move post-receive-email set... 19 # hooks.post-receive-email.mailinglist
96d67e4b » stephenh 2008-06-19 Original script from git co... 20 # This is the list that all pushes will go to; leave it blank to not send
21 # emails for every ref update.
969d385c » stephenh 2008-08-14 Move post-receive-email set... 22 # hooks.post-receive-email.announcelist
96d67e4b » stephenh 2008-06-19 Original script from git co... 23 # This is the list that all pushes of annotated tags will go to. Leave it
24 # blank to default to the mailinglist field. The announce emails lists
25 # the short log summary of the changes since the last annotated tag.
969d385c » stephenh 2008-08-14 Move post-receive-email set... 26 # hooks.post-receive-email.envelopesender
96d67e4b » stephenh 2008-06-19 Original script from git co... 27 # If set then the -f option is passed to sendmail to allow the envelope
28 # sender address to be set
b3c55f33 » stephenh 2008-11-11 Use a config variable for w... 29 # hooks.post-receive-email.sendmail
30 # The path to sendmail, e.g. /usr/sbin/sendmail or /bin/msmtp
8c543a95 » stephenh 2008-11-11 Document USER_EMAIL. 31 # USER_EMAIL
32 # Environment variable that should be set by your repository-specific
33 # post-receive hook. E.g. export USER_EMAIL=${USER}@example.com
96d67e4b » stephenh 2008-06-19 Original script from git co... 34 #
35 # Notes
36 # -----
37 # All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
38 # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and
39 # give information for debugging.
40 #
41
42 # ---------------------------- Functions
43
a5599ba9 » stephenh 2008-08-20 Handle double posting when ... 44 . $(dirname $0)/functions
45
96d67e4b » stephenh 2008-06-19 Original script from git co... 46 #
47 # Top level email generation function. This decides what type of update
48 # this is and calls the appropriate body-generation routine after outputting
49 # the common header
50 #
51 # Note this function doesn't actually generate any email output, that is
52 # taken care of by the functions it calls:
53 # - generate_email_header
54 # - generate_create_XXXX_email
55 # - generate_update_XXXX_email
56 # - generate_delete_XXXX_email
57 #
58 generate_email()
59 {
60 # --- Arguments
61 oldrev=$(git rev-parse $1)
62 newrev=$(git rev-parse $2)
63 refname="$3"
64
41659148 » stephenh 2008-08-21 Refactoring. 65 set_change_type
66 set_rev_types
9c43e655 » stephenh 2008-11-11 Use tags in the email subje... 67 set_describe_tags
96d67e4b » stephenh 2008-06-19 Original script from git co... 68
69 # The revision type tells us what type the commit is, combined with
70 # the location of the ref we can decide between
71 # - working branch
72 # - tracking branch
73 # - unannoted tag
74 # - annotated tag
75 case "$refname","$rev_type" in
76 refs/tags/*,commit)
77 # un-annotated tag
78 refname_type="tag"
41659148 » stephenh 2008-08-21 Refactoring. 79 function="ltag"
96d67e4b » stephenh 2008-06-19 Original script from git co... 80 short_refname=${refname##refs/tags/}
81 ;;
82 refs/tags/*,tag)
83 # annotated tag
84 refname_type="annotated tag"
41659148 » stephenh 2008-08-21 Refactoring. 85 function="atag"
96d67e4b » stephenh 2008-06-19 Original script from git co... 86 short_refname=${refname##refs/tags/}
87 # change recipients
88 if [ -n "$announcerecipients" ]; then
89 recipients="$announcerecipients"
90 fi
91 ;;
92 refs/heads/*,commit)
93 # branch
94 refname_type="branch"
41659148 » stephenh 2008-08-21 Refactoring. 95 function="branch"
96d67e4b » stephenh 2008-06-19 Original script from git co... 96 short_refname=${refname##refs/heads/}
97 ;;
98 refs/remotes/*,commit)
99 # tracking branch
100 refname_type="tracking branch"
101 short_refname=${refname##refs/remotes/}
102 echo >&2 "*** Push-update of tracking branch, $refname"
103 echo >&2 "*** - no email generated."
104 exit 0
105 ;;
106 *)
107 # Anything else (is there anything else?)
108 echo >&2 "*** Unknown type of update to $refname ($rev_type)"
109 echo >&2 "*** - no email generated"
110 exit 1
111 ;;
112 esac
113
114 # Check if we've got anyone to send to
115 if [ -z "$recipients" ]; then
116 case "$refname_type" in
117 "annotated tag")
969d385c » stephenh 2008-08-14 Move post-receive-email set... 118 config_name="hooks.post-receive-email.announcelist"
96d67e4b » stephenh 2008-06-19 Original script from git co... 119 ;;
120 *)
969d385c » stephenh 2008-08-14 Move post-receive-email set... 121 config_name="hooks.post-receive-email.mailinglist"
96d67e4b » stephenh 2008-06-19 Original script from git co... 122 ;;
123 esac
124 echo >&2 "*** $config_name is not set so no email will be sent"
125 echo >&2 "*** for $refname update $oldrev->$newrev"
126 exit 0
127 fi
128
129 generate_email_header
41659148 » stephenh 2008-08-21 Refactoring. 130 generate_${change_type}_${function}_email
96d67e4b » stephenh 2008-06-19 Original script from git co... 131 }
132
133 generate_email_header()
134 {
135 # --- Email (all stdout will be the email)
136 # Generate header
137 cat <<-EOF
fb218703 » stephenh 2008-10-21 Rely on USER_EMAIL instead ... 138 From: $USER_EMAIL
96d67e4b » stephenh 2008-06-19 Original script from git co... 139 To: $recipients
9c43e655 » stephenh 2008-11-11 Use tags in the email subje... 140 Subject: ${emailprefix} $short_refname $refname_type ${change_type}d. $describe_tags
96d67e4b » stephenh 2008-06-19 Original script from git co... 141 X-Git-Refname: $refname
142 X-Git-Reftype: $refname_type
143 X-Git-Oldrev: $oldrev
144 X-Git-Newrev: $newrev
145
146 The $refname_type, $short_refname has been ${change_type}d
147 EOF
148 }
149
150
151 # --------------- Branches
152
153 #
154 # Called for the creation of a branch
155 #
156 generate_create_branch_email()
157 {
158 # This is a new branch and so oldrev is not valid
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 159 git rev-list --pretty=format:" at %h %s" --no-walk "$newrev" | grep -vP "^commit"
a5599ba9 » stephenh 2008-08-20 Handle double posting when ... 160
161 set_new_commits
9f8abe6c » stephenh 2008-06-19 Our changes to the email sc... 162
163 echo ""
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 164 echo $LOGBEGIN
165 echo "$new_commits" | git rev-list --reverse --stdin | while read commit ; do
166 echo ""
167 git rev-list --no-walk --pretty "$commit"
168 git diff-tree --cc "$commit"
169 echo ""
170 echo $LOGEND
171 done
a5599ba9 » stephenh 2008-08-20 Handle double posting when ... 172
173 oldest_new=$(echo "$new_commits" | git rev-list --stdin | tail -n 1)
174 if [ "$oldest_new" != "" ] ; then
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 175 echo ""
176 echo "Summary of changes:"
177 git diff-tree --stat $oldest_new^..$newrev
a5599ba9 » stephenh 2008-08-20 Handle double posting when ... 178 fi
96d67e4b » stephenh 2008-06-19 Original script from git co... 179 }
180
181 #
182 # Called for the change of a pre-existing branch
183 #
184 generate_update_branch_email()
185 {
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 186 # List all of the revisions that were removed by this update (hopefully empty)
187 git rev-list --first-parent --pretty=format:" discards %h %s" $newrev..$oldrev | grep -vP "^commit"
96d67e4b » stephenh 2008-06-19 Original script from git co... 188
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 189 # List all of the revisions that were added by this update
190 git rev-list --first-parent --pretty=format:" via %h %s" $oldrev..$newrev | grep -vP "^commit"
96d67e4b » stephenh 2008-06-19 Original script from git co... 191
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 192 removed=$(git rev-list $newrev..$oldrev)
193 if [ "$removed" == "" ] ; then
194 git rev-list --no-walk --pretty=format:" from %h %s" $oldrev | grep -vP "^commit"
96d67e4b » stephenh 2008-06-19 Original script from git co... 195 else
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 196 # Must be rewind, could be rewind+addition
96d67e4b » stephenh 2008-06-19 Original script from git co... 197 echo ""
198
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 199 # Find the common ancestor of the old and new revisions and compare it with newrev
96d67e4b » stephenh 2008-06-19 Original script from git co... 200 baserev=$(git merge-base $oldrev $newrev)
201 rewind_only=""
202 if [ "$baserev" = "$newrev" ]; then
203 echo "This update discarded existing revisions and left the branch pointing at"
204 echo "a previous point in the repository history."
205 echo ""
206 echo " * -- * -- N ($newrev)"
207 echo " \\"
208 echo " O -- O -- O ($oldrev)"
209 echo ""
210 echo "The removed revisions are not necessarilly gone - if another reference"
211 echo "still refers to them they will stay in the repository."
212 rewind_only=1
213 else
214 echo "This update added new revisions after undoing existing revisions. That is"
215 echo "to say, the old revision is not a strict subset of the new revision. This"
216 echo "situation occurs when you --force push a change and generate a repository"
217 echo "containing something like this:"
218 echo ""
219 echo " * -- * -- B -- O -- O -- O ($oldrev)"
220 echo " \\"
221 echo " N -- N -- N ($newrev)"
222 echo ""
223 echo "When this happens we assume that you've already had alert emails for all"
224 echo "of the O revisions, and so we here report only the revisions in the N"
225 echo "branch from the common base, B."
226 fi
227 fi
228
229 echo ""
230 if [ -z "$rewind_only" ]; then
231 echo "Those revisions listed above that are new to this repository have"
232 echo "not appeared on any other notification email; so we list those"
233 echo "revisions in full, below."
234
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 235 set_new_commits
236
96d67e4b » stephenh 2008-06-19 Original script from git co... 237 echo ""
238 echo $LOGBEGIN
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 239 echo "$new_commits" | git rev-list --reverse --stdin | while read commit ; do
240 echo ""
241 git rev-list --no-walk --pretty "$commit"
242 git diff-tree --cc "$commit"
243 echo ""
244 echo $LOGEND
245 done
96d67e4b » stephenh 2008-06-19 Original script from git co... 246
247 # XXX: Need a way of detecting whether git rev-list actually
248 # outputted anything, so that we can issue a "no new
249 # revisions added by this update" message
250 else
251 echo "No new revisions were added by this update."
252 fi
253
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 254 # Show the diffstat which is what really happened (new commits/whatever aside)
96d67e4b » stephenh 2008-06-19 Original script from git co... 255 echo ""
256 echo "Summary of changes:"
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 257 git diff-tree --stat --find-copies-harder $oldrev..$newrev
96d67e4b » stephenh 2008-06-19 Original script from git co... 258 }
259
260 #
261 # Called for the deletion of a branch
262 #
263 generate_delete_branch_email()
264 {
265 echo " was $oldrev"
266 echo ""
267 echo $LOGEND
268 git show -s --pretty=oneline $oldrev
269 echo $LOGEND
270 }
271
272 # --------------- Annotated tags
273
274 #
275 # Called for the creation of an annotated tag
276 #
277 generate_create_atag_email()
278 {
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 279 echo " at $newrev ($newrev_type)"
96d67e4b » stephenh 2008-06-19 Original script from git co... 280 generate_atag_email
281 }
282
283 #
284 # Called for the update of an annotated tag (this is probably a rare event
285 # and may not even be allowed)
286 #
287 generate_update_atag_email()
288 {
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 289 echo " to $newrev ($newrev_type)"
290 echo " from $oldrev (which is now obsolete)"
96d67e4b » stephenh 2008-06-19 Original script from git co... 291 generate_atag_email
292 }
293
294 #
295 # Called when an annotated tag is created or changed
296 #
297 generate_atag_email()
298 {
299 # Use git for-each-ref to pull out the individual fields from the
300 # tag
301 eval $(git for-each-ref --shell --format='
302 tagobject=%(*objectname)
303 tagtype=%(*objecttype)
304 tagger=%(taggername)
305 tagged=%(taggerdate)' $refname
306 )
307
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 308 echo " tagging $tagobject ($tagtype)"
96d67e4b » stephenh 2008-06-19 Original script from git co... 309 case "$tagtype" in
310 commit)
311 # If the tagged object is a commit, then we assume this is a
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 312 # release, and so we calculate which tag this tag is replacing
96d67e4b » stephenh 2008-06-19 Original script from git co... 313 prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
314 if [ -n "$prevtag" ]; then
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 315 echo " replaces $prevtag"
96d67e4b » stephenh 2008-06-19 Original script from git co... 316 fi
317 ;;
318 *)
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 319 echo " length $(git cat-file -s $tagobject) bytes"
96d67e4b » stephenh 2008-06-19 Original script from git co... 320 ;;
321 esac
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 322 echo " tagged by $tagger"
323 echo " on $tagged"
96d67e4b » stephenh 2008-06-19 Original script from git co... 324
325 echo ""
326 echo $LOGBEGIN
327
328 # Show the content of the tag message; this might contain a change
329 # log or release notes so is worth displaying.
330 git cat-file tag $newrev | sed -e '1,/^$/d'
331
332 echo ""
333 case "$tagtype" in
334 commit)
335 # Only commit tags make sense to have rev-list operations
336 # performed on them
337 if [ -n "$prevtag" ]; then
338 # Show changes since the previous release
339 git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
340 else
341 # No previous tag, show all the changes since time
342 # began
343 git rev-list --pretty=short $newrev | git shortlog
344 fi
345 ;;
346 *)
347 # XXX: Is there anything useful we can do for non-commit
348 # objects?
349 ;;
350 esac
351
352 echo $LOGEND
353 }
354
355 #
356 # Called for the deletion of an annotated tag
357 #
358 generate_delete_atag_email()
359 {
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 360 echo " was $oldrev ($oldrev_type)"
96d67e4b » stephenh 2008-06-19 Original script from git co... 361 echo ""
362 echo $LOGEND
363 git show -s --pretty=oneline $oldrev
364 echo $LOGEND
365 }
366
367 # --------------- General references
368
369 #
370 # Called when any other type of reference is created (most likely a
371 # non-annotated tag)
372 #
41659148 » stephenh 2008-08-21 Refactoring. 373 generate_create_ltag_email()
96d67e4b » stephenh 2008-06-19 Original script from git co... 374 {
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 375 echo " at $newrev ($newrev_type)"
41659148 » stephenh 2008-08-21 Refactoring. 376 generate_ltag_email
96d67e4b » stephenh 2008-06-19 Original script from git co... 377 }
378
379 #
380 # Called when any other type of reference is updated (most likely a
381 # non-annotated tag)
382 #
41659148 » stephenh 2008-08-21 Refactoring. 383 generate_update_ltag_email()
96d67e4b » stephenh 2008-06-19 Original script from git co... 384 {
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 385 echo " to $newrev ($newrev_type)"
386 echo " from $oldrev ($oldrev_type)"
41659148 » stephenh 2008-08-21 Refactoring. 387 generate_ltag_email
96d67e4b » stephenh 2008-06-19 Original script from git co... 388 }
389
390 #
391 # Called for creation or update of any other type of reference
392 #
41659148 » stephenh 2008-08-21 Refactoring. 393 generate_ltag_email()
96d67e4b » stephenh 2008-06-19 Original script from git co... 394 {
395 # Unannotated tags are more about marking a point than releasing a
396 # version; therefore we don't do the shortlog summary that we do for
397 # annotated tags above - we simply show that the point has been
398 # marked, and print the log message for the marked point for
399 # reference purposes
400 #
401 # Note this section also catches any other reference type (although
402 # there aren't any) and deals with them in the same way.
403
404 echo ""
405 if [ "$newrev_type" = "commit" ]; then
406 echo $LOGBEGIN
407 git show --no-color --root -s --pretty=medium $newrev
408 echo $LOGEND
409 else
410 # What can we do here? The tag marks an object that is not
411 # a commit, so there is no log for us to display. It's
412 # probably not wise to output git cat-file as it could be a
413 # binary blob. We'll just say how big it is
414 echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long."
415 fi
416 }
417
418 #
419 # Called for the deletion of any other type of reference
420 #
41659148 » stephenh 2008-08-21 Refactoring. 421 generate_delete_ltag_email()
96d67e4b » stephenh 2008-06-19 Original script from git co... 422 {
c3124d06 » stephenh 2008-08-22 Refactor to do diff per com... 423 echo " was $oldrev ($oldrev_type)"
96d67e4b » stephenh 2008-06-19 Original script from git co... 424 echo ""
425 echo $LOGEND
426 git show -s --pretty=oneline $oldrev
427 echo $LOGEND
428 }
429
430 send_mail()
431 {
e6f64dde » stephenh 2008-06-20 First test for emails. 432 if [ -n "$envelopesender" ] ; then
b3c55f33 » stephenh 2008-11-11 Use a config variable for w... 433 $sendmail -t -f "$envelopesender"
96d67e4b » stephenh 2008-06-19 Original script from git co... 434 else
b3c55f33 » stephenh 2008-11-11 Use a config variable for w... 435 $sendmail -t
96d67e4b » stephenh 2008-06-19 Original script from git co... 436 fi
437 }
438
439 # ---------------------------- main()
440
441 # --- Constants
442 LOGBEGIN="- Log -----------------------------------------------------------------"
443 LOGEND="-----------------------------------------------------------------------"
444
445 # --- Config
a88281f0 » stephenh 2008-08-21 Remove newlines. 446 # Set GIT_DIR either from the working directory or the environment variable.
96d67e4b » stephenh 2008-06-19 Original script from git co... 447 GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
448 if [ -z "$GIT_DIR" ]; then
449 echo >&2 "fatal: post-receive: GIT_DIR not set"
450 exit 1
451 fi
452
453 projectdesc=$(sed -ne '1p' "$GIT_DIR/description")
a88281f0 » stephenh 2008-08-21 Remove newlines. 454 # Shorten the description if it's the default
455 if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null ; then
456 projectdesc="UNNAMED"
96d67e4b » stephenh 2008-06-19 Original script from git co... 457 fi
458
969d385c » stephenh 2008-08-14 Move post-receive-email set... 459 recipients=$(git config hooks.post-receive-email.mailinglist)
460 announcerecipients=$(git config hooks.post-receive-email.announcelist)
461 envelopesender=$(git config hooks.post-receive-email.envelopesender)
6ab50b9d » stephenh 2008-06-24 Actually commit the email t... 462 emailprefix="[$projectdesc]"
969d385c » stephenh 2008-08-14 Move post-receive-email set... 463 debug=$(git config hooks.post-receive-email.debug)
b3c55f33 » stephenh 2008-11-11 Use a config variable for w... 464 sendmail=$(git config hooks.post-receive-email.sendmail)
96d67e4b » stephenh 2008-06-19 Original script from git co... 465
466 # --- Main loop
467 # Allow dual mode: run from the command line just like the update hook, or
468 # if no arguments are given then run as a hook script
469 if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
470 # Output to the terminal in command line mode - if someone wanted to
471 # resend an email; they could redirect the output to sendmail
472 # themselves
473 PAGER= generate_email $2 $3 $1
474 else
475 while read oldrev newrev refname
476 do
e6f64dde » stephenh 2008-06-20 First test for emails. 477 if [ "$debug" == "true" ] ; then
478 generate_email $oldrev $newrev $refname > "${refname//\//.}.out"
479 else
480 generate_email $oldrev $newrev $refname | send_mail
481 fi
96d67e4b » stephenh 2008-06-19 Original script from git co... 482 done
483 fi
89afab0b » stephenh 2008-11-11 Prune contrib-specific docs. 484