##// END OF EJS Templates
redo merge with mpm....
Vadim Gelfer -
r2921:addb58e3 merge default
parent child Browse files
Show More
@@ -0,0 +1,68 b''
1 # mail.py - mail sending bits for mercurial
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from i18n import gettext as _
9 from demandload import *
10 demandload(globals(), "os re smtplib templater util")
11
12 def _smtp(ui):
13 '''send mail using smtp.'''
14
15 local_hostname = ui.config('smtp', 'local_hostname')
16 s = smtplib.SMTP(local_hostname=local_hostname)
17 mailhost = ui.config('smtp', 'host')
18 if not mailhost:
19 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
20 mailport = int(ui.config('smtp', 'port', 25))
21 self.note(_('sending mail: smtp host %s, port %s\n') %
22 (mailhost, mailport))
23 s.connect(host=mailhost, port=mailport)
24 if ui.configbool('smtp', 'tls'):
25 ui.note(_('(using tls)\n'))
26 s.ehlo()
27 s.starttls()
28 s.ehlo()
29 username = ui.config('smtp', 'username')
30 password = ui.config('smtp', 'password')
31 if username and password:
32 ui.note(_('(authenticating to mail server as %s)\n') %
33 (username))
34 s.login(username, password)
35 return s
36
37 class _sendmail(object):
38 '''send mail using sendmail.'''
39
40 def __init__(self, ui, program):
41 self.ui = ui
42 self.program = program
43
44 def sendmail(self, sender, recipients, msg):
45 cmdline = '%s -f %s %s' % (
46 self.program, templater.email(sender),
47 ' '.join(map(templater.email, recipients)))
48 self.ui.note(_('sending mail: %s\n') % cmdline)
49 fp = os.popen(cmdline, 'w')
50 fp.write(msg)
51 ret = fp.close()
52 if ret:
53 raise util.Abort('%s %s' % (
54 os.path.basename(self.program.split(None, 1)[0]),
55 util.explain_exit(ret)[0]))
56
57 def connect(ui):
58 '''make a mail connection. object returned has one method, sendmail.
59 call as sendmail(sender, list-of-recipients, msg).'''
60
61 method = ui.config('email', 'method', 'smtp')
62 if method == 'smtp':
63 return smtp(ui)
64
65 return sendmail(ui, method)
66
67 def sendmail(ui, sender, recipients, msg):
68 return connect(ui).sendmail(sender, recipients, msg)
@@ -77,7 +77,7 b' class queue:'
77 77
78 78 def diffopts(self):
79 79 if self._diffopts is None:
80 self._diffopts = self.ui.diffopts()
80 self._diffopts = patch.diffopts(self.ui)
81 81 return self._diffopts
82 82
83 83 def join(self, *p):
@@ -67,8 +67,8 b''
67 67 from mercurial.demandload import *
68 68 from mercurial.i18n import gettext as _
69 69 from mercurial.node import *
70 demandload(globals(), 'email.Parser mercurial:commands,patch,templater,util')
71 demandload(globals(), 'fnmatch socket time')
70 demandload(globals(), 'mercurial:commands,patch,templater,util,mail')
71 demandload(globals(), 'email.Parser fnmatch socket time')
72 72
73 73 # template for single changeset can include email headers.
74 74 single_template = '''
@@ -229,8 +229,8 b' class notifier(object):'
229 229 else:
230 230 self.ui.status(_('notify: sending %d subscribers %d changes\n') %
231 231 (len(self.subs), count))
232 mail = self.ui.sendmail()
233 mail.sendmail(templater.email(msg['From']), self.subs, msgtext)
232 mail.sendmail(self.ui, templater.email(msg['From']),
233 self.subs, msgtext)
234 234
235 235 def diff(self, node, ref):
236 236 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
@@ -241,7 +241,7 b' def patchbomb(ui, repo, *revs, **opts):'
241 241 ui.write('\n')
242 242
243 243 if not opts['test'] and not opts['mbox']:
244 mail = ui.sendmail()
244 mailer = mail.connect(ui)
245 245 parent = None
246 246
247 247 # Calculate UTC offset
@@ -290,7 +290,7 b' def patchbomb(ui, repo, *revs, **opts):'
290 290 ui.status('Sending ', m['Subject'], ' ...\n')
291 291 # Exim does not remove the Bcc field
292 292 del m['Bcc']
293 mail.sendmail(sender, to + bcc + cc, m.as_string(0))
293 mailer.sendmail(sender, to + bcc + cc, m.as_string(0))
294 294
295 295 cmdtable = {
296 296 'email':
@@ -1347,7 +1347,7 b' def diff(ui, repo, *pats, **opts):'
1347 1347 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1348 1348
1349 1349 patch.diff(repo, node1, node2, fns, match=matchfn,
1350 opts=ui.diffopts(opts))
1350 opts=patch.diffopts(ui, opts))
1351 1351
1352 1352 def export(ui, repo, *changesets, **opts):
1353 1353 """dump the header and diffs for one or more changesets
@@ -1384,7 +1384,8 b' def export(ui, repo, *changesets, **opts'
1384 1384 else:
1385 1385 ui.note(_('exporting patch:\n'))
1386 1386 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1387 switch_parent=opts['switch_parent'], opts=ui.diffopts(opts))
1387 switch_parent=opts['switch_parent'],
1388 opts=patch.diffopts(ui, opts))
1388 1389
1389 1390 def forget(ui, repo, *pats, **opts):
1390 1391 """don't add the specified files on the next commit (DEPRECATED)
@@ -65,26 +65,25 b' class filelog(revlog):'
65 65 return (m["copy"], bin(m["copyrev"]))
66 66 return False
67 67
68 def size(self, rev):
69 """return the size of a given revision"""
70
71 # for revisions with renames, we have to go the slow way
72 node = self.node(rev)
73 if self.renamed(node):
74 return len(self.read(node))
75
76 return revlog.size(self, rev)
77
68 78 def cmp(self, node, text):
69 79 """compare text with a given file revision"""
70 80
71 81 # for renames, we have to go the slow way
72 82 if self.renamed(node):
73 83 t2 = self.read(node)
74 return t2 == text
75
76 p1, p2 = self.parents(node)
77 h = hash(text, p1, p2)
78
79 return h != node
84 return t2 != text
80 85
81 def makenode(self, node, text):
82 """calculate a file nodeid for text, descended or possibly
83 unchanged from node"""
84
85 if self.cmp(node, text):
86 return hash(text, node, nullid)
87 return node
86 return revlog.cmp(self, node, text)
88 87
89 88 def annotate(self, node):
90 89
@@ -11,7 +11,7 b' import os.path'
11 11 import mimetypes
12 12 from mercurial.demandload import demandload
13 13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch")
15 15 demandload(globals(), "mercurial:templater")
16 16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
17 17 from mercurial.node import *
@@ -134,7 +134,7 b' class hgweb(object):'
134 134 modified, added, removed = map(lambda x: filterfiles(files, x),
135 135 (modified, added, removed))
136 136
137 diffopts = self.repo.ui.diffopts()
137 diffopts = patch.diffopts(ui)
138 138 for f in modified:
139 139 to = r.file(f).read(mmap1[f])
140 140 tn = r.file(f).read(mmap2[f])
@@ -10,6 +10,11 b' from i18n import gettext as _'
10 10 from demandload import *
11 11 demandload(globals(), "util os tempfile")
12 12
13 def fmerge(f, local, other, ancestor):
14 """merge executable flags"""
15 a, b, c = ancestor.execf(f), local.execf(f), other.execf(f)
16 return ((a^b) | (a^c)) ^ a
17
13 18 def merge3(repo, fn, my, other, p1, p2):
14 19 """perform a 3-way merge in the working directory"""
15 20
@@ -90,9 +95,7 b' def update(repo, node, branchmerge=False'
90 95 if not force:
91 96 for f in unknown:
92 97 if f in m2:
93 t1 = repo.wread(f)
94 t2 = repo.file(f).read(m2[f])
95 if cmp(t1, t2) != 0:
98 if repo.file(f).cmp(m2[f], repo.wread(f)):
96 99 raise util.Abort(_("'%s' already exists in the working"
97 100 " dir and differs from remote") % f)
98 101
@@ -100,13 +103,14 b' def update(repo, node, branchmerge=False'
100 103 # we care about merging
101 104 repo.ui.note(_("resolving manifests\n"))
102 105 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") %
103 (overwrite, branchmerge, partial and True or False, linear_path))
106 (overwrite, branchmerge, bool(partial), linear_path))
104 107 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
105 108 (short(man), short(m1n), short(m2n)))
106 109
107 110 merge = {}
108 111 get = {}
109 112 remove = []
113 forget = []
110 114
111 115 # construct a working dir manifest
112 116 mw = m1.copy()
@@ -114,6 +118,11 b' def update(repo, node, branchmerge=False'
114 118
115 119 for f in added + modified + unknown:
116 120 mw[f] = ""
121 # is the wfile new and matches m2?
122 if (f not in m1 and f in m2 and
123 not repo.file(f).cmp(m2[f], repo.wread(f))):
124 mw[f] = m2[f]
125
117 126 mw.set(f, util.is_exec(repo.wjoin(f), mw.execf(f)))
118 127
119 128 for f in deleted + removed:
@@ -125,8 +134,8 b' def update(repo, node, branchmerge=False'
125 134 # the file, then we need to remove it from the dirstate, to
126 135 # prevent the dirstate from listing the file when it is no
127 136 # longer in the manifest.
128 if not partial and linear_path and f not in m2:
129 repo.dirstate.forget((f,))
137 if linear_path and f not in m2:
138 forget.append(f)
130 139
131 140 # Compare manifests
132 141 for f, n in mw.iteritems():
@@ -135,25 +144,13 b' def update(repo, node, branchmerge=False'
135 144 if f in m2:
136 145 s = 0
137 146
138 # is the wfile new since m1, and match m2?
139 if f not in m1:
140 t1 = repo.wread(f)
141 t2 = repo.file(f).read(m2[f])
142 if cmp(t1, t2) == 0:
143 n = m2[f]
144 del t1, t2
145
146 147 # are files different?
147 148 if n != m2[f]:
148 149 a = ma.get(f, nullid)
149 150 # are both different from the ancestor?
150 151 if n != a and m2[f] != a:
151 152 repo.ui.debug(_(" %s versions differ, resolve\n") % f)
152 # merge executable bits
153 # "if we changed or they changed, change in merge"
154 a, b, c = ma.execf(f), mw.execf(f), m2.execf(f)
155 mode = ((a^b) | (a^c)) ^ a
156 merge[f] = (mode, m1.get(f, nullid), m2[f])
153 merge[f] = (fmerge(f, mw, m2, ma), m1.get(f, nullid), m2[f])
157 154 s = 1
158 155 # are we clobbering?
159 156 # is remote's version newer?
@@ -172,9 +169,7 b' def update(repo, node, branchmerge=False'
172 169 repo.ui.debug(_(" updating permissions for %s\n") % f)
173 170 util.set_exec(repo.wjoin(f), m2.execf(f))
174 171 else:
175 a, b, c = ma.execf(f), mw.execf(f), m2.execf(f)
176 mode = ((a^b) | (a^c)) ^ a
177 if mode != b:
172 if fmerge(f, mw, m2, ma) != mw.execf(f):
178 173 repo.ui.debug(_(" updating permissions for %s\n")
179 174 % f)
180 175 util.set_exec(repo.wjoin(f), mode)
@@ -230,6 +225,8 b' def update(repo, node, branchmerge=False'
230 225
231 226 del mw, m1, m2, ma
232 227
228 ### apply phase
229
233 230 if overwrite:
234 231 for f in merge:
235 232 get[f] = merge[f][:2]
@@ -257,11 +254,6 b' def update(repo, node, branchmerge=False'
257 254 t = repo.file(f).read(node)
258 255 repo.wwrite(f, t)
259 256 util.set_exec(repo.wjoin(f), flag)
260 if not partial:
261 if branchmerge:
262 repo.dirstate.update([f], 'n', st_mtime=-1)
263 else:
264 repo.dirstate.update([f], 'n')
265 257
266 258 # merge the tricky bits
267 259 unresolved = []
@@ -274,19 +266,6 b' def update(repo, node, branchmerge=False'
274 266 if ret:
275 267 unresolved.append(f)
276 268 util.set_exec(repo.wjoin(f), flag)
277 if not partial:
278 if branchmerge:
279 # We've done a branch merge, mark this file as merged
280 # so that we properly record the merger later
281 repo.dirstate.update([f], 'm')
282 else:
283 # We've update-merged a locally modified file, so
284 # we set the dirstate to emulate a normal checkout
285 # of that file some time in the past. Thus our
286 # merge will appear as a normal local file
287 # modification.
288 f_len = len(repo.file(f).read(other))
289 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
290 269
291 270 remove.sort()
292 271 for f in remove:
@@ -298,14 +277,40 b' def update(repo, node, branchmerge=False'
298 277 if inst.errno != errno.ENOENT:
299 278 repo.ui.warn(_("update failed to remove %s: %s!\n") %
300 279 (f, inst.strerror))
280
281 # update dirstate
301 282 if not partial:
283 repo.dirstate.setparents(p1, p2)
284 repo.dirstate.forget(forget)
302 285 if branchmerge:
303 286 repo.dirstate.update(remove, 'r')
304 287 else:
305 288 repo.dirstate.forget(remove)
306 289
307 if not partial:
308 repo.dirstate.setparents(p1, p2)
290 files = get.keys()
291 files.sort()
292 for f in files:
293 if branchmerge:
294 repo.dirstate.update([f], 'n', st_mtime=-1)
295 else:
296 repo.dirstate.update([f], 'n')
297
298 files = merge.keys()
299 files.sort()
300 for f in files:
301 if branchmerge:
302 # We've done a branch merge, mark this file as merged
303 # so that we properly record the merger later
304 repo.dirstate.update([f], 'm')
305 else:
306 # We've update-merged a locally modified file, so
307 # we set the dirstate to emulate a normal checkout
308 # of that file some time in the past. Thus our
309 # merge will appear as a normal local file
310 # modification.
311 fl = repo.file(f)
312 f_len = fl.size(fl.rev(other))
313 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
309 314
310 315 if show_stats:
311 316 stats = ((len(get), _("updated")),
@@ -267,6 +267,20 b' def patch(patchname, ui, strip=1, cwd=No'
267 267
268 268 return (files, fuzz)
269 269
270 def diffopts(ui, opts={}):
271 return mdiff.diffopts(
272 text=opts.get('text'),
273 git=(opts.get('git') or
274 ui.configbool('diff', 'git', None)),
275 showfunc=(opts.get('show_function') or
276 ui.configbool('diff', 'showfunc', None)),
277 ignorews=(opts.get('ignore_all_space') or
278 ui.configbool('diff', 'ignorews', None)),
279 ignorewsamount=(opts.get('ignore_space_change') or
280 ui.configbool('diff', 'ignorewsamount', None)),
281 ignoreblanklines=(opts.get('ignore_blank_lines') or
282 ui.configbool('diff', 'ignoreblanklines', None)))
283
270 284 def diff(repo, node1=None, node2=None, files=None, match=util.always,
271 285 fp=None, changes=None, opts=None):
272 286 '''print diff of changes to files between two nodes, or node and
@@ -766,6 +766,19 b' class revlog(object):'
766 766
767 767 raise RevlogError(_("No match found"))
768 768
769 def cmp(self, node, text):
770 """compare text with a given file revision"""
771 p1, p2 = self.parents(node)
772 return hash(text, p1, p2) != node
773
774 def makenode(self, node, text):
775 """calculate a file nodeid for text, descended or possibly
776 unchanged from node"""
777
778 if self.cmp(node, text):
779 return hash(text, node, nullid)
780 return node
781
769 782 def diff(self, a, b):
770 783 """return a delta between two revisions"""
771 784 return mdiff.textdiff(a, b)
@@ -7,7 +7,7 b''
7 7
8 8 from i18n import gettext as _
9 9 from demandload import *
10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
10 demandload(globals(), "errno getpass os re socket sys tempfile")
11 11 demandload(globals(), "ConfigParser mdiff templater traceback util")
12 12
13 13 class ui(object):
@@ -169,20 +169,6 b' class ui(object):'
169 169 result[key.lower()] = value
170 170 return result
171 171
172 def diffopts(self, opts={}):
173 return mdiff.diffopts(
174 text=opts.get('text'),
175 showfunc=(opts.get('show_function') or
176 self.configbool('diff', 'showfunc', None)),
177 git=(opts.get('git') or
178 self.configbool('diff', 'git', None)),
179 ignorews=(opts.get('ignore_all_space') or
180 self.configbool('diff', 'ignorews', None)),
181 ignorewsamount=(opts.get('ignore_space_change') or
182 self.configbool('diff', 'ignorewsamount', None)),
183 ignoreblanklines=(opts.get('ignore_blank_lines') or
184 self.configbool('diff', 'ignoreblanklines', None)))
185
186 172 def username(self):
187 173 """Return default username to be used in commits.
188 174
@@ -295,62 +281,6 b' class ui(object):'
295 281
296 282 return t
297 283
298 def sendmail(self):
299 '''send mail message. object returned has one method, sendmail.
300 call as sendmail(sender, list-of-recipients, msg).'''
301
302 def smtp():
303 '''send mail using smtp.'''
304
305 local_hostname = self.config('smtp', 'local_hostname')
306 s = smtplib.SMTP(local_hostname=local_hostname)
307 mailhost = self.config('smtp', 'host')
308 if not mailhost:
309 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
310 mailport = int(self.config('smtp', 'port', 25))
311 self.note(_('sending mail: smtp host %s, port %s\n') %
312 (mailhost, mailport))
313 s.connect(host=mailhost, port=mailport)
314 if self.configbool('smtp', 'tls'):
315 self.note(_('(using tls)\n'))
316 s.ehlo()
317 s.starttls()
318 s.ehlo()
319 username = self.config('smtp', 'username')
320 password = self.config('smtp', 'password')
321 if username and password:
322 self.note(_('(authenticating to mail server as %s)\n') %
323 (username))
324 s.login(username, password)
325 return s
326
327 class sendmail(object):
328 '''send mail using sendmail.'''
329
330 def __init__(self, ui, program):
331 self.ui = ui
332 self.program = program
333
334 def sendmail(self, sender, recipients, msg):
335 cmdline = '%s -f %s %s' % (
336 self.program, templater.email(sender),
337 ' '.join(map(templater.email, recipients)))
338 self.ui.note(_('sending mail: %s\n') % cmdline)
339 fp = os.popen(cmdline, 'w')
340 fp.write(msg)
341 ret = fp.close()
342 if ret:
343 raise util.Abort('%s %s' % (
344 os.path.basename(self.program.split(None, 1)[0]),
345 util.explain_exit(ret)[0]))
346
347 method = self.config('email', 'method', 'smtp')
348 if method == 'smtp':
349 mail = smtp()
350 else:
351 mail = sendmail(self, method)
352 return mail
353
354 284 def print_exc(self):
355 285 '''print exception traceback if traceback printing enabled.
356 286 only to call in exception handler. returns true if traceback
General Comments 0
You need to be logged in to leave comments. Login now