##// END OF EJS Templates
notify: add option for deterministic message-id generation...
Joerg Sonnenberger -
r43174:d26a6706 default
parent child Browse files
Show More
@@ -1,519 +1,540 b''
1 1 # notify.py - email notifications for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''hooks for sending email push notifications
9 9
10 10 This extension implements hooks to send email notifications when
11 11 changesets are sent from or received by the local repository.
12 12
13 13 First, enable the extension as explained in :hg:`help extensions`, and
14 14 register the hook you want to run. ``incoming`` and ``changegroup`` hooks
15 15 are run when changesets are received, while ``outgoing`` hooks are for
16 16 changesets sent to another repository::
17 17
18 18 [hooks]
19 19 # one email for each incoming changeset
20 20 incoming.notify = python:hgext.notify.hook
21 21 # one email for all incoming changesets
22 22 changegroup.notify = python:hgext.notify.hook
23 23
24 24 # one email for all outgoing changesets
25 25 outgoing.notify = python:hgext.notify.hook
26 26
27 27 This registers the hooks. To enable notification, subscribers must
28 28 be assigned to repositories. The ``[usersubs]`` section maps multiple
29 29 repositories to a given recipient. The ``[reposubs]`` section maps
30 30 multiple recipients to a single repository::
31 31
32 32 [usersubs]
33 33 # key is subscriber email, value is a comma-separated list of repo patterns
34 34 user@host = pattern
35 35
36 36 [reposubs]
37 37 # key is repo pattern, value is a comma-separated list of subscriber emails
38 38 pattern = user@host
39 39
40 40 A ``pattern`` is a ``glob`` matching the absolute path to a repository,
41 41 optionally combined with a revset expression. A revset expression, if
42 42 present, is separated from the glob by a hash. Example::
43 43
44 44 [reposubs]
45 45 */widgets#branch(release) = qa-team@example.com
46 46
47 47 This sends to ``qa-team@example.com`` whenever a changeset on the ``release``
48 48 branch triggers a notification in any repository ending in ``widgets``.
49 49
50 50 In order to place them under direct user management, ``[usersubs]`` and
51 51 ``[reposubs]`` sections may be placed in a separate ``hgrc`` file and
52 52 incorporated by reference::
53 53
54 54 [notify]
55 55 config = /path/to/subscriptionsfile
56 56
57 57 Notifications will not be sent until the ``notify.test`` value is set
58 58 to ``False``; see below.
59 59
60 60 Notifications content can be tweaked with the following configuration entries:
61 61
62 62 notify.test
63 63 If ``True``, print messages to stdout instead of sending them. Default: True.
64 64
65 65 notify.sources
66 66 Space-separated list of change sources. Notifications are activated only
67 67 when a changeset's source is in this list. Sources may be:
68 68
69 69 :``serve``: changesets received via http or ssh
70 70 :``pull``: changesets received via ``hg pull``
71 71 :``unbundle``: changesets received via ``hg unbundle``
72 72 :``push``: changesets sent or received via ``hg push``
73 73 :``bundle``: changesets sent via ``hg unbundle``
74 74
75 75 Default: serve.
76 76
77 77 notify.strip
78 78 Number of leading slashes to strip from url paths. By default, notifications
79 79 reference repositories with their absolute path. ``notify.strip`` lets you
80 80 turn them into relative paths. For example, ``notify.strip=3`` will change
81 81 ``/long/path/repository`` into ``repository``. Default: 0.
82 82
83 83 notify.domain
84 84 Default email domain for sender or recipients with no explicit domain.
85 It is also used for the domain part of the ``Message-Id`` when using
86 ``notify.messageidseed``.
87
88 notify.messageidseed
89 Create deterministic ``Message-Id`` headers for the mails based on the seed
90 and the revision identifier of the first commit in the changeset.
85 91
86 92 notify.style
87 93 Style file to use when formatting emails.
88 94
89 95 notify.template
90 96 Template to use when formatting emails.
91 97
92 98 notify.incoming
93 99 Template to use when run as an incoming hook, overriding ``notify.template``.
94 100
95 101 notify.outgoing
96 102 Template to use when run as an outgoing hook, overriding ``notify.template``.
97 103
98 104 notify.changegroup
99 105 Template to use when running as a changegroup hook, overriding
100 106 ``notify.template``.
101 107
102 108 notify.maxdiff
103 109 Maximum number of diff lines to include in notification email. Set to 0
104 110 to disable the diff, or -1 to include all of it. Default: 300.
105 111
106 112 notify.maxdiffstat
107 113 Maximum number of diffstat lines to include in notification email. Set to -1
108 114 to include all of it. Default: -1.
109 115
110 116 notify.maxsubject
111 117 Maximum number of characters in email's subject line. Default: 67.
112 118
113 119 notify.diffstat
114 120 Set to True to include a diffstat before diff content. Default: True.
115 121
116 122 notify.showfunc
117 123 If set, override ``diff.showfunc`` for the diff content. Default: None.
118 124
119 125 notify.merge
120 126 If True, send notifications for merge changesets. Default: True.
121 127
122 128 notify.mbox
123 129 If set, append mails to this mbox file instead of sending. Default: None.
124 130
125 131 notify.fromauthor
126 132 If set, use the committer of the first changeset in a changegroup for
127 133 the "From" field of the notification mail. If not set, take the user
128 134 from the pushing repo. Default: False.
129 135
130 136 If set, the following entries will also be used to customize the
131 137 notifications:
132 138
133 139 email.from
134 140 Email ``From`` address to use if none can be found in the generated
135 141 email content.
136 142
137 143 web.baseurl
138 144 Root repository URL to combine with repository paths when making
139 145 references. See also ``notify.strip``.
140 146
141 147 '''
142 148 from __future__ import absolute_import
143 149
144 150 import email.errors as emailerrors
145 151 import email.parser as emailparser
146 152 import fnmatch
153 import hashlib
147 154 import socket
148 155 import time
149 156
150 157 from mercurial.i18n import _
151 158 from mercurial import (
152 159 encoding,
153 160 error,
154 161 logcmdutil,
155 162 mail,
156 163 patch,
157 164 registrar,
158 165 util,
159 166 )
160 167 from mercurial.utils import (
161 168 dateutil,
162 169 stringutil,
163 170 )
164 171
165 172 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
166 173 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
167 174 # be specifying the version(s) of Mercurial they are tested with, or
168 175 # leave the attribute unspecified.
169 176 testedwith = 'ships-with-hg-core'
170 177
171 178 configtable = {}
172 179 configitem = registrar.configitem(configtable)
173 180
174 181 configitem('notify', 'changegroup',
175 182 default=None,
176 183 )
177 184 configitem('notify', 'config',
178 185 default=None,
179 186 )
180 187 configitem('notify', 'diffstat',
181 188 default=True,
182 189 )
183 190 configitem('notify', 'domain',
184 191 default=None,
185 192 )
193 configitem('notify', 'messageidseed',
194 default=None,
195 )
186 196 configitem('notify', 'fromauthor',
187 197 default=None,
188 198 )
189 199 configitem('notify', 'incoming',
190 200 default=None,
191 201 )
192 202 configitem('notify', 'maxdiff',
193 203 default=300,
194 204 )
195 205 configitem('notify', 'maxdiffstat',
196 206 default=-1,
197 207 )
198 208 configitem('notify', 'maxsubject',
199 209 default=67,
200 210 )
201 211 configitem('notify', 'mbox',
202 212 default=None,
203 213 )
204 214 configitem('notify', 'merge',
205 215 default=True,
206 216 )
207 217 configitem('notify', 'outgoing',
208 218 default=None,
209 219 )
210 220 configitem('notify', 'sources',
211 221 default='serve',
212 222 )
213 223 configitem('notify', 'showfunc',
214 224 default=None,
215 225 )
216 226 configitem('notify', 'strip',
217 227 default=0,
218 228 )
219 229 configitem('notify', 'style',
220 230 default=None,
221 231 )
222 232 configitem('notify', 'template',
223 233 default=None,
224 234 )
225 235 configitem('notify', 'test',
226 236 default=True,
227 237 )
228 238
229 239 # template for single changeset can include email headers.
230 240 single_template = b'''
231 241 Subject: changeset in {webroot}: {desc|firstline|strip}
232 242 From: {author}
233 243
234 244 changeset {node|short} in {root}
235 245 details: {baseurl}{webroot}?cmd=changeset;node={node|short}
236 246 description:
237 247 \t{desc|tabindent|strip}
238 248 '''.lstrip()
239 249
240 250 # template for multiple changesets should not contain email headers,
241 251 # because only first set of headers will be used and result will look
242 252 # strange.
243 253 multiple_template = b'''
244 254 changeset {node|short} in {root}
245 255 details: {baseurl}{webroot}?cmd=changeset;node={node|short}
246 256 summary: {desc|firstline}
247 257 '''
248 258
249 259 deftemplates = {
250 260 'changegroup': multiple_template,
251 261 }
252 262
253 263 class notifier(object):
254 264 '''email notification class.'''
255 265
256 266 def __init__(self, ui, repo, hooktype):
257 267 self.ui = ui
258 268 cfg = self.ui.config('notify', 'config')
259 269 if cfg:
260 270 self.ui.readconfig(cfg, sections=['usersubs', 'reposubs'])
261 271 self.repo = repo
262 272 self.stripcount = int(self.ui.config('notify', 'strip'))
263 273 self.root = self.strip(self.repo.root)
264 274 self.domain = self.ui.config('notify', 'domain')
265 275 self.mbox = self.ui.config('notify', 'mbox')
266 276 self.test = self.ui.configbool('notify', 'test')
267 277 self.charsets = mail._charsets(self.ui)
268 278 self.subs = self.subscribers()
269 279 self.merge = self.ui.configbool('notify', 'merge')
270 280 self.showfunc = self.ui.configbool('notify', 'showfunc')
281 self.messageidseed = self.ui.config('notify', 'messageidseed')
271 282 if self.showfunc is None:
272 283 self.showfunc = self.ui.configbool('diff', 'showfunc')
273 284
274 285 mapfile = None
275 286 template = (self.ui.config('notify', hooktype) or
276 287 self.ui.config('notify', 'template'))
277 288 if not template:
278 289 mapfile = self.ui.config('notify', 'style')
279 290 if not mapfile and not template:
280 291 template = deftemplates.get(hooktype) or single_template
281 292 spec = logcmdutil.templatespec(template, mapfile)
282 293 self.t = logcmdutil.changesettemplater(self.ui, self.repo, spec)
283 294
284 295 def strip(self, path):
285 296 '''strip leading slashes from local path, turn into web-safe path.'''
286 297
287 298 path = util.pconvert(path)
288 299 count = self.stripcount
289 300 while count > 0:
290 301 c = path.find('/')
291 302 if c == -1:
292 303 break
293 304 path = path[c + 1:]
294 305 count -= 1
295 306 return path
296 307
297 308 def fixmail(self, addr):
298 309 '''try to clean up email addresses.'''
299 310
300 311 addr = stringutil.email(addr.strip())
301 312 if self.domain:
302 313 a = addr.find('@localhost')
303 314 if a != -1:
304 315 addr = addr[:a]
305 316 if '@' not in addr:
306 317 return addr + '@' + self.domain
307 318 return addr
308 319
309 320 def subscribers(self):
310 321 '''return list of email addresses of subscribers to this repo.'''
311 322 subs = set()
312 323 for user, pats in self.ui.configitems('usersubs'):
313 324 for pat in pats.split(','):
314 325 if '#' in pat:
315 326 pat, revs = pat.split('#', 1)
316 327 else:
317 328 revs = None
318 329 if fnmatch.fnmatch(self.repo.root, pat.strip()):
319 330 subs.add((self.fixmail(user), revs))
320 331 for pat, users in self.ui.configitems('reposubs'):
321 332 if '#' in pat:
322 333 pat, revs = pat.split('#', 1)
323 334 else:
324 335 revs = None
325 336 if fnmatch.fnmatch(self.repo.root, pat):
326 337 for user in users.split(','):
327 338 subs.add((self.fixmail(user), revs))
328 339 return [(mail.addressencode(self.ui, s, self.charsets, self.test), r)
329 340 for s, r in sorted(subs)]
330 341
331 342 def node(self, ctx, **props):
332 343 '''format one changeset, unless it is a suppressed merge.'''
333 344 if not self.merge and len(ctx.parents()) > 1:
334 345 return False
335 346 self.t.show(ctx, changes=ctx.changeset(),
336 347 baseurl=self.ui.config('web', 'baseurl'),
337 348 root=self.repo.root, webroot=self.root, **props)
338 349 return True
339 350
340 351 def skipsource(self, source):
341 352 '''true if incoming changes from this source should be skipped.'''
342 353 ok_sources = self.ui.config('notify', 'sources').split()
343 354 return source not in ok_sources
344 355
345 356 def send(self, ctx, count, data):
346 357 '''send message.'''
347 358
348 359 # Select subscribers by revset
349 360 subs = set()
350 361 for sub, spec in self.subs:
351 362 if spec is None:
352 363 subs.add(sub)
353 364 continue
354 365 revs = self.repo.revs('%r and %d:', spec, ctx.rev())
355 366 if len(revs):
356 367 subs.add(sub)
357 368 continue
358 369 if len(subs) == 0:
359 370 self.ui.debug('notify: no subscribers to selected repo '
360 371 'and revset\n')
361 372 return
362 373
363 374 p = emailparser.Parser()
364 375 try:
365 376 msg = p.parsestr(encoding.strfromlocal(data))
366 377 except emailerrors.MessageParseError as inst:
367 378 raise error.Abort(inst)
368 379
369 380 # store sender and subject
370 381 sender = msg[r'From']
371 382 subject = msg[r'Subject']
372 383 if sender is not None:
373 384 sender = encoding.strtolocal(sender)
374 385 if subject is not None:
375 386 subject = encoding.strtolocal(subject)
376 387 del msg[r'From'], msg[r'Subject']
377 388
378 389 if not msg.is_multipart():
379 390 # create fresh mime message from scratch
380 391 # (multipart templates must take care of this themselves)
381 392 headers = msg.items()
382 393 payload = msg.get_payload()
383 394 # for notification prefer readability over data precision
384 395 msg = mail.mimeencode(self.ui, payload, self.charsets, self.test)
385 396 # reinstate custom headers
386 397 for k, v in headers:
387 398 msg[k] = v
388 399
389 400 msg[r'Date'] = encoding.strfromlocal(
390 401 dateutil.datestr(format="%a, %d %b %Y %H:%M:%S %1%2"))
391 402
392 403 # try to make subject line exist and be useful
393 404 if not subject:
394 405 if count > 1:
395 406 subject = _('%s: %d new changesets') % (self.root, count)
396 407 else:
397 408 s = ctx.description().lstrip().split('\n', 1)[0].rstrip()
398 409 subject = '%s: %s' % (self.root, s)
399 410 maxsubject = int(self.ui.config('notify', 'maxsubject'))
400 411 if maxsubject:
401 412 subject = stringutil.ellipsis(subject, maxsubject)
402 413 msg[r'Subject'] = encoding.strfromlocal(
403 414 mail.headencode(self.ui, subject, self.charsets, self.test))
404 415
405 416 # try to make message have proper sender
406 417 if not sender:
407 418 sender = self.ui.config('email', 'from') or self.ui.username()
408 419 if '@' not in sender or '@localhost' in sender:
409 420 sender = self.fixmail(sender)
410 421 msg[r'From'] = encoding.strfromlocal(
411 422 mail.addressencode(self.ui, sender, self.charsets, self.test))
412 423
413 424 msg[r'X-Hg-Notification'] = r'changeset %s' % ctx
414 425 if not msg[r'Message-Id']:
415 msg[r'Message-Id'] = encoding.strfromlocal(
416 '<hg.%s.%d.%d@%s>' % (ctx, int(time.time()),
417 hash(self.repo.root),
418 encoding.strtolocal(socket.getfqdn())))
426 msg[r'Message-Id'] = messageid(ctx, self.domain, self.messageidseed)
419 427 msg[r'To'] = encoding.strfromlocal(', '.join(sorted(subs)))
420 428
421 429 msgtext = encoding.strtolocal(msg.as_string())
422 430 if self.test:
423 431 self.ui.write(msgtext)
424 432 if not msgtext.endswith('\n'):
425 433 self.ui.write('\n')
426 434 else:
427 435 self.ui.status(_('notify: sending %d subscribers %d changes\n') %
428 436 (len(subs), count))
429 437 mail.sendmail(self.ui, stringutil.email(msg[r'From']),
430 438 subs, msgtext, mbox=self.mbox)
431 439
432 440 def diff(self, ctx, ref=None):
433 441
434 442 maxdiff = int(self.ui.config('notify', 'maxdiff'))
435 443 prev = ctx.p1().node()
436 444 if ref:
437 445 ref = ref.node()
438 446 else:
439 447 ref = ctx.node()
440 448 diffopts = patch.diffallopts(self.ui)
441 449 diffopts.showfunc = self.showfunc
442 450 chunks = patch.diff(self.repo, prev, ref, opts=diffopts)
443 451 difflines = ''.join(chunks).splitlines()
444 452
445 453 if self.ui.configbool('notify', 'diffstat'):
446 454 maxdiffstat = int(self.ui.config('notify', 'maxdiffstat'))
447 455 s = patch.diffstat(difflines)
448 456 # s may be nil, don't include the header if it is
449 457 if s:
450 458 if maxdiffstat >= 0 and s.count("\n") > maxdiffstat + 1:
451 459 s = s.split("\n")
452 460 msg = _('\ndiffstat (truncated from %d to %d lines):\n\n')
453 461 self.ui.write(msg % (len(s) - 2, maxdiffstat))
454 462 self.ui.write("\n".join(s[:maxdiffstat] + s[-2:]))
455 463 else:
456 464 self.ui.write(_('\ndiffstat:\n\n%s') % s)
457 465
458 466 if maxdiff == 0:
459 467 return
460 468 elif maxdiff > 0 and len(difflines) > maxdiff:
461 469 msg = _('\ndiffs (truncated from %d to %d lines):\n\n')
462 470 self.ui.write(msg % (len(difflines), maxdiff))
463 471 difflines = difflines[:maxdiff]
464 472 elif difflines:
465 473 self.ui.write(_('\ndiffs (%d lines):\n\n') % len(difflines))
466 474
467 475 self.ui.write("\n".join(difflines))
468 476
469 477 def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
470 478 '''send email notifications to interested subscribers.
471 479
472 480 if used as changegroup hook, send one email for all changesets in
473 481 changegroup. else send one email per changeset.'''
474 482
475 483 n = notifier(ui, repo, hooktype)
476 484 ctx = repo.unfiltered()[node]
477 485
478 486 if not n.subs:
479 487 ui.debug('notify: no subscribers to repository %s\n' % n.root)
480 488 return
481 489 if n.skipsource(source):
482 490 ui.debug('notify: changes have source "%s" - skipping\n' % source)
483 491 return
484 492
485 493 ui.pushbuffer()
486 494 data = ''
487 495 count = 0
488 496 author = ''
489 497 if hooktype == 'changegroup' or hooktype == 'outgoing':
490 498 for rev in repo.changelog.revs(start=ctx.rev()):
491 499 if n.node(repo[rev]):
492 500 count += 1
493 501 if not author:
494 502 author = repo[rev].user()
495 503 else:
496 504 data += ui.popbuffer()
497 505 ui.note(_('notify: suppressing notification for merge %d:%s\n')
498 506 % (rev, repo[rev].hex()[:12]))
499 507 ui.pushbuffer()
500 508 if count:
501 509 n.diff(ctx, repo['tip'])
502 510 elif ctx.rev() in repo:
503 511 if not n.node(ctx):
504 512 ui.popbuffer()
505 513 ui.note(_('notify: suppressing notification for merge %d:%s\n') %
506 514 (ctx.rev(), ctx.hex()[:12]))
507 515 return
508 516 count += 1
509 517 n.diff(ctx)
510 518 if not author:
511 519 author = ctx.user()
512 520
513 521 data += ui.popbuffer()
514 522 fromauthor = ui.config('notify', 'fromauthor')
515 523 if author and fromauthor:
516 524 data = '\n'.join(['From: %s' % author, data])
517 525
518 526 if count:
519 527 n.send(ctx, count, data)
528
529 def messageid(ctx, domain, messageidseed):
530 if domain and messageidseed:
531 host = domain
532 else:
533 host = encoding.strtolocal(socket.getfqdn())
534 if messageidseed:
535 messagehash = hashlib.sha512(ctx.hex() + messageidseed)
536 messageid = '<hg.%s@%s>' % (messagehash.hexdigest()[:64], host)
537 else:
538 messageid = '<hg.%s.%d.%d@%s>' % (ctx, int(time.time()),
539 hash(ctx.repo().root), host)
540 return encoding.strfromlocal(messageid)
@@ -1,748 +1,754 b''
1 1 $ cat > $TESTTMP/filter.py <<EOF
2 2 > from __future__ import absolute_import, print_function
3 3 > import re
4 4 > import sys
5 5 > print(re.sub("\n[ \t]", " ", sys.stdin.read()), end="")
6 6 > EOF
7 7
8 8 $ cat <<EOF >> $HGRCPATH
9 9 > [extensions]
10 10 > notify=
11 11 >
12 12 > [hooks]
13 13 > incoming.notify = python:hgext.notify.hook
14 14 >
15 15 > [notify]
16 16 > sources = pull
17 17 > diffstat = False
18 18 >
19 19 > [usersubs]
20 20 > foo@bar = *
21 21 >
22 22 > [reposubs]
23 23 > * = baz
24 24 > EOF
25 25 $ hg help notify
26 26 notify extension - hooks for sending email push notifications
27 27
28 28 This extension implements hooks to send email notifications when changesets
29 29 are sent from or received by the local repository.
30 30
31 31 First, enable the extension as explained in 'hg help extensions', and register
32 32 the hook you want to run. "incoming" and "changegroup" hooks are run when
33 33 changesets are received, while "outgoing" hooks are for changesets sent to
34 34 another repository:
35 35
36 36 [hooks]
37 37 # one email for each incoming changeset
38 38 incoming.notify = python:hgext.notify.hook
39 39 # one email for all incoming changesets
40 40 changegroup.notify = python:hgext.notify.hook
41 41
42 42 # one email for all outgoing changesets
43 43 outgoing.notify = python:hgext.notify.hook
44 44
45 45 This registers the hooks. To enable notification, subscribers must be assigned
46 46 to repositories. The "[usersubs]" section maps multiple repositories to a
47 47 given recipient. The "[reposubs]" section maps multiple recipients to a single
48 48 repository:
49 49
50 50 [usersubs]
51 51 # key is subscriber email, value is a comma-separated list of repo patterns
52 52 user@host = pattern
53 53
54 54 [reposubs]
55 55 # key is repo pattern, value is a comma-separated list of subscriber emails
56 56 pattern = user@host
57 57
58 58 A "pattern" is a "glob" matching the absolute path to a repository, optionally
59 59 combined with a revset expression. A revset expression, if present, is
60 60 separated from the glob by a hash. Example:
61 61
62 62 [reposubs]
63 63 */widgets#branch(release) = qa-team@example.com
64 64
65 65 This sends to "qa-team@example.com" whenever a changeset on the "release"
66 66 branch triggers a notification in any repository ending in "widgets".
67 67
68 68 In order to place them under direct user management, "[usersubs]" and
69 69 "[reposubs]" sections may be placed in a separate "hgrc" file and incorporated
70 70 by reference:
71 71
72 72 [notify]
73 73 config = /path/to/subscriptionsfile
74 74
75 75 Notifications will not be sent until the "notify.test" value is set to
76 76 "False"; see below.
77 77
78 78 Notifications content can be tweaked with the following configuration entries:
79 79
80 80 notify.test
81 81 If "True", print messages to stdout instead of sending them. Default: True.
82 82
83 83 notify.sources
84 84 Space-separated list of change sources. Notifications are activated only
85 85 when a changeset's source is in this list. Sources may be:
86 86
87 87 "serve" changesets received via http or ssh
88 88 "pull" changesets received via "hg pull"
89 89 "unbundle" changesets received via "hg unbundle"
90 90 "push" changesets sent or received via "hg push"
91 91 "bundle" changesets sent via "hg unbundle"
92 92
93 93 Default: serve.
94 94
95 95 notify.strip
96 96 Number of leading slashes to strip from url paths. By default, notifications
97 97 reference repositories with their absolute path. "notify.strip" lets you
98 98 turn them into relative paths. For example, "notify.strip=3" will change
99 99 "/long/path/repository" into "repository". Default: 0.
100 100
101 101 notify.domain
102 Default email domain for sender or recipients with no explicit domain.
102 Default email domain for sender or recipients with no explicit domain. It is
103 also used for the domain part of the "Message-Id" when using
104 "notify.messageidseed".
105
106 notify.messageidseed
107 Create deterministic "Message-Id" headers for the mails based on the seed
108 and the revision identifier of the first commit in the changeset.
103 109
104 110 notify.style
105 111 Style file to use when formatting emails.
106 112
107 113 notify.template
108 114 Template to use when formatting emails.
109 115
110 116 notify.incoming
111 117 Template to use when run as an incoming hook, overriding "notify.template".
112 118
113 119 notify.outgoing
114 120 Template to use when run as an outgoing hook, overriding "notify.template".
115 121
116 122 notify.changegroup
117 123 Template to use when running as a changegroup hook, overriding
118 124 "notify.template".
119 125
120 126 notify.maxdiff
121 127 Maximum number of diff lines to include in notification email. Set to 0 to
122 128 disable the diff, or -1 to include all of it. Default: 300.
123 129
124 130 notify.maxdiffstat
125 131 Maximum number of diffstat lines to include in notification email. Set to -1
126 132 to include all of it. Default: -1.
127 133
128 134 notify.maxsubject
129 135 Maximum number of characters in email's subject line. Default: 67.
130 136
131 137 notify.diffstat
132 138 Set to True to include a diffstat before diff content. Default: True.
133 139
134 140 notify.showfunc
135 141 If set, override "diff.showfunc" for the diff content. Default: None.
136 142
137 143 notify.merge
138 144 If True, send notifications for merge changesets. Default: True.
139 145
140 146 notify.mbox
141 147 If set, append mails to this mbox file instead of sending. Default: None.
142 148
143 149 notify.fromauthor
144 150 If set, use the committer of the first changeset in a changegroup for the
145 151 "From" field of the notification mail. If not set, take the user from the
146 152 pushing repo. Default: False.
147 153
148 154 If set, the following entries will also be used to customize the
149 155 notifications:
150 156
151 157 email.from
152 158 Email "From" address to use if none can be found in the generated email
153 159 content.
154 160
155 161 web.baseurl
156 162 Root repository URL to combine with repository paths when making references.
157 163 See also "notify.strip".
158 164
159 165 no commands defined
160 166 $ hg init a
161 167 $ echo a > a/a
162 168 $ echo b > a/b
163 169
164 170 commit
165 171
166 172 $ hg --cwd a commit -Ama -d '0 0'
167 173 adding a
168 174 adding b
169 175
170 176 clone
171 177
172 178 $ hg --traceback clone a b
173 179 updating to branch default
174 180 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 181 $ echo a >> a/a
176 182 $ echo b >> a/b
177 183
178 184 commit
179 185
180 186 $ hg --traceback --cwd a commit -Amb -d '1 0'
181 187
182 188 on Mac OS X 10.5 the tmp path is very long so would get stripped in the subject line
183 189
184 190 $ cat <<EOF >> $HGRCPATH
185 191 > [notify]
186 192 > maxsubject = 200
187 193 > EOF
188 194
189 195 the python call below wraps continuation lines, which appear on Mac OS X 10.5 because
190 196 of the very long subject line
191 197 pull (minimal config)
192 198
193 $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
199 $ hg --traceback --cwd b --config notify.domain=example.com --config notify.messageidseed=example pull ../a | "$PYTHON" $TESTTMP/filter.py
194 200 pulling from ../a
195 201 searching for changes
196 202 adding changesets
197 203 adding manifests
198 204 adding file changes
199 205 added 1 changesets with 2 changes to 2 files
200 206 new changesets 00a13f371396
201 207 MIME-Version: 1.0
202 208 Content-Type: text/plain; charset="us-ascii"
203 209 Content-Transfer-Encoding: 7bit
204 210 Date: * (glob)
205 211 Subject: changeset in $TESTTMP/b: b
206 From: test
212 From: test@example.com
207 213 X-Hg-Notification: changeset 00a13f371396
208 Message-Id: <*> (glob)
209 To: baz, foo@bar
214 Message-Id: <hg.ba3098a36bd4c297288d16788623a841f81f618ea961a0f0fd65de7eb1191b66@example.com>
215 To: baz@example.com, foo@bar
210 216
211 217 changeset 00a13f371396 in $TESTTMP/b
212 218 details: $TESTTMP/b?cmd=changeset;node=00a13f371396
213 219 description: b
214 220
215 221 diffs (12 lines):
216 222
217 223 diff -r 0cd96de13884 -r 00a13f371396 a
218 224 --- a/a Thu Jan 01 00:00:00 1970 +0000
219 225 +++ b/a Thu Jan 01 00:00:01 1970 +0000
220 226 @@ -1,1 +1,2 @@ a
221 227 +a
222 228 diff -r 0cd96de13884 -r 00a13f371396 b
223 229 --- a/b Thu Jan 01 00:00:00 1970 +0000
224 230 +++ b/b Thu Jan 01 00:00:01 1970 +0000
225 231 @@ -1,1 +1,2 @@ b
226 232 +b
227 233 (run 'hg update' to get a working copy)
228 234
229 235 $ cat <<EOF >> $HGRCPATH
230 236 > [notify]
231 237 > config = `pwd`/.notify.conf
232 238 > domain = test.com
233 239 > strip = 42
234 240 > template = Subject: {desc|firstline|strip}\nFrom: {author}\nX-Test: foo\n\nchangeset {node|short} in {webroot}\ndescription:\n\t{desc|tabindent|strip}
235 241 >
236 242 > [web]
237 243 > baseurl = http://test/
238 244 > EOF
239 245
240 246 fail for config file is missing
241 247
242 248 $ hg --cwd b rollback
243 249 repository tip rolled back to revision 0 (undo pull)
244 250 $ hg --cwd b pull ../a 2>&1 | grep 'error.*\.notify\.conf' > /dev/null && echo pull failed
245 251 pull failed
246 252 $ touch ".notify.conf"
247 253
248 254 pull
249 255
250 256 $ hg --cwd b rollback
251 257 repository tip rolled back to revision 0 (undo pull)
252 258 $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
253 259 pulling from ../a
254 260 searching for changes
255 261 adding changesets
256 262 adding manifests
257 263 adding file changes
258 264 added 1 changesets with 2 changes to 2 files
259 265 new changesets 00a13f371396
260 266 MIME-Version: 1.0
261 267 Content-Type: text/plain; charset="us-ascii"
262 268 Content-Transfer-Encoding: 7bit
263 269 X-Test: foo
264 270 Date: * (glob)
265 271 Subject: b
266 272 From: test@test.com
267 273 X-Hg-Notification: changeset 00a13f371396
268 274 Message-Id: <*> (glob)
269 275 To: baz@test.com, foo@bar
270 276
271 277 changeset 00a13f371396 in b
272 278 description: b
273 279 diffs (12 lines):
274 280
275 281 diff -r 0cd96de13884 -r 00a13f371396 a
276 282 --- a/a Thu Jan 01 00:00:00 1970 +0000
277 283 +++ b/a Thu Jan 01 00:00:01 1970 +0000
278 284 @@ -1,1 +1,2 @@ a
279 285 +a
280 286 diff -r 0cd96de13884 -r 00a13f371396 b
281 287 --- a/b Thu Jan 01 00:00:00 1970 +0000
282 288 +++ b/b Thu Jan 01 00:00:01 1970 +0000
283 289 @@ -1,1 +1,2 @@ b
284 290 +b
285 291 (run 'hg update' to get a working copy)
286 292
287 293 $ cat << EOF >> $HGRCPATH
288 294 > [hooks]
289 295 > incoming.notify = python:hgext.notify.hook
290 296 >
291 297 > [notify]
292 298 > sources = pull
293 299 > diffstat = True
294 300 > EOF
295 301
296 302 pull
297 303
298 304 $ hg --cwd b rollback
299 305 repository tip rolled back to revision 0 (undo pull)
300 306 $ hg --traceback --config notify.maxdiffstat=1 --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
301 307 pulling from ../a
302 308 searching for changes
303 309 adding changesets
304 310 adding manifests
305 311 adding file changes
306 312 added 1 changesets with 2 changes to 2 files
307 313 new changesets 00a13f371396
308 314 MIME-Version: 1.0
309 315 Content-Type: text/plain; charset="us-ascii"
310 316 Content-Transfer-Encoding: 7bit
311 317 X-Test: foo
312 318 Date: * (glob)
313 319 Subject: b
314 320 From: test@test.com
315 321 X-Hg-Notification: changeset 00a13f371396
316 322 Message-Id: <*> (glob)
317 323 To: baz@test.com, foo@bar
318 324
319 325 changeset 00a13f371396 in b
320 326 description: b
321 327 diffstat (truncated from 2 to 1 lines):
322 328 a | 1 + 2 files changed, 2 insertions(+), 0 deletions(-)
323 329
324 330 diffs (12 lines):
325 331
326 332 diff -r 0cd96de13884 -r 00a13f371396 a
327 333 --- a/a Thu Jan 01 00:00:00 1970 +0000
328 334 +++ b/a Thu Jan 01 00:00:01 1970 +0000
329 335 @@ -1,1 +1,2 @@ a
330 336 +a
331 337 diff -r 0cd96de13884 -r 00a13f371396 b
332 338 --- a/b Thu Jan 01 00:00:00 1970 +0000
333 339 +++ b/b Thu Jan 01 00:00:01 1970 +0000
334 340 @@ -1,1 +1,2 @@ b
335 341 +b
336 342 (run 'hg update' to get a working copy)
337 343
338 344 test merge
339 345
340 346 $ cd a
341 347 $ hg up -C 0
342 348 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 349 $ echo a >> a
344 350 $ hg ci -Am adda2 -d '2 0'
345 351 created new head
346 352 $ hg merge
347 353 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 354 (branch merge, don't forget to commit)
349 355 $ hg ci -m merge -d '3 0'
350 356 $ cd ..
351 357 $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
352 358 pulling from ../a
353 359 searching for changes
354 360 adding changesets
355 361 adding manifests
356 362 adding file changes
357 363 added 2 changesets with 0 changes to 0 files
358 364 new changesets 3332653e1f3c:fccf66cd0c35
359 365 MIME-Version: 1.0
360 366 Content-Type: text/plain; charset="us-ascii"
361 367 Content-Transfer-Encoding: 7bit
362 368 X-Test: foo
363 369 Date: * (glob)
364 370 Subject: adda2
365 371 From: test@test.com
366 372 X-Hg-Notification: changeset 3332653e1f3c
367 373 Message-Id: <*> (glob)
368 374 To: baz@test.com, foo@bar
369 375
370 376 changeset 3332653e1f3c in b
371 377 description: adda2
372 378 diffstat:
373 379 a | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
374 380
375 381 diffs (6 lines):
376 382
377 383 diff -r 0cd96de13884 -r 3332653e1f3c a
378 384 --- a/a Thu Jan 01 00:00:00 1970 +0000
379 385 +++ b/a Thu Jan 01 00:00:02 1970 +0000
380 386 @@ -1,1 +1,2 @@ a
381 387 +a
382 388 MIME-Version: 1.0
383 389 Content-Type: text/plain; charset="us-ascii"
384 390 Content-Transfer-Encoding: 7bit
385 391 X-Test: foo
386 392 Date: * (glob)
387 393 Subject: merge
388 394 From: test@test.com
389 395 X-Hg-Notification: changeset fccf66cd0c35
390 396 Message-Id: <*> (glob)
391 397 To: baz@test.com, foo@bar
392 398
393 399 changeset fccf66cd0c35 in b
394 400 description: merge
395 401 diffstat:
396 402 b | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
397 403
398 404 diffs (6 lines):
399 405
400 406 diff -r 3332653e1f3c -r fccf66cd0c35 b
401 407 --- a/b Thu Jan 01 00:00:02 1970 +0000
402 408 +++ b/b Thu Jan 01 00:00:03 1970 +0000
403 409 @@ -1,1 +1,2 @@ b
404 410 +b
405 411 (run 'hg update' to get a working copy)
406 412
407 413 non-ascii content and truncation of multi-byte subject
408 414
409 415 $ cat <<EOF >> $HGRCPATH
410 416 > [notify]
411 417 > maxsubject = 4
412 418 > EOF
413 419 $ echo a >> a/a
414 420 $ hg --cwd a --encoding utf-8 commit -A -d '0 0' \
415 421 > -m `"$PYTHON" -c 'print("\xc3\xa0\xc3\xa1\xc3\xa2\xc3\xa3\xc3\xa4")'`
416 422 $ hg --traceback --cwd b --encoding utf-8 pull ../a | \
417 423 > "$PYTHON" $TESTTMP/filter.py
418 424 pulling from ../a
419 425 searching for changes
420 426 adding changesets
421 427 adding manifests
422 428 adding file changes
423 429 added 1 changesets with 1 changes to 1 files
424 430 new changesets 0f25f9c22b4c
425 431 MIME-Version: 1.0
426 432 Content-Type: text/plain; charset="us-ascii"
427 433 Content-Transfer-Encoding: 8bit
428 434 X-Test: foo
429 435 Date: * (glob)
430 436 Subject: \xc3\xa0... (esc)
431 437 From: test@test.com
432 438 X-Hg-Notification: changeset 0f25f9c22b4c
433 439 Message-Id: <*> (glob)
434 440 To: baz@test.com, foo@bar
435 441
436 442 changeset 0f25f9c22b4c in b
437 443 description: \xc3\xa0\xc3\xa1\xc3\xa2\xc3\xa3\xc3\xa4 (esc)
438 444 diffstat:
439 445 a | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
440 446
441 447 diffs (7 lines):
442 448
443 449 diff -r fccf66cd0c35 -r 0f25f9c22b4c a
444 450 --- a/a Thu Jan 01 00:00:03 1970 +0000
445 451 +++ b/a Thu Jan 01 00:00:00 1970 +0000
446 452 @@ -1,2 +1,3 @@ a a
447 453 +a
448 454 (run 'hg update' to get a working copy)
449 455
450 456 long lines
451 457
452 458 $ cat <<EOF >> $HGRCPATH
453 459 > [notify]
454 460 > maxsubject = 67
455 461 > test = False
456 462 > mbox = mbox
457 463 > EOF
458 464 $ "$PYTHON" -c 'open("a/a", "ab").write(b"no" * 500 + b"\xd1\x84" + b"\n")'
459 465 $ hg --cwd a commit -A -m "long line"
460 466 $ hg --traceback --cwd b pull ../a
461 467 pulling from ../a
462 468 searching for changes
463 469 adding changesets
464 470 adding manifests
465 471 adding file changes
466 472 added 1 changesets with 1 changes to 1 files
467 473 new changesets a846b5f6ebb7
468 474 notify: sending 2 subscribers 1 changes
469 475 (run 'hg update' to get a working copy)
470 476 $ "$PYTHON" $TESTTMP/filter.py < b/mbox
471 477 From test@test.com ... ... .. ..:..:.. .... (re)
472 478 MIME-Version: 1.0
473 479 Content-Type: text/plain; charset="*" (glob)
474 480 Content-Transfer-Encoding: quoted-printable
475 481 X-Test: foo
476 482 Date: * (glob)
477 483 Subject: long line
478 484 From: test@test.com
479 485 X-Hg-Notification: changeset a846b5f6ebb7
480 486 Message-Id: <hg.a846b5f6ebb7.*.*@*> (glob)
481 487 To: baz@test.com, foo@bar
482 488
483 489 changeset a846b5f6ebb7 in b
484 490 description: long line
485 491 diffstat:
486 492 a | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
487 493
488 494 diffs (8 lines):
489 495
490 496 diff -r 0f25f9c22b4c -r a846b5f6ebb7 a
491 497 --- a/a Thu Jan 01 00:00:00 1970 +0000
492 498 +++ b/a Thu Jan 01 00:00:00 1970 +0000
493 499 @@ -1,3 +1,4 @@ a a a
494 500 +nonononononononononononononononononononononononononononononononononononono=
495 501 nononononononononononononononononononononononononononononononononononononon=
496 502 ononononononononononononononononononononononononononononononononononononono=
497 503 nononononononononononononononononononononononononononononononononononononon=
498 504 ononononononononononononononononononononononononononononononononononononono=
499 505 nononononononononononononononononononononononononononononononononononononon=
500 506 ononononononononononononononononononononononononononononononononononononono=
501 507 nononononononononononononononononononononononononononononononononononononon=
502 508 ononononononononononononononononononononononononononononononononononononono=
503 509 nononononononononononononononononononononononononononononononononononononon=
504 510 ononononononononononononononononononononononononononononononononononononono=
505 511 nononononononononononononononononononononononononononononononononononononon=
506 512 ononononononononononononononononononononononononononononononononononononono=
507 513 nonononononononononononono=D1=84
508 514
509 515 revset selection: send to address that matches branch and repo
510 516
511 517 $ cat << EOF >> $HGRCPATH
512 518 > [hooks]
513 519 > incoming.notify = python:hgext.notify.hook
514 520 >
515 521 > [notify]
516 522 > sources = pull
517 523 > test = True
518 524 > diffstat = False
519 525 > maxdiff = 0
520 526 >
521 527 > [reposubs]
522 528 > */a#branch(test) = will_no_be_send@example.com
523 529 > */b#branch(test) = notify@example.com
524 530 > EOF
525 531 $ hg --cwd a branch test
526 532 marked working directory as branch test
527 533 (branches are permanent and global, did you want a bookmark?)
528 534 $ echo a >> a/a
529 535 $ hg --cwd a ci -m test -d '1 0'
530 536 $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
531 537 pulling from ../a
532 538 searching for changes
533 539 adding changesets
534 540 adding manifests
535 541 adding file changes
536 542 added 1 changesets with 1 changes to 1 files
537 543 new changesets f7e5aaed4080
538 544 MIME-Version: 1.0
539 545 Content-Type: text/plain; charset="us-ascii"
540 546 Content-Transfer-Encoding: 7bit
541 547 X-Test: foo
542 548 Date: * (glob)
543 549 Subject: test
544 550 From: test@test.com
545 551 X-Hg-Notification: changeset f7e5aaed4080
546 552 Message-Id: <hg.f7e5aaed4080.*.*@*> (glob)
547 553 To: baz@test.com, foo@bar, notify@example.com
548 554
549 555 changeset f7e5aaed4080 in b
550 556 description: test
551 557 (run 'hg update' to get a working copy)
552 558
553 559 revset selection: don't send to address that waits for mails
554 560 from different branch
555 561
556 562 $ hg --cwd a update default
557 563 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
558 564 $ echo a >> a/a
559 565 $ hg --cwd a ci -m test -d '1 0'
560 566 $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
561 567 pulling from ../a
562 568 searching for changes
563 569 adding changesets
564 570 adding manifests
565 571 adding file changes
566 572 added 1 changesets with 0 changes to 0 files (+1 heads)
567 573 new changesets 645eb6690ecf
568 574 MIME-Version: 1.0
569 575 Content-Type: text/plain; charset="us-ascii"
570 576 Content-Transfer-Encoding: 7bit
571 577 X-Test: foo
572 578 Date: * (glob)
573 579 Subject: test
574 580 From: test@test.com
575 581 X-Hg-Notification: changeset 645eb6690ecf
576 582 Message-Id: <hg.645eb6690ecf.*.*@*> (glob)
577 583 To: baz@test.com, foo@bar
578 584
579 585 changeset 645eb6690ecf in b
580 586 description: test
581 587 (run 'hg heads' to see heads)
582 588
583 589 default template:
584 590
585 591 $ grep -v '^template =' $HGRCPATH > "$HGRCPATH.new"
586 592 $ mv "$HGRCPATH.new" $HGRCPATH
587 593 $ echo a >> a/a
588 594 $ hg --cwd a commit -m 'default template'
589 595 $ hg --cwd b pull ../a -q | "$PYTHON" $TESTTMP/filter.py
590 596 MIME-Version: 1.0
591 597 Content-Type: text/plain; charset="us-ascii"
592 598 Content-Transfer-Encoding: 7bit
593 599 Date: * (glob)
594 600 Subject: changeset in b: default template
595 601 From: test@test.com
596 602 X-Hg-Notification: changeset 5cd4346eed47
597 603 Message-Id: <hg.5cd4346eed47.*.*@*> (glob)
598 604 To: baz@test.com, foo@bar
599 605
600 606 changeset 5cd4346eed47 in $TESTTMP/b
601 607 details: http://test/b?cmd=changeset;node=5cd4346eed47
602 608 description: default template
603 609
604 610 with style:
605 611
606 612 $ cat <<EOF > notifystyle.map
607 613 > changeset = "Subject: {desc|firstline|strip}
608 614 > From: {author}
609 615 > {""}
610 616 > changeset {node|short}"
611 617 > EOF
612 618 $ cat <<EOF >> $HGRCPATH
613 619 > [notify]
614 620 > style = $TESTTMP/notifystyle.map
615 621 > EOF
616 622 $ echo a >> a/a
617 623 $ hg --cwd a commit -m 'with style'
618 624 $ hg --cwd b pull ../a -q | "$PYTHON" $TESTTMP/filter.py
619 625 MIME-Version: 1.0
620 626 Content-Type: text/plain; charset="us-ascii"
621 627 Content-Transfer-Encoding: 7bit
622 628 Date: * (glob)
623 629 Subject: with style
624 630 From: test@test.com
625 631 X-Hg-Notification: changeset ec8d9d852f56
626 632 Message-Id: <hg.ec8d9d852f56.*.*@*> (glob)
627 633 To: baz@test.com, foo@bar
628 634
629 635 changeset ec8d9d852f56
630 636
631 637 with template (overrides style):
632 638
633 639 $ cat <<EOF >> $HGRCPATH
634 640 > template = Subject: {node|short}: {desc|firstline|strip}
635 641 > From: {author}
636 642 > {""}
637 643 > {desc}
638 644 > EOF
639 645 $ echo a >> a/a
640 646 $ hg --cwd a commit -m 'with template'
641 647 $ hg --cwd b pull ../a -q | "$PYTHON" $TESTTMP/filter.py
642 648 MIME-Version: 1.0
643 649 Content-Type: text/plain; charset="us-ascii"
644 650 Content-Transfer-Encoding: 7bit
645 651 Date: * (glob)
646 652 Subject: 14721b538ae3: with template
647 653 From: test@test.com
648 654 X-Hg-Notification: changeset 14721b538ae3
649 655 Message-Id: <hg.14721b538ae3.*.*@*> (glob)
650 656 To: baz@test.com, foo@bar
651 657
652 658 with template
653 659
654 660 showfunc diff
655 661 $ cat <<EOF >> $HGRCPATH
656 662 > showfunc = True
657 663 > template =
658 664 > maxdiff = -1
659 665 > EOF
660 666 $ cd a
661 667 $ cat > f1 << EOF
662 668 > int main() {
663 669 > int a = 0;
664 670 > int b = 1;
665 671 > int c = 2;
666 672 > int d = 3;
667 673 > return a + b + c + d;
668 674 > }
669 675 > EOF
670 676 $ hg commit -Am addfunction
671 677 adding f1
672 678 $ hg --cwd ../b pull ../a
673 679 pulling from ../a
674 680 searching for changes
675 681 adding changesets
676 682 adding manifests
677 683 adding file changes
678 684 added 1 changesets with 1 changes to 1 files
679 685 new changesets b86bc16ff894
680 686 MIME-Version: 1.0
681 687 Content-Type: text/plain; charset="us-ascii"
682 688 Content-Transfer-Encoding: 7bit
683 689 Date: * (glob)
684 690 Subject: addfunction
685 691 From: test@test.com
686 692 X-Hg-Notification: changeset b86bc16ff894
687 693 Message-Id: <hg.b86bc16ff894.*.*@*> (glob)
688 694 To: baz@test.com, foo@bar
689 695
690 696 changeset b86bc16ff894
691 697 diffs (11 lines):
692 698
693 699 diff -r 14721b538ae3 -r b86bc16ff894 f1
694 700 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
695 701 +++ b/f1 Thu Jan 01 00:00:00 1970 +0000
696 702 @@ -0,0 +1,7 @@
697 703 +int main() {
698 704 + int a = 0;
699 705 + int b = 1;
700 706 + int c = 2;
701 707 + int d = 3;
702 708 + return a + b + c + d;
703 709 +}
704 710 (run 'hg update' to get a working copy)
705 711 $ cat > f1 << EOF
706 712 > int main() {
707 713 > int a = 0;
708 714 > int b = 1;
709 715 > int c = 2;
710 716 > int e = 3;
711 717 > return a + b + c + e;
712 718 > }
713 719 > EOF
714 720 $ hg commit -m changefunction
715 721 $ hg --cwd ../b --config notify.showfunc=True pull ../a
716 722 pulling from ../a
717 723 searching for changes
718 724 adding changesets
719 725 adding manifests
720 726 adding file changes
721 727 added 1 changesets with 1 changes to 1 files
722 728 new changesets e81040e9838c
723 729 MIME-Version: 1.0
724 730 Content-Type: text/plain; charset="us-ascii"
725 731 Content-Transfer-Encoding: 7bit
726 732 Date: * (glob)
727 733 Subject: changefunction
728 734 From: test@test.com
729 735 X-Hg-Notification: changeset e81040e9838c
730 736 Message-Id: <hg.e81040e9838c.*.*@*> (glob)
731 737 To: baz@test.com, foo@bar
732 738
733 739 changeset e81040e9838c
734 740 diffs (12 lines):
735 741
736 742 diff -r b86bc16ff894 -r e81040e9838c f1
737 743 --- a/f1 Thu Jan 01 00:00:00 1970 +0000
738 744 +++ b/f1 Thu Jan 01 00:00:00 1970 +0000
739 745 @@ -2,6 +2,6 @@ int main() {
740 746 int a = 0;
741 747 int b = 1;
742 748 int c = 2;
743 749 - int d = 3;
744 750 - return a + b + c + d;
745 751 + int e = 3;
746 752 + return a + b + c + e;
747 753 }
748 754 (run 'hg update' to get a working copy)
General Comments 0
You need to be logged in to leave comments. Login now