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