##// END OF EJS Templates
notify: cleanup module docstring
Martin Geisler -
r9104:c9c5aa12 default
parent child Browse files
Show More
@@ -1,289 +1,289 b''
1 # notify.py - email notifications for mercurial
1 # notify.py - email notifications for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''hooks for sending email notifications at commit/push time
8 '''hooks for sending email notifications at commit/push time
9
9
10 Subscriptions can be managed through hgrc. Default mode is to print messages
10 Subscriptions can be managed through a hgrc file. Default mode is to print
11 to stdout, for testing and configuring.
11 messages to stdout, for testing and configuring.
12
12
13 To use, configure notify extension and enable in hgrc like this:
13 To use, configure the notify extension and enable it in hgrc like this:
14
14
15 [extensions]
15 [extensions]
16 hgext.notify =
16 hgext.notify =
17
17
18 [hooks]
18 [hooks]
19 # one email for each incoming changeset
19 # one email for each incoming changeset
20 incoming.notify = python:hgext.notify.hook
20 incoming.notify = python:hgext.notify.hook
21 # batch emails when many changesets incoming at one time
21 # batch emails when many changesets incoming at one time
22 changegroup.notify = python:hgext.notify.hook
22 changegroup.notify = python:hgext.notify.hook
23
23
24 [notify]
24 [notify]
25 # config items go in here
25 # config items go here
26
26
27 config items:
27 Required configuration items:
28
28
29 REQUIRED:
30 config = /path/to/file # file containing subscriptions
29 config = /path/to/file # file containing subscriptions
31
30
32 OPTIONAL:
31 Optional configuration items:
32
33 test = True # print messages to stdout for testing
33 test = True # print messages to stdout for testing
34 strip = 3 # number of slashes to strip for url paths
34 strip = 3 # number of slashes to strip for url paths
35 domain = example.com # domain to use if committer missing domain
35 domain = example.com # domain to use if committer missing domain
36 style = ... # style file to use when formatting email
36 style = ... # style file to use when formatting email
37 template = ... # template to use when formatting email
37 template = ... # template to use when formatting email
38 incoming = ... # template to use when run as incoming hook
38 incoming = ... # template to use when run as incoming hook
39 changegroup = ... # template when run as changegroup hook
39 changegroup = ... # template when run as changegroup hook
40 maxdiff = 300 # max lines of diffs to include (0=none, -1=all)
40 maxdiff = 300 # max lines of diffs to include (0=none, -1=all)
41 maxsubject = 67 # truncate subject line longer than this
41 maxsubject = 67 # truncate subject line longer than this
42 diffstat = True # add a diffstat before the diff content
42 diffstat = True # add a diffstat before the diff content
43 sources = serve # notify if source of incoming changes in this list
43 sources = serve # notify if source of incoming changes in this list
44 # (serve == ssh or http, push, pull, bundle)
44 # (serve == ssh or http, push, pull, bundle)
45 [email]
45 [email]
46 from = user@host.com # email address to send as if none given
46 from = user@host.com # email address to send as if none given
47 [web]
47 [web]
48 baseurl = http://hgserver/... # root of hg web site for browsing commits
48 baseurl = http://hgserver/... # root of hg web site for browsing commits
49
49
50 notify config file has same format as regular hgrc. it has two sections so
50 The notify config file has same format as a regular hgrc file. It has two
51 you can express subscriptions in whatever way is handier for you.
51 sections so you can express subscriptions in whatever way is handier for you.
52
52
53 [usersubs]
53 [usersubs]
54 # key is subscriber email, value is ","-separated list of glob patterns
54 # key is subscriber email, value is ","-separated list of glob patterns
55 user@host = pattern
55 user@host = pattern
56
56
57 [reposubs]
57 [reposubs]
58 # key is glob pattern, value is ","-separated list of subscriber emails
58 # key is glob pattern, value is ","-separated list of subscriber emails
59 pattern = user@host
59 pattern = user@host
60
60
61 glob patterns are matched against path to repository root.
61 Glob patterns are matched against path to repository root.
62
62
63 if you like, you can put notify config file in repository that users can push
63 If you like, you can put notify config file in repository that users can push
64 changes to, they can manage their own subscriptions.
64 changes to, they can manage their own subscriptions.
65 '''
65 '''
66
66
67 from mercurial.i18n import _
67 from mercurial.i18n import _
68 from mercurial import patch, cmdutil, templater, util, mail
68 from mercurial import patch, cmdutil, templater, util, mail
69 import email.Parser, fnmatch, socket, time
69 import email.Parser, fnmatch, socket, time
70
70
71 # template for single changeset can include email headers.
71 # template for single changeset can include email headers.
72 single_template = '''
72 single_template = '''
73 Subject: changeset in {webroot}: {desc|firstline|strip}
73 Subject: changeset in {webroot}: {desc|firstline|strip}
74 From: {author}
74 From: {author}
75
75
76 changeset {node|short} in {root}
76 changeset {node|short} in {root}
77 details: {baseurl}{webroot}?cmd=changeset;node={node|short}
77 details: {baseurl}{webroot}?cmd=changeset;node={node|short}
78 description:
78 description:
79 \t{desc|tabindent|strip}
79 \t{desc|tabindent|strip}
80 '''.lstrip()
80 '''.lstrip()
81
81
82 # template for multiple changesets should not contain email headers,
82 # template for multiple changesets should not contain email headers,
83 # because only first set of headers will be used and result will look
83 # because only first set of headers will be used and result will look
84 # strange.
84 # strange.
85 multiple_template = '''
85 multiple_template = '''
86 changeset {node|short} in {root}
86 changeset {node|short} in {root}
87 details: {baseurl}{webroot}?cmd=changeset;node={node|short}
87 details: {baseurl}{webroot}?cmd=changeset;node={node|short}
88 summary: {desc|firstline}
88 summary: {desc|firstline}
89 '''
89 '''
90
90
91 deftemplates = {
91 deftemplates = {
92 'changegroup': multiple_template,
92 'changegroup': multiple_template,
93 }
93 }
94
94
95 class notifier(object):
95 class notifier(object):
96 '''email notification class.'''
96 '''email notification class.'''
97
97
98 def __init__(self, ui, repo, hooktype):
98 def __init__(self, ui, repo, hooktype):
99 self.ui = ui
99 self.ui = ui
100 cfg = self.ui.config('notify', 'config')
100 cfg = self.ui.config('notify', 'config')
101 if cfg:
101 if cfg:
102 self.ui.readconfig(cfg, sections=['usersubs', 'reposubs'])
102 self.ui.readconfig(cfg, sections=['usersubs', 'reposubs'])
103 self.repo = repo
103 self.repo = repo
104 self.stripcount = int(self.ui.config('notify', 'strip', 0))
104 self.stripcount = int(self.ui.config('notify', 'strip', 0))
105 self.root = self.strip(self.repo.root)
105 self.root = self.strip(self.repo.root)
106 self.domain = self.ui.config('notify', 'domain')
106 self.domain = self.ui.config('notify', 'domain')
107 self.test = self.ui.configbool('notify', 'test', True)
107 self.test = self.ui.configbool('notify', 'test', True)
108 self.charsets = mail._charsets(self.ui)
108 self.charsets = mail._charsets(self.ui)
109 self.subs = self.subscribers()
109 self.subs = self.subscribers()
110
110
111 mapfile = self.ui.config('notify', 'style')
111 mapfile = self.ui.config('notify', 'style')
112 template = (self.ui.config('notify', hooktype) or
112 template = (self.ui.config('notify', hooktype) or
113 self.ui.config('notify', 'template'))
113 self.ui.config('notify', 'template'))
114 self.t = cmdutil.changeset_templater(self.ui, self.repo,
114 self.t = cmdutil.changeset_templater(self.ui, self.repo,
115 False, None, mapfile, False)
115 False, None, mapfile, False)
116 if not mapfile and not template:
116 if not mapfile and not template:
117 template = deftemplates.get(hooktype) or single_template
117 template = deftemplates.get(hooktype) or single_template
118 if template:
118 if template:
119 template = templater.parsestring(template, quoted=False)
119 template = templater.parsestring(template, quoted=False)
120 self.t.use_template(template)
120 self.t.use_template(template)
121
121
122 def strip(self, path):
122 def strip(self, path):
123 '''strip leading slashes from local path, turn into web-safe path.'''
123 '''strip leading slashes from local path, turn into web-safe path.'''
124
124
125 path = util.pconvert(path)
125 path = util.pconvert(path)
126 count = self.stripcount
126 count = self.stripcount
127 while count > 0:
127 while count > 0:
128 c = path.find('/')
128 c = path.find('/')
129 if c == -1:
129 if c == -1:
130 break
130 break
131 path = path[c+1:]
131 path = path[c+1:]
132 count -= 1
132 count -= 1
133 return path
133 return path
134
134
135 def fixmail(self, addr):
135 def fixmail(self, addr):
136 '''try to clean up email addresses.'''
136 '''try to clean up email addresses.'''
137
137
138 addr = util.email(addr.strip())
138 addr = util.email(addr.strip())
139 if self.domain:
139 if self.domain:
140 a = addr.find('@localhost')
140 a = addr.find('@localhost')
141 if a != -1:
141 if a != -1:
142 addr = addr[:a]
142 addr = addr[:a]
143 if '@' not in addr:
143 if '@' not in addr:
144 return addr + '@' + self.domain
144 return addr + '@' + self.domain
145 return addr
145 return addr
146
146
147 def subscribers(self):
147 def subscribers(self):
148 '''return list of email addresses of subscribers to this repo.'''
148 '''return list of email addresses of subscribers to this repo.'''
149 subs = set()
149 subs = set()
150 for user, pats in self.ui.configitems('usersubs'):
150 for user, pats in self.ui.configitems('usersubs'):
151 for pat in pats.split(','):
151 for pat in pats.split(','):
152 if fnmatch.fnmatch(self.repo.root, pat.strip()):
152 if fnmatch.fnmatch(self.repo.root, pat.strip()):
153 subs.add(self.fixmail(user))
153 subs.add(self.fixmail(user))
154 for pat, users in self.ui.configitems('reposubs'):
154 for pat, users in self.ui.configitems('reposubs'):
155 if fnmatch.fnmatch(self.repo.root, pat):
155 if fnmatch.fnmatch(self.repo.root, pat):
156 for user in users.split(','):
156 for user in users.split(','):
157 subs.add(self.fixmail(user))
157 subs.add(self.fixmail(user))
158 return [mail.addressencode(self.ui, s, self.charsets, self.test)
158 return [mail.addressencode(self.ui, s, self.charsets, self.test)
159 for s in sorted(subs)]
159 for s in sorted(subs)]
160
160
161 def url(self, path=None):
161 def url(self, path=None):
162 return self.ui.config('web', 'baseurl') + (path or self.root)
162 return self.ui.config('web', 'baseurl') + (path or self.root)
163
163
164 def node(self, ctx):
164 def node(self, ctx):
165 '''format one changeset.'''
165 '''format one changeset.'''
166 self.t.show(ctx, changes=ctx.changeset(),
166 self.t.show(ctx, changes=ctx.changeset(),
167 baseurl=self.ui.config('web', 'baseurl'),
167 baseurl=self.ui.config('web', 'baseurl'),
168 root=self.repo.root, webroot=self.root)
168 root=self.repo.root, webroot=self.root)
169
169
170 def skipsource(self, source):
170 def skipsource(self, source):
171 '''true if incoming changes from this source should be skipped.'''
171 '''true if incoming changes from this source should be skipped.'''
172 ok_sources = self.ui.config('notify', 'sources', 'serve').split()
172 ok_sources = self.ui.config('notify', 'sources', 'serve').split()
173 return source not in ok_sources
173 return source not in ok_sources
174
174
175 def send(self, ctx, count, data):
175 def send(self, ctx, count, data):
176 '''send message.'''
176 '''send message.'''
177
177
178 p = email.Parser.Parser()
178 p = email.Parser.Parser()
179 msg = p.parsestr(data)
179 msg = p.parsestr(data)
180
180
181 # store sender and subject
181 # store sender and subject
182 sender, subject = msg['From'], msg['Subject']
182 sender, subject = msg['From'], msg['Subject']
183 del msg['From'], msg['Subject']
183 del msg['From'], msg['Subject']
184 # store remaining headers
184 # store remaining headers
185 headers = msg.items()
185 headers = msg.items()
186 # create fresh mime message from msg body
186 # create fresh mime message from msg body
187 text = msg.get_payload()
187 text = msg.get_payload()
188 # for notification prefer readability over data precision
188 # for notification prefer readability over data precision
189 msg = mail.mimeencode(self.ui, text, self.charsets, self.test)
189 msg = mail.mimeencode(self.ui, text, self.charsets, self.test)
190 # reinstate custom headers
190 # reinstate custom headers
191 for k, v in headers:
191 for k, v in headers:
192 msg[k] = v
192 msg[k] = v
193
193
194 msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")
194 msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")
195
195
196 # try to make subject line exist and be useful
196 # try to make subject line exist and be useful
197 if not subject:
197 if not subject:
198 if count > 1:
198 if count > 1:
199 subject = _('%s: %d new changesets') % (self.root, count)
199 subject = _('%s: %d new changesets') % (self.root, count)
200 else:
200 else:
201 s = ctx.description().lstrip().split('\n', 1)[0].rstrip()
201 s = ctx.description().lstrip().split('\n', 1)[0].rstrip()
202 subject = '%s: %s' % (self.root, s)
202 subject = '%s: %s' % (self.root, s)
203 maxsubject = int(self.ui.config('notify', 'maxsubject', 67))
203 maxsubject = int(self.ui.config('notify', 'maxsubject', 67))
204 if maxsubject and len(subject) > maxsubject:
204 if maxsubject and len(subject) > maxsubject:
205 subject = subject[:maxsubject-3] + '...'
205 subject = subject[:maxsubject-3] + '...'
206 msg['Subject'] = mail.headencode(self.ui, subject,
206 msg['Subject'] = mail.headencode(self.ui, subject,
207 self.charsets, self.test)
207 self.charsets, self.test)
208
208
209 # try to make message have proper sender
209 # try to make message have proper sender
210 if not sender:
210 if not sender:
211 sender = self.ui.config('email', 'from') or self.ui.username()
211 sender = self.ui.config('email', 'from') or self.ui.username()
212 if '@' not in sender or '@localhost' in sender:
212 if '@' not in sender or '@localhost' in sender:
213 sender = self.fixmail(sender)
213 sender = self.fixmail(sender)
214 msg['From'] = mail.addressencode(self.ui, sender,
214 msg['From'] = mail.addressencode(self.ui, sender,
215 self.charsets, self.test)
215 self.charsets, self.test)
216
216
217 msg['X-Hg-Notification'] = 'changeset %s' % ctx
217 msg['X-Hg-Notification'] = 'changeset %s' % ctx
218 if not msg['Message-Id']:
218 if not msg['Message-Id']:
219 msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' %
219 msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' %
220 (ctx, int(time.time()),
220 (ctx, int(time.time()),
221 hash(self.repo.root), socket.getfqdn()))
221 hash(self.repo.root), socket.getfqdn()))
222 msg['To'] = ', '.join(self.subs)
222 msg['To'] = ', '.join(self.subs)
223
223
224 msgtext = msg.as_string(0)
224 msgtext = msg.as_string(0)
225 if self.test:
225 if self.test:
226 self.ui.write(msgtext)
226 self.ui.write(msgtext)
227 if not msgtext.endswith('\n'):
227 if not msgtext.endswith('\n'):
228 self.ui.write('\n')
228 self.ui.write('\n')
229 else:
229 else:
230 self.ui.status(_('notify: sending %d subscribers %d changes\n') %
230 self.ui.status(_('notify: sending %d subscribers %d changes\n') %
231 (len(self.subs), count))
231 (len(self.subs), count))
232 mail.sendmail(self.ui, util.email(msg['From']),
232 mail.sendmail(self.ui, util.email(msg['From']),
233 self.subs, msgtext)
233 self.subs, msgtext)
234
234
235 def diff(self, ctx, ref=None):
235 def diff(self, ctx, ref=None):
236
236
237 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
237 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
238 prev = ctx.parents()[0].node()
238 prev = ctx.parents()[0].node()
239 ref = ref and ref.node() or ctx.node()
239 ref = ref and ref.node() or ctx.node()
240 chunks = patch.diff(self.repo, prev, ref, opts=patch.diffopts(self.ui))
240 chunks = patch.diff(self.repo, prev, ref, opts=patch.diffopts(self.ui))
241 difflines = ''.join(chunks).splitlines()
241 difflines = ''.join(chunks).splitlines()
242
242
243 if self.ui.configbool('notify', 'diffstat', True):
243 if self.ui.configbool('notify', 'diffstat', True):
244 s = patch.diffstat(difflines)
244 s = patch.diffstat(difflines)
245 # s may be nil, don't include the header if it is
245 # s may be nil, don't include the header if it is
246 if s:
246 if s:
247 self.ui.write('\ndiffstat:\n\n%s' % s)
247 self.ui.write('\ndiffstat:\n\n%s' % s)
248
248
249 if maxdiff == 0:
249 if maxdiff == 0:
250 return
250 return
251 elif maxdiff > 0 and len(difflines) > maxdiff:
251 elif maxdiff > 0 and len(difflines) > maxdiff:
252 msg = _('\ndiffs (truncated from %d to %d lines):\n\n')
252 msg = _('\ndiffs (truncated from %d to %d lines):\n\n')
253 self.ui.write(msg % (len(difflines), maxdiff))
253 self.ui.write(msg % (len(difflines), maxdiff))
254 difflines = difflines[:maxdiff]
254 difflines = difflines[:maxdiff]
255 elif difflines:
255 elif difflines:
256 self.ui.write(_('\ndiffs (%d lines):\n\n') % len(difflines))
256 self.ui.write(_('\ndiffs (%d lines):\n\n') % len(difflines))
257
257
258 self.ui.write("\n".join(difflines))
258 self.ui.write("\n".join(difflines))
259
259
260 def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
260 def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
261 '''send email notifications to interested subscribers.
261 '''send email notifications to interested subscribers.
262
262
263 if used as changegroup hook, send one email for all changesets in
263 if used as changegroup hook, send one email for all changesets in
264 changegroup. else send one email per changeset.'''
264 changegroup. else send one email per changeset.'''
265
265
266 n = notifier(ui, repo, hooktype)
266 n = notifier(ui, repo, hooktype)
267 ctx = repo[node]
267 ctx = repo[node]
268
268
269 if not n.subs:
269 if not n.subs:
270 ui.debug(_('notify: no subscribers to repository %s\n') % n.root)
270 ui.debug(_('notify: no subscribers to repository %s\n') % n.root)
271 return
271 return
272 if n.skipsource(source):
272 if n.skipsource(source):
273 ui.debug(_('notify: changes have source "%s" - skipping\n') % source)
273 ui.debug(_('notify: changes have source "%s" - skipping\n') % source)
274 return
274 return
275
275
276 ui.pushbuffer()
276 ui.pushbuffer()
277 if hooktype == 'changegroup':
277 if hooktype == 'changegroup':
278 start, end = ctx.rev(), len(repo)
278 start, end = ctx.rev(), len(repo)
279 count = end - start
279 count = end - start
280 for rev in xrange(start, end):
280 for rev in xrange(start, end):
281 n.node(repo[rev])
281 n.node(repo[rev])
282 n.diff(ctx, repo['tip'])
282 n.diff(ctx, repo['tip'])
283 else:
283 else:
284 count = 1
284 count = 1
285 n.node(ctx)
285 n.node(ctx)
286 n.diff(ctx)
286 n.diff(ctx)
287
287
288 data = ui.popbuffer()
288 data = ui.popbuffer()
289 n.send(ctx, count, data)
289 n.send(ctx, count, data)
@@ -1,218 +1,218 b''
1 notify extension - hooks for sending email notifications at commit/push time
1 notify extension - hooks for sending email notifications at commit/push time
2
2
3 Subscriptions can be managed through hgrc. Default mode is to print messages
3 Subscriptions can be managed through a hgrc file. Default mode is to print
4 to stdout, for testing and configuring.
4 messages to stdout, for testing and configuring.
5
5
6 To use, configure notify extension and enable in hgrc like this:
6 To use, configure the notify extension and enable it in hgrc like this:
7
7
8 [extensions]
8 [extensions]
9 hgext.notify =
9 hgext.notify =
10
10
11 [hooks]
11 [hooks]
12 # one email for each incoming changeset
12 # one email for each incoming changeset
13 incoming.notify = python:hgext.notify.hook
13 incoming.notify = python:hgext.notify.hook
14 # batch emails when many changesets incoming at one time
14 # batch emails when many changesets incoming at one time
15 changegroup.notify = python:hgext.notify.hook
15 changegroup.notify = python:hgext.notify.hook
16
16
17 [notify]
17 [notify]
18 # config items go in here
18 # config items go here
19
19
20 config items:
20 Required configuration items:
21
21
22 REQUIRED:
23 config = /path/to/file # file containing subscriptions
22 config = /path/to/file # file containing subscriptions
24
23
25 OPTIONAL:
24 Optional configuration items:
25
26 test = True # print messages to stdout for testing
26 test = True # print messages to stdout for testing
27 strip = 3 # number of slashes to strip for url paths
27 strip = 3 # number of slashes to strip for url paths
28 domain = example.com # domain to use if committer missing domain
28 domain = example.com # domain to use if committer missing domain
29 style = ... # style file to use when formatting email
29 style = ... # style file to use when formatting email
30 template = ... # template to use when formatting email
30 template = ... # template to use when formatting email
31 incoming = ... # template to use when run as incoming hook
31 incoming = ... # template to use when run as incoming hook
32 changegroup = ... # template when run as changegroup hook
32 changegroup = ... # template when run as changegroup hook
33 maxdiff = 300 # max lines of diffs to include (0=none, -1=all)
33 maxdiff = 300 # max lines of diffs to include (0=none, -1=all)
34 maxsubject = 67 # truncate subject line longer than this
34 maxsubject = 67 # truncate subject line longer than this
35 diffstat = True # add a diffstat before the diff content
35 diffstat = True # add a diffstat before the diff content
36 sources = serve # notify if source of incoming changes in this list
36 sources = serve # notify if source of incoming changes in this list
37 # (serve == ssh or http, push, pull, bundle)
37 # (serve == ssh or http, push, pull, bundle)
38 [email]
38 [email]
39 from = user@host.com # email address to send as if none given
39 from = user@host.com # email address to send as if none given
40 [web]
40 [web]
41 baseurl = http://hgserver/... # root of hg web site for browsing commits
41 baseurl = http://hgserver/... # root of hg web site for browsing commits
42
42
43 notify config file has same format as regular hgrc. it has two sections so
43 The notify config file has same format as a regular hgrc file. It has two
44 you can express subscriptions in whatever way is handier for you.
44 sections so you can express subscriptions in whatever way is handier for you.
45
45
46 [usersubs]
46 [usersubs]
47 # key is subscriber email, value is ","-separated list of glob patterns
47 # key is subscriber email, value is ","-separated list of glob patterns
48 user@host = pattern
48 user@host = pattern
49
49
50 [reposubs]
50 [reposubs]
51 # key is glob pattern, value is ","-separated list of subscriber emails
51 # key is glob pattern, value is ","-separated list of subscriber emails
52 pattern = user@host
52 pattern = user@host
53
53
54 glob patterns are matched against path to repository root.
54 Glob patterns are matched against path to repository root.
55
55
56 if you like, you can put notify config file in repository that users can push
56 If you like, you can put notify config file in repository that users can push
57 changes to, they can manage their own subscriptions.
57 changes to, they can manage their own subscriptions.
58
58
59 no commands defined
59 no commands defined
60 % commit
60 % commit
61 adding a
61 adding a
62 % clone
62 % clone
63 updating working directory
63 updating working directory
64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 % commit
65 % commit
66 % pull (minimal config)
66 % pull (minimal config)
67 pulling from ../a
67 pulling from ../a
68 searching for changes
68 searching for changes
69 adding changesets
69 adding changesets
70 adding manifests
70 adding manifests
71 adding file changes
71 adding file changes
72 added 1 changesets with 1 changes to 1 files
72 added 1 changesets with 1 changes to 1 files
73 Content-Type: text/plain; charset="us-ascii"
73 Content-Type: text/plain; charset="us-ascii"
74 MIME-Version: 1.0
74 MIME-Version: 1.0
75 Content-Transfer-Encoding: 7bit
75 Content-Transfer-Encoding: 7bit
76 Date:
76 Date:
77 Subject: changeset in test-notify/b: b
77 Subject: changeset in test-notify/b: b
78 From: test
78 From: test
79 X-Hg-Notification: changeset 0647d048b600
79 X-Hg-Notification: changeset 0647d048b600
80 Message-Id:
80 Message-Id:
81 To: baz, foo@bar
81 To: baz, foo@bar
82
82
83 changeset 0647d048b600 in test-notify/b
83 changeset 0647d048b600 in test-notify/b
84 details: test-notify/b?cmd=changeset;node=0647d048b600
84 details: test-notify/b?cmd=changeset;node=0647d048b600
85 description: b
85 description: b
86
86
87 diffs (6 lines):
87 diffs (6 lines):
88
88
89 diff -r cb9a9f314b8b -r 0647d048b600 a
89 diff -r cb9a9f314b8b -r 0647d048b600 a
90 --- a/a Thu Jan 01 00:00:00 1970 +0000
90 --- a/a Thu Jan 01 00:00:00 1970 +0000
91 +++ b/a Thu Jan 01 00:00:01 1970 +0000
91 +++ b/a Thu Jan 01 00:00:01 1970 +0000
92 @@ -1,1 +1,2 @@
92 @@ -1,1 +1,2 @@
93 a
93 a
94 +a
94 +a
95 (run 'hg update' to get a working copy)
95 (run 'hg update' to get a working copy)
96 % fail for config file is missing
96 % fail for config file is missing
97 rolling back last transaction
97 rolling back last transaction
98 pull failed
98 pull failed
99 % pull
99 % pull
100 rolling back last transaction
100 rolling back last transaction
101 pulling from ../a
101 pulling from ../a
102 searching for changes
102 searching for changes
103 adding changesets
103 adding changesets
104 adding manifests
104 adding manifests
105 adding file changes
105 adding file changes
106 added 1 changesets with 1 changes to 1 files
106 added 1 changesets with 1 changes to 1 files
107 Content-Type: text/plain; charset="us-ascii"
107 Content-Type: text/plain; charset="us-ascii"
108 MIME-Version: 1.0
108 MIME-Version: 1.0
109 Content-Transfer-Encoding: 7bit
109 Content-Transfer-Encoding: 7bit
110 X-Test: foo
110 X-Test: foo
111 Date:
111 Date:
112 Subject: b
112 Subject: b
113 From: test@test.com
113 From: test@test.com
114 X-Hg-Notification: changeset 0647d048b600
114 X-Hg-Notification: changeset 0647d048b600
115 Message-Id:
115 Message-Id:
116 To: baz@test.com, foo@bar
116 To: baz@test.com, foo@bar
117
117
118 changeset 0647d048b600
118 changeset 0647d048b600
119 description:
119 description:
120 b
120 b
121 diffs (6 lines):
121 diffs (6 lines):
122
122
123 diff -r cb9a9f314b8b -r 0647d048b600 a
123 diff -r cb9a9f314b8b -r 0647d048b600 a
124 --- a/a Thu Jan 01 00:00:00 1970 +0000
124 --- a/a Thu Jan 01 00:00:00 1970 +0000
125 +++ b/a Thu Jan 01 00:00:01 1970 +0000
125 +++ b/a Thu Jan 01 00:00:01 1970 +0000
126 @@ -1,1 +1,2 @@
126 @@ -1,1 +1,2 @@
127 a
127 a
128 +a
128 +a
129 (run 'hg update' to get a working copy)
129 (run 'hg update' to get a working copy)
130 % pull
130 % pull
131 rolling back last transaction
131 rolling back last transaction
132 pulling from ../a
132 pulling from ../a
133 searching for changes
133 searching for changes
134 adding changesets
134 adding changesets
135 adding manifests
135 adding manifests
136 adding file changes
136 adding file changes
137 added 1 changesets with 1 changes to 1 files
137 added 1 changesets with 1 changes to 1 files
138 Content-Type: text/plain; charset="us-ascii"
138 Content-Type: text/plain; charset="us-ascii"
139 MIME-Version: 1.0
139 MIME-Version: 1.0
140 Content-Transfer-Encoding: 7bit
140 Content-Transfer-Encoding: 7bit
141 X-Test: foo
141 X-Test: foo
142 Date:
142 Date:
143 Subject: b
143 Subject: b
144 From: test@test.com
144 From: test@test.com
145 X-Hg-Notification: changeset 0647d048b600
145 X-Hg-Notification: changeset 0647d048b600
146 Message-Id:
146 Message-Id:
147 To: baz@test.com, foo@bar
147 To: baz@test.com, foo@bar
148
148
149 changeset 0647d048b600
149 changeset 0647d048b600
150 description:
150 description:
151 b
151 b
152 diffstat:
152 diffstat:
153
153
154 a | 1 +
154 a | 1 +
155 1 files changed, 1 insertions(+), 0 deletions(-)
155 1 files changed, 1 insertions(+), 0 deletions(-)
156
156
157 diffs (6 lines):
157 diffs (6 lines):
158
158
159 diff -r cb9a9f314b8b -r 0647d048b600 a
159 diff -r cb9a9f314b8b -r 0647d048b600 a
160 --- a/a Thu Jan 01 00:00:00 1970 +0000
160 --- a/a Thu Jan 01 00:00:00 1970 +0000
161 +++ b/a Thu Jan 01 00:00:01 1970 +0000
161 +++ b/a Thu Jan 01 00:00:01 1970 +0000
162 @@ -1,1 +1,2 @@
162 @@ -1,1 +1,2 @@
163 a
163 a
164 +a
164 +a
165 (run 'hg update' to get a working copy)
165 (run 'hg update' to get a working copy)
166 % test merge
166 % test merge
167 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
167 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
168 created new head
168 created new head
169 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 (branch merge, don't forget to commit)
170 (branch merge, don't forget to commit)
171 pulling from ../a
171 pulling from ../a
172 searching for changes
172 searching for changes
173 adding changesets
173 adding changesets
174 adding manifests
174 adding manifests
175 adding file changes
175 adding file changes
176 added 2 changesets with 0 changes to 1 files
176 added 2 changesets with 0 changes to 1 files
177 Content-Type: text/plain; charset="us-ascii"
177 Content-Type: text/plain; charset="us-ascii"
178 MIME-Version: 1.0
178 MIME-Version: 1.0
179 Content-Transfer-Encoding: 7bit
179 Content-Transfer-Encoding: 7bit
180 X-Test: foo
180 X-Test: foo
181 Date:
181 Date:
182 Subject: adda2
182 Subject: adda2
183 From: test@test.com
183 From: test@test.com
184 X-Hg-Notification: changeset 0a184ce6067f
184 X-Hg-Notification: changeset 0a184ce6067f
185 Message-Id:
185 Message-Id:
186 To: baz@test.com, foo@bar
186 To: baz@test.com, foo@bar
187
187
188 changeset 0a184ce6067f
188 changeset 0a184ce6067f
189 description:
189 description:
190 adda2
190 adda2
191 diffstat:
191 diffstat:
192
192
193 a | 1 +
193 a | 1 +
194 1 files changed, 1 insertions(+), 0 deletions(-)
194 1 files changed, 1 insertions(+), 0 deletions(-)
195
195
196 diffs (6 lines):
196 diffs (6 lines):
197
197
198 diff -r cb9a9f314b8b -r 0a184ce6067f a
198 diff -r cb9a9f314b8b -r 0a184ce6067f a
199 --- a/a Thu Jan 01 00:00:00 1970 +0000
199 --- a/a Thu Jan 01 00:00:00 1970 +0000
200 +++ b/a Thu Jan 01 00:00:02 1970 +0000
200 +++ b/a Thu Jan 01 00:00:02 1970 +0000
201 @@ -1,1 +1,2 @@
201 @@ -1,1 +1,2 @@
202 a
202 a
203 +a
203 +a
204 Content-Type: text/plain; charset="us-ascii"
204 Content-Type: text/plain; charset="us-ascii"
205 MIME-Version: 1.0
205 MIME-Version: 1.0
206 Content-Transfer-Encoding: 7bit
206 Content-Transfer-Encoding: 7bit
207 X-Test: foo
207 X-Test: foo
208 Date:
208 Date:
209 Subject: merge
209 Subject: merge
210 From: test@test.com
210 From: test@test.com
211 X-Hg-Notification: changeset 22c88b85aa27
211 X-Hg-Notification: changeset 22c88b85aa27
212 Message-Id:
212 Message-Id:
213 To: baz@test.com, foo@bar
213 To: baz@test.com, foo@bar
214
214
215 changeset 22c88b85aa27
215 changeset 22c88b85aa27
216 description:
216 description:
217 merge
217 merge
218 (run 'hg update' to get a working copy)
218 (run 'hg update' to get a working copy)
General Comments 0
You need to be logged in to leave comments. Login now