Merge branch 'nd/daemon-informative-errors-typofix' into maint
[git] / contrib / hooks / post-receive-email
1 #!/bin/sh
2 #
3 # Copyright (c) 2007 Andy Parkins
4 #
5 # An example hook script to mail out commit update information.
6 #
7 # NOTE: This script is no longer under active development.  There
8 # is another script, git-multimail, which is more capable and
9 # configurable and is largely backwards-compatible with this script;
10 # please see "contrib/hooks/multimail/".  For instructions on how to
11 # migrate from post-receive-email to git-multimail, please see
12 # "README.migrate-from-post-receive-email" in that directory.
13 #
14 # This hook sends emails listing new revisions to the repository
15 # introduced by the change being reported.  The rule is that (for
16 # branch updates) each commit will appear on one email and one email
17 # only.
18 #
19 # This hook is stored in the contrib/hooks directory.  Your distribution
20 # will have put this somewhere standard.  You should make this script
21 # executable then link to it in the repository you would like to use it in.
22 # For example, on debian the hook is stored in
23 # /usr/share/git-core/contrib/hooks/post-receive-email:
24 #
25 #  chmod a+x post-receive-email
26 #  cd /path/to/your/repository.git
27 #  ln -sf /usr/share/git-core/contrib/hooks/post-receive-email hooks/post-receive
28 #
29 # This hook script assumes it is enabled on the central repository of a
30 # project, with all users pushing only to it and not between each other.  It
31 # will still work if you don't operate in that style, but it would become
32 # possible for the email to be from someone other than the person doing the
33 # push.
34 #
35 # To help with debugging and use on pre-v1.5.1 git servers, this script will
36 # also obey the interface of hooks/update, taking its arguments on the
37 # command line.  Unfortunately, hooks/update is called once for each ref.
38 # To avoid firing one email per ref, this script just prints its output to
39 # the screen when used in this mode.  The output can then be redirected if
40 # wanted.
41 #
42 # Config
43 # ------
44 # hooks.mailinglist
45 #   This is the list that all pushes will go to; leave it blank to not send
46 #   emails for every ref update.
47 # hooks.announcelist
48 #   This is the list that all pushes of annotated tags will go to.  Leave it
49 #   blank to default to the mailinglist field.  The announce emails lists
50 #   the short log summary of the changes since the last annotated tag.
51 # hooks.envelopesender
52 #   If set then the -f option is passed to sendmail to allow the envelope
53 #   sender address to be set
54 # hooks.emailprefix
55 #   All emails have their subjects prefixed with this prefix, or "[SCM]"
56 #   if emailprefix is unset, to aid filtering
57 # hooks.showrev
58 #   The shell command used to format each revision in the email, with
59 #   "%s" replaced with the commit id.  Defaults to "git rev-list -1
60 #   --pretty %s", displaying the commit id, author, date and log
61 #   message.  To list full patches separated by a blank line, you
62 #   could set this to "git show -C %s; echo".
63 #   To list a gitweb/cgit URL *and* a full patch for each change set, use this:
64 #     "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo"
65 #   Be careful if "..." contains things that will be expanded by shell "eval"
66 #   or printf.
67 # hooks.emailmaxlines
68 #   The maximum number of lines that should be included in the generated
69 #   email body. If not specified, there is no limit.
70 #   Lines beyond the limit are suppressed and counted, and a final
71 #   line is added indicating the number of suppressed lines.
72 # hooks.diffopts
73 #   Alternate options for the git diff-tree invocation that shows changes.
74 #   Default is "--stat --summary --find-copies-harder". Add -p to those
75 #   options to include a unified diff of changes in addition to the usual
76 #   summary output.
77 #
78 # Notes
79 # -----
80 # All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
81 # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and
82 # give information for debugging.
83 #
84
85 # ---------------------------- Functions
86
87 #
88 # Function to prepare for email generation. This decides what type
89 # of update this is and whether an email should even be generated.
90 #
91 prep_for_email()
92 {
93         # --- Arguments
94         oldrev=$(git rev-parse $1)
95         newrev=$(git rev-parse $2)
96         refname="$3"
97
98         # --- Interpret
99         # 0000->1234 (create)
100         # 1234->2345 (update)
101         # 2345->0000 (delete)
102         if expr "$oldrev" : '0*$' >/dev/null
103         then
104                 change_type="create"
105         else
106                 if expr "$newrev" : '0*$' >/dev/null
107                 then
108                         change_type="delete"
109                 else
110                         change_type="update"
111                 fi
112         fi
113
114         # --- Get the revision types
115         newrev_type=$(git cat-file -t $newrev 2> /dev/null)
116         oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null)
117         case "$change_type" in
118         create|update)
119                 rev="$newrev"
120                 rev_type="$newrev_type"
121                 ;;
122         delete)
123                 rev="$oldrev"
124                 rev_type="$oldrev_type"
125                 ;;
126         esac
127
128         # The revision type tells us what type the commit is, combined with
129         # the location of the ref we can decide between
130         #  - working branch
131         #  - tracking branch
132         #  - unannoted tag
133         #  - annotated tag
134         case "$refname","$rev_type" in
135                 refs/tags/*,commit)
136                         # un-annotated tag
137                         refname_type="tag"
138                         short_refname=${refname##refs/tags/}
139                         ;;
140                 refs/tags/*,tag)
141                         # annotated tag
142                         refname_type="annotated tag"
143                         short_refname=${refname##refs/tags/}
144                         # change recipients
145                         if [ -n "$announcerecipients" ]; then
146                                 recipients="$announcerecipients"
147                         fi
148                         ;;
149                 refs/heads/*,commit)
150                         # branch
151                         refname_type="branch"
152                         short_refname=${refname##refs/heads/}
153                         ;;
154                 refs/remotes/*,commit)
155                         # tracking branch
156                         refname_type="tracking branch"
157                         short_refname=${refname##refs/remotes/}
158                         echo >&2 "*** Push-update of tracking branch, $refname"
159                         echo >&2 "***  - no email generated."
160                         return 1
161                         ;;
162                 *)
163                         # Anything else (is there anything else?)
164                         echo >&2 "*** Unknown type of update to $refname ($rev_type)"
165                         echo >&2 "***  - no email generated"
166                         return 1
167                         ;;
168         esac
169
170         # Check if we've got anyone to send to
171         if [ -z "$recipients" ]; then
172                 case "$refname_type" in
173                         "annotated tag")
174                                 config_name="hooks.announcelist"
175                                 ;;
176                         *)
177                                 config_name="hooks.mailinglist"
178                                 ;;
179                 esac
180                 echo >&2 "*** $config_name is not set so no email will be sent"
181                 echo >&2 "*** for $refname update $oldrev->$newrev"
182                 return 1
183         fi
184
185         return 0
186 }
187
188 #
189 # Top level email generation function.  This calls the appropriate
190 # body-generation routine after outputting the common header.
191 #
192 # Note this function doesn't actually generate any email output, that is
193 # taken care of by the functions it calls:
194 #  - generate_email_header
195 #  - generate_create_XXXX_email
196 #  - generate_update_XXXX_email
197 #  - generate_delete_XXXX_email
198 #  - generate_email_footer
199 #
200 # Note also that this function cannot 'exit' from the script; when this
201 # function is running (in hook script mode), the send_mail() function
202 # is already executing in another process, connected via a pipe, and
203 # if this function exits without, whatever has been generated to that
204 # point will be sent as an email... even if nothing has been generated.
205 #
206 generate_email()
207 {
208         # Email parameters
209         # The email subject will contain the best description of the ref
210         # that we can build from the parameters
211         describe=$(git describe $rev 2>/dev/null)
212         if [ -z "$describe" ]; then
213                 describe=$rev
214         fi
215
216         generate_email_header
217
218         # Call the correct body generation function
219         fn_name=general
220         case "$refname_type" in
221         "tracking branch"|branch)
222                 fn_name=branch
223                 ;;
224         "annotated tag")
225                 fn_name=atag
226                 ;;
227         esac
228
229         if [ -z "$maxlines" ]; then
230                 generate_${change_type}_${fn_name}_email
231         else
232                 generate_${change_type}_${fn_name}_email | limit_lines $maxlines
233         fi
234
235         generate_email_footer
236 }
237
238 generate_email_header()
239 {
240         # --- Email (all stdout will be the email)
241         # Generate header
242         cat <<-EOF
243         To: $recipients
244         Subject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe
245         MIME-Version: 1.0
246         Content-Type: text/plain; charset=utf-8
247         Content-Transfer-Encoding: 8bit
248         X-Git-Refname: $refname
249         X-Git-Reftype: $refname_type
250         X-Git-Oldrev: $oldrev
251         X-Git-Newrev: $newrev
252         Auto-Submitted: auto-generated
253
254         This is an automated email from the git hooks/post-receive script. It was
255         generated because a ref change was pushed to the repository containing
256         the project "$projectdesc".
257
258         The $refname_type, $short_refname has been ${change_type}d
259         EOF
260 }
261
262 generate_email_footer()
263 {
264         SPACE=" "
265         cat <<-EOF
266
267
268         hooks/post-receive
269         --${SPACE}
270         $projectdesc
271         EOF
272 }
273
274 # --------------- Branches
275
276 #
277 # Called for the creation of a branch
278 #
279 generate_create_branch_email()
280 {
281         # This is a new branch and so oldrev is not valid
282         echo "        at  $newrev ($newrev_type)"
283         echo ""
284
285         echo $LOGBEGIN
286         show_new_revisions
287         echo $LOGEND
288 }
289
290 #
291 # Called for the change of a pre-existing branch
292 #
293 generate_update_branch_email()
294 {
295         # Consider this:
296         #   1 --- 2 --- O --- X --- 3 --- 4 --- N
297         #
298         # O is $oldrev for $refname
299         # N is $newrev for $refname
300         # X is a revision pointed to by some other ref, for which we may
301         #   assume that an email has already been generated.
302         # In this case we want to issue an email containing only revisions
303         # 3, 4, and N.  Given (almost) by
304         #
305         #  git rev-list N ^O --not --all
306         #
307         # The reason for the "almost", is that the "--not --all" will take
308         # precedence over the "N", and effectively will translate to
309         #
310         #  git rev-list N ^O ^X ^N
311         #
312         # So, we need to build up the list more carefully.  git rev-parse
313         # will generate a list of revs that may be fed into git rev-list.
314         # We can get it to make the "--not --all" part and then filter out
315         # the "^N" with:
316         #
317         #  git rev-parse --not --all | grep -v N
318         #
319         # Then, using the --stdin switch to git rev-list we have effectively
320         # manufactured
321         #
322         #  git rev-list N ^O ^X
323         #
324         # This leaves a problem when someone else updates the repository
325         # while this script is running.  Their new value of the ref we're
326         # working on would be included in the "--not --all" output; and as
327         # our $newrev would be an ancestor of that commit, it would exclude
328         # all of our commits.  What we really want is to exclude the current
329         # value of $refname from the --not list, rather than N itself.  So:
330         #
331         #  git rev-parse --not --all | grep -v $(git rev-parse $refname)
332         #
333         # Get's us to something pretty safe (apart from the small time
334         # between refname being read, and git rev-parse running - for that,
335         # I give up)
336         #
337         #
338         # Next problem, consider this:
339         #   * --- B --- * --- O ($oldrev)
340         #          \
341         #           * --- X --- * --- N ($newrev)
342         #
343         # That is to say, there is no guarantee that oldrev is a strict
344         # subset of newrev (it would have required a --force, but that's
345         # allowed).  So, we can't simply say rev-list $oldrev..$newrev.
346         # Instead we find the common base of the two revs and list from
347         # there.
348         #
349         # As above, we need to take into account the presence of X; if
350         # another branch is already in the repository and points at some of
351         # the revisions that we are about to output - we don't want them.
352         # The solution is as before: git rev-parse output filtered.
353         #
354         # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N
355         #
356         # Tags pushed into the repository generate nice shortlog emails that
357         # summarise the commits between them and the previous tag.  However,
358         # those emails don't include the full commit messages that we output
359         # for a branch update.  Therefore we still want to output revisions
360         # that have been output on a tag email.
361         #
362         # Luckily, git rev-parse includes just the tool.  Instead of using
363         # "--all" we use "--branches"; this has the added benefit that
364         # "remotes/" will be ignored as well.
365
366         # List all of the revisions that were removed by this update, in a
367         # fast-forward update, this list will be empty, because rev-list O
368         # ^N is empty.  For a non-fast-forward, O ^N is the list of removed
369         # revisions
370         fast_forward=""
371         rev=""
372         for rev in $(git rev-list $newrev..$oldrev)
373         do
374                 revtype=$(git cat-file -t "$rev")
375                 echo "  discards  $rev ($revtype)"
376         done
377         if [ -z "$rev" ]; then
378                 fast_forward=1
379         fi
380
381         # List all the revisions from baserev to newrev in a kind of
382         # "table-of-contents"; note this list can include revisions that
383         # have already had notification emails and is present to show the
384         # full detail of the change from rolling back the old revision to
385         # the base revision and then forward to the new revision
386         for rev in $(git rev-list $oldrev..$newrev)
387         do
388                 revtype=$(git cat-file -t "$rev")
389                 echo "       via  $rev ($revtype)"
390         done
391
392         if [ "$fast_forward" ]; then
393                 echo "      from  $oldrev ($oldrev_type)"
394         else
395                 #  1. Existing revisions were removed.  In this case newrev
396                 #     is a subset of oldrev - this is the reverse of a
397                 #     fast-forward, a rewind
398                 #  2. New revisions were added on top of an old revision,
399                 #     this is a rewind and addition.
400
401                 # (1) certainly happened, (2) possibly.  When (2) hasn't
402                 # happened, we set a flag to indicate that no log printout
403                 # is required.
404
405                 echo ""
406
407                 # Find the common ancestor of the old and new revisions and
408                 # compare it with newrev
409                 baserev=$(git merge-base $oldrev $newrev)
410                 rewind_only=""
411                 if [ "$baserev" = "$newrev" ]; then
412                         echo "This update discarded existing revisions and left the branch pointing at"
413                         echo "a previous point in the repository history."
414                         echo ""
415                         echo " * -- * -- N ($newrev)"
416                         echo "            \\"
417                         echo "             O -- O -- O ($oldrev)"
418                         echo ""
419                         echo "The removed revisions are not necessarily gone - if another reference"
420                         echo "still refers to them they will stay in the repository."
421                         rewind_only=1
422                 else
423                         echo "This update added new revisions after undoing existing revisions.  That is"
424                         echo "to say, the old revision is not a strict subset of the new revision.  This"
425                         echo "situation occurs when you --force push a change and generate a repository"
426                         echo "containing something like this:"
427                         echo ""
428                         echo " * -- * -- B -- O -- O -- O ($oldrev)"
429                         echo "            \\"
430                         echo "             N -- N -- N ($newrev)"
431                         echo ""
432                         echo "When this happens we assume that you've already had alert emails for all"
433                         echo "of the O revisions, and so we here report only the revisions in the N"
434                         echo "branch from the common base, B."
435                 fi
436         fi
437
438         echo ""
439         if [ -z "$rewind_only" ]; then
440                 echo "Those revisions listed above that are new to this repository have"
441                 echo "not appeared on any other notification email; so we list those"
442                 echo "revisions in full, below."
443
444                 echo ""
445                 echo $LOGBEGIN
446                 show_new_revisions
447
448                 # XXX: Need a way of detecting whether git rev-list actually
449                 # outputted anything, so that we can issue a "no new
450                 # revisions added by this update" message
451
452                 echo $LOGEND
453         else
454                 echo "No new revisions were added by this update."
455         fi
456
457         # The diffstat is shown from the old revision to the new revision.
458         # This is to show the truth of what happened in this change.
459         # There's no point showing the stat from the base to the new
460         # revision because the base is effectively a random revision at this
461         # point - the user will be interested in what this revision changed
462         # - including the undoing of previous revisions in the case of
463         # non-fast-forward updates.
464         echo ""
465         echo "Summary of changes:"
466         git diff-tree $diffopts $oldrev..$newrev
467 }
468
469 #
470 # Called for the deletion of a branch
471 #
472 generate_delete_branch_email()
473 {
474         echo "       was  $oldrev"
475         echo ""
476         echo $LOGBEGIN
477         git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev
478         echo $LOGEND
479 }
480
481 # --------------- Annotated tags
482
483 #
484 # Called for the creation of an annotated tag
485 #
486 generate_create_atag_email()
487 {
488         echo "        at  $newrev ($newrev_type)"
489
490         generate_atag_email
491 }
492
493 #
494 # Called for the update of an annotated tag (this is probably a rare event
495 # and may not even be allowed)
496 #
497 generate_update_atag_email()
498 {
499         echo "        to  $newrev ($newrev_type)"
500         echo "      from  $oldrev (which is now obsolete)"
501
502         generate_atag_email
503 }
504
505 #
506 # Called when an annotated tag is created or changed
507 #
508 generate_atag_email()
509 {
510         # Use git for-each-ref to pull out the individual fields from the
511         # tag
512         eval $(git for-each-ref --shell --format='
513         tagobject=%(*objectname)
514         tagtype=%(*objecttype)
515         tagger=%(taggername)
516         tagged=%(taggerdate)' $refname
517         )
518
519         echo "   tagging  $tagobject ($tagtype)"
520         case "$tagtype" in
521         commit)
522
523                 # If the tagged object is a commit, then we assume this is a
524                 # release, and so we calculate which tag this tag is
525                 # replacing
526                 prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
527
528                 if [ -n "$prevtag" ]; then
529                         echo "  replaces  $prevtag"
530                 fi
531                 ;;
532         *)
533                 echo "    length  $(git cat-file -s $tagobject) bytes"
534                 ;;
535         esac
536         echo " tagged by  $tagger"
537         echo "        on  $tagged"
538
539         echo ""
540         echo $LOGBEGIN
541
542         # Show the content of the tag message; this might contain a change
543         # log or release notes so is worth displaying.
544         git cat-file tag $newrev | sed -e '1,/^$/d'
545
546         echo ""
547         case "$tagtype" in
548         commit)
549                 # Only commit tags make sense to have rev-list operations
550                 # performed on them
551                 if [ -n "$prevtag" ]; then
552                         # Show changes since the previous release
553                         git shortlog "$prevtag..$newrev"
554                 else
555                         # No previous tag, show all the changes since time
556                         # began
557                         git shortlog $newrev
558                 fi
559                 ;;
560         *)
561                 # XXX: Is there anything useful we can do for non-commit
562                 # objects?
563                 ;;
564         esac
565
566         echo $LOGEND
567 }
568
569 #
570 # Called for the deletion of an annotated tag
571 #
572 generate_delete_atag_email()
573 {
574         echo "       was  $oldrev"
575         echo ""
576         echo $LOGBEGIN
577         git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev
578         echo $LOGEND
579 }
580
581 # --------------- General references
582
583 #
584 # Called when any other type of reference is created (most likely a
585 # non-annotated tag)
586 #
587 generate_create_general_email()
588 {
589         echo "        at  $newrev ($newrev_type)"
590
591         generate_general_email
592 }
593
594 #
595 # Called when any other type of reference is updated (most likely a
596 # non-annotated tag)
597 #
598 generate_update_general_email()
599 {
600         echo "        to  $newrev ($newrev_type)"
601         echo "      from  $oldrev"
602
603         generate_general_email
604 }
605
606 #
607 # Called for creation or update of any other type of reference
608 #
609 generate_general_email()
610 {
611         # Unannotated tags are more about marking a point than releasing a
612         # version; therefore we don't do the shortlog summary that we do for
613         # annotated tags above - we simply show that the point has been
614         # marked, and print the log message for the marked point for
615         # reference purposes
616         #
617         # Note this section also catches any other reference type (although
618         # there aren't any) and deals with them in the same way.
619
620         echo ""
621         if [ "$newrev_type" = "commit" ]; then
622                 echo $LOGBEGIN
623                 git diff-tree -s --always --encoding=UTF-8 --pretty=medium $newrev
624                 echo $LOGEND
625         else
626                 # What can we do here?  The tag marks an object that is not
627                 # a commit, so there is no log for us to display.  It's
628                 # probably not wise to output git cat-file as it could be a
629                 # binary blob.  We'll just say how big it is
630                 echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long."
631         fi
632 }
633
634 #
635 # Called for the deletion of any other type of reference
636 #
637 generate_delete_general_email()
638 {
639         echo "       was  $oldrev"
640         echo ""
641         echo $LOGBEGIN
642         git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev
643         echo $LOGEND
644 }
645
646
647 # --------------- Miscellaneous utilities
648
649 #
650 # Show new revisions as the user would like to see them in the email.
651 #
652 show_new_revisions()
653 {
654         # This shows all log entries that are not already covered by
655         # another ref - i.e. commits that are now accessible from this
656         # ref that were previously not accessible
657         # (see generate_update_branch_email for the explanation of this
658         # command)
659
660         # Revision range passed to rev-list differs for new vs. updated
661         # branches.
662         if [ "$change_type" = create ]
663         then
664                 # Show all revisions exclusive to this (new) branch.
665                 revspec=$newrev
666         else
667                 # Branch update; show revisions not part of $oldrev.
668                 revspec=$oldrev..$newrev
669         fi
670
671         other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ |
672             grep -F -v $refname)
673         git rev-parse --not $other_branches |
674         if [ -z "$custom_showrev" ]
675         then
676                 git rev-list --pretty --stdin $revspec
677         else
678                 git rev-list --stdin $revspec |
679                 while read onerev
680                 do
681                         eval $(printf "$custom_showrev" $onerev)
682                 done
683         fi
684 }
685
686
687 limit_lines()
688 {
689         lines=0
690         skipped=0
691         while IFS="" read -r line; do
692                 lines=$((lines + 1))
693                 if [ $lines -gt $1 ]; then
694                         skipped=$((skipped + 1))
695                 else
696                         printf "%s\n" "$line"
697                 fi
698         done
699         if [ $skipped -ne 0 ]; then
700                 echo "... $skipped lines suppressed ..."
701         fi
702 }
703
704
705 send_mail()
706 {
707         if [ -n "$envelopesender" ]; then
708                 /usr/sbin/sendmail -t -f "$envelopesender"
709         else
710                 /usr/sbin/sendmail -t
711         fi
712 }
713
714 # ---------------------------- main()
715
716 # --- Constants
717 LOGBEGIN="- Log -----------------------------------------------------------------"
718 LOGEND="-----------------------------------------------------------------------"
719
720 # --- Config
721 # Set GIT_DIR either from the working directory, or from the environment
722 # variable.
723 GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
724 if [ -z "$GIT_DIR" ]; then
725         echo >&2 "fatal: post-receive: GIT_DIR not set"
726         exit 1
727 fi
728
729 projectdesc=$(sed -ne '1p' "$GIT_DIR/description" 2>/dev/null)
730 # Check if the description is unchanged from it's default, and shorten it to
731 # a more manageable length if it is
732 if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null
733 then
734         projectdesc="UNNAMED PROJECT"
735 fi
736
737 recipients=$(git config hooks.mailinglist)
738 announcerecipients=$(git config hooks.announcelist)
739 envelopesender=$(git config hooks.envelopesender)
740 emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
741 custom_showrev=$(git config hooks.showrev)
742 maxlines=$(git config hooks.emailmaxlines)
743 diffopts=$(git config hooks.diffopts)
744 : ${diffopts:="--stat --summary --find-copies-harder"}
745
746 # --- Main loop
747 # Allow dual mode: run from the command line just like the update hook, or
748 # if no arguments are given then run as a hook script
749 if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
750         # Output to the terminal in command line mode - if someone wanted to
751         # resend an email; they could redirect the output to sendmail
752         # themselves
753         prep_for_email $2 $3 $1 && PAGER= generate_email
754 else
755         while read oldrev newrev refname
756         do
757                 prep_for_email $oldrev $newrev $refname || continue
758                 generate_email $maxlines | send_mail
759         done
760 fi