##// END OF EJS Templates
merge with mpm.
Vadim Gelfer -
r2920:ef8ee447 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 def diffopts(self):
78 def diffopts(self):
79 if self._diffopts is None:
79 if self._diffopts is None:
80 self._diffopts = self.ui.diffopts()
80 self._diffopts = patch.diffopts(self.ui)
81 return self._diffopts
81 return self._diffopts
82
82
83 def join(self, *p):
83 def join(self, *p):
@@ -400,15 +400,39 b' class queue:'
400 '''Apply patchfile to the working directory.
400 '''Apply patchfile to the working directory.
401 patchfile: file name of patch'''
401 patchfile: file name of patch'''
402 try:
402 try:
403 (files, fuzz) = patch.patch(patchfile, self.ui, strip=1,
403 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
404 cwd=repo.root)
404 f = os.popen("%s -d %s -p1 --no-backup-if-mismatch < %s" %
405 except Exception, inst:
405 (pp, util.shellquote(repo.root), util.shellquote(patchfile)))
406 self.ui.note(str(inst) + '\n')
406 except:
407 if not self.ui.verbose:
407 self.ui.warn("patch failed, unable to continue (try -v)\n")
408 self.ui.warn("patch failed, unable to continue (try -v)\n")
408 return (None, [], False)
409 return (False, [], False)
409 files = []
410 fuzz = False
411 for l in f:
412 l = l.rstrip('\r\n');
413 if self.ui.verbose:
414 self.ui.warn(l + "\n")
415 if l[:14] == 'patching file ':
416 pf = os.path.normpath(util.parse_patch_output(l))
417 if pf not in files:
418 files.append(pf)
419 printed_file = False
420 file_str = l
421 elif l.find('with fuzz') >= 0:
422 if not printed_file:
423 self.ui.warn(file_str + '\n')
424 printed_file = True
425 self.ui.warn(l + '\n')
426 fuzz = True
427 elif l.find('saving rejects to file') >= 0:
428 self.ui.warn(l + '\n')
429 elif l.find('FAILED') >= 0:
430 if not printed_file:
431 self.ui.warn(file_str + '\n')
432 printed_file = True
433 self.ui.warn(l + '\n')
410
434
411 return (True, files.keys(), fuzz)
435 return (not f.close(), files, fuzz)
412
436
413 def apply(self, repo, series, list=False, update_status=True,
437 def apply(self, repo, series, list=False, update_status=True,
414 strict=False, patchdir=None, merge=None, wlock=None):
438 strict=False, patchdir=None, merge=None, wlock=None):
@@ -482,28 +506,21 b' class queue:'
482 tr.close()
506 tr.close()
483 return (err, n)
507 return (err, n)
484
508
485 def delete(self, repo, patches, keep=False):
509 def delete(self, repo, patch, force=False):
486 realpatches = []
510 patch = self.lookup(patch, strict=True)
487 for patch in patches:
511 info = self.isapplied(patch)
488 patch = self.lookup(patch, strict=True)
512 if info:
489 info = self.isapplied(patch)
513 raise util.Abort(_("cannot delete applied patch %s") % patch)
490 if info:
514 if patch not in self.series:
491 raise util.Abort(_("cannot delete applied patch %s") % patch)
515 raise util.Abort(_("patch %s not in series file") % patch)
492 if patch not in self.series:
516 if force:
493 raise util.Abort(_("patch %s not in series file") % patch)
494 realpatches.append(patch)
495
496 if not keep:
497 r = self.qrepo()
517 r = self.qrepo()
498 if r:
518 if r:
499 r.remove(realpatches, True)
519 r.remove([patch], True)
500 else:
520 else:
501 os.unlink(self.join(patch))
521 os.unlink(self.join(patch))
502
522 i = self.find_series(patch)
503 indices = [self.find_series(p) for p in realpatches]
523 del self.full_series[i]
504 indices.sort()
505 for i in indices[-1::-1]:
506 del self.full_series[i]
507 self.parse_series()
524 self.parse_series()
508 self.series_dirty = 1
525 self.series_dirty = 1
509
526
@@ -1283,13 +1300,13 b' class queue:'
1283 if qrepo:
1300 if qrepo:
1284 qrepo.add(added)
1301 qrepo.add(added)
1285
1302
1286 def delete(ui, repo, patch, *patches, **opts):
1303 def delete(ui, repo, patch, **opts):
1287 """remove patches from queue
1304 """remove a patch from the series file
1288
1305
1289 The patches must not be applied.
1306 The patch must not be applied.
1290 With -k, the patch files are preserved in the patch directory."""
1307 With -f, deletes the patch file as well as the series entry."""
1291 q = repo.mq
1308 q = repo.mq
1292 q.delete(repo, (patch,) + patches, keep=opts.get('keep'))
1309 q.delete(repo, patch, force=opts.get('force'))
1293 q.save_dirty()
1310 q.save_dirty()
1294 return 0
1311 return 0
1295
1312
@@ -1447,7 +1464,7 b' def fold(ui, repo, *files, **opts):'
1447 applied to the current patch in the order given. If all the
1464 applied to the current patch in the order given. If all the
1448 patches apply successfully, the current patch will be refreshed
1465 patches apply successfully, the current patch will be refreshed
1449 with the new cumulative patch, and the folded patches will
1466 with the new cumulative patch, and the folded patches will
1450 be deleted. With -k/--keep, the folded patch files will not
1467 be deleted. With -f/--force, the folded patch files will
1451 be removed afterwards.
1468 be removed afterwards.
1452
1469
1453 The header for each folded patch will be concatenated with
1470 The header for each folded patch will be concatenated with
@@ -1497,7 +1514,7 b' def fold(ui, repo, *files, **opts):'
1497 q.refresh(repo, msg=message)
1514 q.refresh(repo, msg=message)
1498
1515
1499 for patch in patches:
1516 for patch in patches:
1500 q.delete(repo, patch, keep=opts['keep'])
1517 q.delete(repo, patch, force=opts['force'])
1501
1518
1502 q.save_dirty()
1519 q.save_dirty()
1503
1520
@@ -1886,14 +1903,14 b' cmdtable = {'
1886 commands.table["^commit|ci"][1],
1903 commands.table["^commit|ci"][1],
1887 'hg qcommit [OPTION]... [FILE]...'),
1904 'hg qcommit [OPTION]... [FILE]...'),
1888 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1905 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1889 "qdelete|qremove|qrm":
1906 "qdelete":
1890 (delete,
1907 (delete,
1891 [('k', 'keep', None, _('keep patch file'))],
1908 [('f', 'force', None, _('delete patch file'))],
1892 'hg qdelete [-k] PATCH'),
1909 'hg qdelete [-f] PATCH'),
1893 'qfold':
1910 'qfold':
1894 (fold,
1911 (fold,
1895 [('e', 'edit', None, _('edit patch header')),
1912 [('e', 'edit', None, _('edit patch header')),
1896 ('k', 'keep', None, _('keep folded patch files')),
1913 ('f', 'force', None, _('delete folded patch files')),
1897 ('m', 'message', '', _('set patch header to <text>')),
1914 ('m', 'message', '', _('set patch header to <text>')),
1898 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1915 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
1899 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
1916 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
@@ -67,8 +67,8 b''
67 from mercurial.demandload import *
67 from mercurial.demandload import *
68 from mercurial.i18n import gettext as _
68 from mercurial.i18n import gettext as _
69 from mercurial.node import *
69 from mercurial.node import *
70 demandload(globals(), 'email.Parser mercurial:commands,patch,templater,util')
70 demandload(globals(), 'mercurial:commands,patch,templater,util,mail')
71 demandload(globals(), 'fnmatch socket time')
71 demandload(globals(), 'email.Parser fnmatch socket time')
72
72
73 # template for single changeset can include email headers.
73 # template for single changeset can include email headers.
74 single_template = '''
74 single_template = '''
@@ -229,8 +229,8 b' class notifier(object):'
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 = self.ui.sendmail()
232 mail.sendmail(self.ui, templater.email(msg['From']),
233 mail.sendmail(templater.email(msg['From']), self.subs, msgtext)
233 self.subs, msgtext)
234
234
235 def diff(self, node, ref):
235 def diff(self, node, ref):
236 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
236 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
@@ -241,7 +241,7 b' def patchbomb(ui, repo, *revs, **opts):'
241 ui.write('\n')
241 ui.write('\n')
242
242
243 if not opts['test'] and not opts['mbox']:
243 if not opts['test'] and not opts['mbox']:
244 mail = ui.sendmail()
244 mailer = mail.connect(ui)
245 parent = None
245 parent = None
246
246
247 # Calculate UTC offset
247 # Calculate UTC offset
@@ -290,7 +290,7 b' def patchbomb(ui, repo, *revs, **opts):'
290 ui.status('Sending ', m['Subject'], ' ...\n')
290 ui.status('Sending ', m['Subject'], ' ...\n')
291 # Exim does not remove the Bcc field
291 # Exim does not remove the Bcc field
292 del m['Bcc']
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 cmdtable = {
295 cmdtable = {
296 'email':
296 'email':
@@ -183,59 +183,49 b' def walkchangerevs(ui, repo, pats, opts)'
183 fncache[rev] = matches
183 fncache[rev] = matches
184 wanted[rev] = 1
184 wanted[rev] = 1
185
185
186 class followfilter:
186 def iterate():
187 def __init__(self, onlyfirst=False):
187 class followfilter:
188 self.startrev = -1
188 def __init__(self, onlyfirst=False):
189 self.roots = []
189 self.startrev = -1
190 self.onlyfirst = onlyfirst
190 self.roots = []
191
191 self.onlyfirst = onlyfirst
192 def match(self, rev):
192
193 def realparents(rev):
193 def match(self, rev):
194 if self.onlyfirst:
194 def realparents(rev):
195 return repo.changelog.parentrevs(rev)[0:1]
195 if self.onlyfirst:
196 return repo.changelog.parentrevs(rev)[0:1]
197 else:
198 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
199
200 if self.startrev == -1:
201 self.startrev = rev
202 return True
203
204 if rev > self.startrev:
205 # forward: all descendants
206 if not self.roots:
207 self.roots.append(self.startrev)
208 for parent in realparents(rev):
209 if parent in self.roots:
210 self.roots.append(rev)
211 return True
196 else:
212 else:
197 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
213 # backwards: all parents
198
214 if not self.roots:
199 if self.startrev == -1:
215 self.roots.extend(realparents(self.startrev))
200 self.startrev = rev
216 if rev in self.roots:
201 return True
217 self.roots.remove(rev)
202
218 self.roots.extend(realparents(rev))
203 if rev > self.startrev:
204 # forward: all descendants
205 if not self.roots:
206 self.roots.append(self.startrev)
207 for parent in realparents(rev):
208 if parent in self.roots:
209 self.roots.append(rev)
210 return True
219 return True
211 else:
220
212 # backwards: all parents
221 return False
213 if not self.roots:
222
214 self.roots.extend(realparents(self.startrev))
215 if rev in self.roots:
216 self.roots.remove(rev)
217 self.roots.extend(realparents(rev))
218 return True
219
220 return False
221
222 # it might be worthwhile to do this in the iterator if the rev range
223 # is descending and the prune args are all within that range
224 for rev in opts.get('prune', ()):
225 rev = repo.changelog.rev(repo.lookup(rev))
226 ff = followfilter()
227 stop = min(revs[0], revs[-1])
228 for x in range(rev, stop-1, -1):
229 if ff.match(x) and wanted.has_key(x):
230 del wanted[x]
231
232 def iterate():
233 if follow and not files:
223 if follow and not files:
234 ff = followfilter(onlyfirst=opts.get('follow_first'))
224 ff = followfilter(onlyfirst=opts.get('follow_first'))
235 def want(rev):
225 def want(rev):
236 if ff.match(rev) and rev in wanted:
226 if rev not in wanted:
237 return True
227 return False
238 return False
228 return ff.match(rev)
239 else:
229 else:
240 def want(rev):
230 def want(rev):
241 return rev in wanted
231 return rev in wanted
@@ -1347,7 +1337,7 b' def diff(ui, repo, *pats, **opts):'
1347 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1337 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1348
1338
1349 patch.diff(repo, node1, node2, fns, match=matchfn,
1339 patch.diff(repo, node1, node2, fns, match=matchfn,
1350 opts=ui.diffopts(opts))
1340 opts=patch.diffopts(ui, opts))
1351
1341
1352 def export(ui, repo, *changesets, **opts):
1342 def export(ui, repo, *changesets, **opts):
1353 """dump the header and diffs for one or more changesets
1343 """dump the header and diffs for one or more changesets
@@ -1384,7 +1374,8 b' def export(ui, repo, *changesets, **opts'
1384 else:
1374 else:
1385 ui.note(_('exporting patch:\n'))
1375 ui.note(_('exporting patch:\n'))
1386 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1376 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1387 switch_parent=opts['switch_parent'], opts=ui.diffopts(opts))
1377 switch_parent=opts['switch_parent'],
1378 opts=patch.diffopts(ui, opts))
1388
1379
1389 def forget(ui, repo, *pats, **opts):
1380 def forget(ui, repo, *pats, **opts):
1390 """don't add the specified files on the next commit (DEPRECATED)
1381 """don't add the specified files on the next commit (DEPRECATED)
@@ -1681,7 +1672,7 b' def import_(ui, repo, patch1, *patches, '
1681 message = None
1672 message = None
1682 ui.debug(_('message:\n%s\n') % message)
1673 ui.debug(_('message:\n%s\n') % message)
1683
1674
1684 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1675 files = patch.patch(strip, tmpname, ui, cwd=repo.root)
1685 removes = []
1676 removes = []
1686 if len(files) > 0:
1677 if len(files) > 0:
1687 cfiles = files.keys()
1678 cfiles = files.keys()
@@ -1969,29 +1960,9 b' def merge(ui, repo, node=None, force=Non'
1969 requested revision. Files that changed between either parent are
1960 requested revision. Files that changed between either parent are
1970 marked as changed for the next commit and a commit must be
1961 marked as changed for the next commit and a commit must be
1971 performed before any further updates are allowed.
1962 performed before any further updates are allowed.
1972
1973 If no revision is specified, the working directory's parent is a
1974 head revision, and the repository contains exactly one other head,
1975 the other head is merged with by default. Otherwise, an explicit
1976 revision to merge with must be provided.
1977 """
1963 """
1978
1964
1979 if node:
1965 node = _lookup(repo, node, branch)
1980 node = _lookup(repo, node, branch)
1981 else:
1982 heads = repo.heads()
1983 if len(heads) > 2:
1984 raise util.Abort(_('repo has %d heads - '
1985 'please merge with an explicit rev') %
1986 len(heads))
1987 if len(heads) == 1:
1988 raise util.Abort(_('there is nothing to merge - '
1989 'use "hg update" instead'))
1990 parent = repo.dirstate.parents()[0]
1991 if parent not in heads:
1992 raise util.Abort(_('working dir not at a head rev - '
1993 'use "hg update" or merge with an explicit rev'))
1994 node = parent == heads[0] and heads[-1] or heads[0]
1995 return hg.merge(repo, node, force=force)
1966 return hg.merge(repo, node, force=force)
1996
1967
1997 def outgoing(ui, repo, dest=None, **opts):
1968 def outgoing(ui, repo, dest=None, **opts):
@@ -2897,7 +2868,6 b' table = {'
2897 ('a', 'text', None, _('treat all files as text')),
2868 ('a', 'text', None, _('treat all files as text')),
2898 ('p', 'show-function', None,
2869 ('p', 'show-function', None,
2899 _('show which function each change is in')),
2870 _('show which function each change is in')),
2900 ('g', 'git', None, _('use git extended diff format')),
2901 ('w', 'ignore-all-space', None,
2871 ('w', 'ignore-all-space', None,
2902 _('ignore white space when comparing lines')),
2872 _('ignore white space when comparing lines')),
2903 ('b', 'ignore-space-change', None,
2873 ('b', 'ignore-space-change', None,
@@ -2997,7 +2967,6 b' table = {'
2997 ('', 'style', '', _('display using template map file')),
2967 ('', 'style', '', _('display using template map file')),
2998 ('m', 'only-merges', None, _('show only merges')),
2968 ('m', 'only-merges', None, _('show only merges')),
2999 ('p', 'patch', None, _('show patch')),
2969 ('p', 'patch', None, _('show patch')),
3000 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3001 ('', 'template', '', _('display with template')),
2970 ('', 'template', '', _('display with template')),
3002 ('I', 'include', [], _('include names matching the given patterns')),
2971 ('I', 'include', [], _('include names matching the given patterns')),
3003 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2972 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
@@ -65,26 +65,25 b' class filelog(revlog):'
65 return (m["copy"], bin(m["copyrev"]))
65 return (m["copy"], bin(m["copyrev"]))
66 return False
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 def cmp(self, node, text):
78 def cmp(self, node, text):
69 """compare text with a given file revision"""
79 """compare text with a given file revision"""
70
80
71 # for renames, we have to go the slow way
81 # for renames, we have to go the slow way
72 if self.renamed(node):
82 if self.renamed(node):
73 t2 = self.read(node)
83 t2 = self.read(node)
74 return t2 == text
84 return t2 != text
75
76 p1, p2 = self.parents(node)
77 h = hash(text, p1, p2)
78
79 return h != node
80
85
81 def makenode(self, node, text):
86 return revlog.cmp(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
88
87
89 def annotate(self, node):
88 def annotate(self, node):
90
89
@@ -11,7 +11,7 b' import os.path'
11 import mimetypes
11 import mimetypes
12 from mercurial.demandload import demandload
12 from mercurial.demandload import demandload
13 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
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 demandload(globals(), "mercurial:templater")
15 demandload(globals(), "mercurial:templater")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
17 from mercurial.node import *
17 from mercurial.node import *
@@ -134,7 +134,7 b' class hgweb(object):'
134 modified, added, removed = map(lambda x: filterfiles(files, x),
134 modified, added, removed = map(lambda x: filterfiles(files, x),
135 (modified, added, removed))
135 (modified, added, removed))
136
136
137 diffopts = self.repo.ui.diffopts()
137 diffopts = patch.diffopts(ui)
138 for f in modified:
138 for f in modified:
139 to = r.file(f).read(mmap1[f])
139 to = r.file(f).read(mmap1[f])
140 tn = r.file(f).read(mmap2[f])
140 tn = r.file(f).read(mmap2[f])
@@ -10,6 +10,11 b' from i18n import gettext as _'
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "util os tempfile")
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 def merge3(repo, fn, my, other, p1, p2):
18 def merge3(repo, fn, my, other, p1, p2):
14 """perform a 3-way merge in the working directory"""
19 """perform a 3-way merge in the working directory"""
15
20
@@ -90,9 +95,7 b' def update(repo, node, branchmerge=False'
90 if not force:
95 if not force:
91 for f in unknown:
96 for f in unknown:
92 if f in m2:
97 if f in m2:
93 t1 = repo.wread(f)
98 if repo.file(f).cmp(m2[f], repo.wread(f)):
94 t2 = repo.file(f).read(m2[f])
95 if cmp(t1, t2) != 0:
96 raise util.Abort(_("'%s' already exists in the working"
99 raise util.Abort(_("'%s' already exists in the working"
97 " dir and differs from remote") % f)
100 " dir and differs from remote") % f)
98
101
@@ -100,13 +103,14 b' def update(repo, node, branchmerge=False'
100 # we care about merging
103 # we care about merging
101 repo.ui.note(_("resolving manifests\n"))
104 repo.ui.note(_("resolving manifests\n"))
102 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") %
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 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
107 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
105 (short(man), short(m1n), short(m2n)))
108 (short(man), short(m1n), short(m2n)))
106
109
107 merge = {}
110 merge = {}
108 get = {}
111 get = {}
109 remove = []
112 remove = []
113 forget = []
110
114
111 # construct a working dir manifest
115 # construct a working dir manifest
112 mw = m1.copy()
116 mw = m1.copy()
@@ -114,6 +118,11 b' def update(repo, node, branchmerge=False'
114
118
115 for f in added + modified + unknown:
119 for f in added + modified + unknown:
116 mw[f] = ""
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 mw.set(f, util.is_exec(repo.wjoin(f), mw.execf(f)))
126 mw.set(f, util.is_exec(repo.wjoin(f), mw.execf(f)))
118
127
119 for f in deleted + removed:
128 for f in deleted + removed:
@@ -125,8 +134,8 b' def update(repo, node, branchmerge=False'
125 # the file, then we need to remove it from the dirstate, to
134 # the file, then we need to remove it from the dirstate, to
126 # prevent the dirstate from listing the file when it is no
135 # prevent the dirstate from listing the file when it is no
127 # longer in the manifest.
136 # longer in the manifest.
128 if not partial and linear_path and f not in m2:
137 if linear_path and f not in m2:
129 repo.dirstate.forget((f,))
138 forget.append(f)
130
139
131 # Compare manifests
140 # Compare manifests
132 for f, n in mw.iteritems():
141 for f, n in mw.iteritems():
@@ -135,25 +144,13 b' def update(repo, node, branchmerge=False'
135 if f in m2:
144 if f in m2:
136 s = 0
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 # are files different?
147 # are files different?
147 if n != m2[f]:
148 if n != m2[f]:
148 a = ma.get(f, nullid)
149 a = ma.get(f, nullid)
149 # are both different from the ancestor?
150 # are both different from the ancestor?
150 if n != a and m2[f] != a:
151 if n != a and m2[f] != a:
151 repo.ui.debug(_(" %s versions differ, resolve\n") % f)
152 repo.ui.debug(_(" %s versions differ, resolve\n") % f)
152 # merge executable bits
153 merge[f] = (fmerge(f, mw, m2, ma), m1.get(f, nullid), m2[f])
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])
157 s = 1
154 s = 1
158 # are we clobbering?
155 # are we clobbering?
159 # is remote's version newer?
156 # is remote's version newer?
@@ -172,9 +169,7 b' def update(repo, node, branchmerge=False'
172 repo.ui.debug(_(" updating permissions for %s\n") % f)
169 repo.ui.debug(_(" updating permissions for %s\n") % f)
173 util.set_exec(repo.wjoin(f), m2.execf(f))
170 util.set_exec(repo.wjoin(f), m2.execf(f))
174 else:
171 else:
175 a, b, c = ma.execf(f), mw.execf(f), m2.execf(f)
172 if fmerge(f, mw, m2, ma) != mw.execf(f):
176 mode = ((a^b) | (a^c)) ^ a
177 if mode != b:
178 repo.ui.debug(_(" updating permissions for %s\n")
173 repo.ui.debug(_(" updating permissions for %s\n")
179 % f)
174 % f)
180 util.set_exec(repo.wjoin(f), mode)
175 util.set_exec(repo.wjoin(f), mode)
@@ -230,6 +225,8 b' def update(repo, node, branchmerge=False'
230
225
231 del mw, m1, m2, ma
226 del mw, m1, m2, ma
232
227
228 ### apply phase
229
233 if overwrite:
230 if overwrite:
234 for f in merge:
231 for f in merge:
235 get[f] = merge[f][:2]
232 get[f] = merge[f][:2]
@@ -257,11 +254,6 b' def update(repo, node, branchmerge=False'
257 t = repo.file(f).read(node)
254 t = repo.file(f).read(node)
258 repo.wwrite(f, t)
255 repo.wwrite(f, t)
259 util.set_exec(repo.wjoin(f), flag)
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 # merge the tricky bits
258 # merge the tricky bits
267 unresolved = []
259 unresolved = []
@@ -274,19 +266,6 b' def update(repo, node, branchmerge=False'
274 if ret:
266 if ret:
275 unresolved.append(f)
267 unresolved.append(f)
276 util.set_exec(repo.wjoin(f), flag)
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 remove.sort()
270 remove.sort()
292 for f in remove:
271 for f in remove:
@@ -298,14 +277,40 b' def update(repo, node, branchmerge=False'
298 if inst.errno != errno.ENOENT:
277 if inst.errno != errno.ENOENT:
299 repo.ui.warn(_("update failed to remove %s: %s!\n") %
278 repo.ui.warn(_("update failed to remove %s: %s!\n") %
300 (f, inst.strerror))
279 (f, inst.strerror))
280
281 # update dirstate
301 if not partial:
282 if not partial:
283 repo.dirstate.setparents(p1, p2)
284 repo.dirstate.forget(forget)
302 if branchmerge:
285 if branchmerge:
303 repo.dirstate.update(remove, 'r')
286 repo.dirstate.update(remove, 'r')
304 else:
287 else:
305 repo.dirstate.forget(remove)
288 repo.dirstate.forget(remove)
306
289
307 if not partial:
290 files = get.keys()
308 repo.dirstate.setparents(p1, p2)
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 if show_stats:
315 if show_stats:
311 stats = ((len(get), _("updated")),
316 stats = ((len(get), _("updated")),
@@ -215,14 +215,13 b' def dogitpatch(patchname, gitpatches):'
215 tmpfp.close()
215 tmpfp.close()
216 return patchname
216 return patchname
217
217
218 def patch(patchname, ui, strip=1, cwd=None):
218 def patch(strip, patchname, ui, cwd=None):
219 """apply the patch <patchname> to the working directory.
219 """apply the patch <patchname> to the working directory.
220 a list of patched files is returned"""
220 a list of patched files is returned"""
221
221
222 (dopatch, gitpatches) = readgitpatch(patchname)
222 (dopatch, gitpatches) = readgitpatch(patchname)
223
223
224 files = {}
224 files = {}
225 fuzz = False
226 if dopatch:
225 if dopatch:
227 if dopatch == 'filter':
226 if dopatch == 'filter':
228 patchname = dogitpatch(patchname, gitpatches)
227 patchname = dogitpatch(patchname, gitpatches)
@@ -238,25 +237,10 b' def patch(patchname, ui, strip=1, cwd=No'
238
237
239 for line in fp:
238 for line in fp:
240 line = line.rstrip()
239 line = line.rstrip()
241 ui.note(line + '\n')
240 ui.status("%s\n" % line)
242 if line.startswith('patching file '):
241 if line.startswith('patching file '):
243 pf = util.parse_patch_output(line)
242 pf = util.parse_patch_output(line)
244 printed_file = False
245 files.setdefault(pf, (None, None))
243 files.setdefault(pf, (None, None))
246 elif line.find('with fuzz') >= 0:
247 fuzz = True
248 if not printed_file:
249 ui.warn(pf + '\n')
250 printed_file = True
251 ui.warn(line + '\n')
252 elif line.find('saving rejects to file') >= 0:
253 ui.warn(line + '\n')
254 elif line.find('FAILED') >= 0:
255 if not printed_file:
256 ui.warn(pf + '\n')
257 printed_file = True
258 ui.warn(line + '\n')
259
260 code = fp.close()
244 code = fp.close()
261 if code:
245 if code:
262 raise util.Abort(_("patch command failed: %s") %
246 raise util.Abort(_("patch command failed: %s") %
@@ -265,7 +249,19 b' def patch(patchname, ui, strip=1, cwd=No'
265 for gp in gitpatches:
249 for gp in gitpatches:
266 files[gp.path] = (gp.op, gp)
250 files[gp.path] = (gp.op, gp)
267
251
268 return (files, fuzz)
252 return files
253
254 def diffopts(ui, opts={}):
255 return mdiff.diffopts(
256 text=opts.get('text'),
257 showfunc=(opts.get('show_function') or
258 ui.configbool('diff', 'showfunc', None)),
259 ignorews=(opts.get('ignore_all_space') or
260 ui.configbool('diff', 'ignorews', None)),
261 ignorewsamount=(opts.get('ignore_space_change') or
262 ui.configbool('diff', 'ignorewsamount', None)),
263 ignoreblanklines=(opts.get('ignore_blank_lines') or
264 ui.configbool('diff', 'ignoreblanklines', None)))
269
265
270 def diff(repo, node1=None, node2=None, files=None, match=util.always,
266 def diff(repo, node1=None, node2=None, files=None, match=util.always,
271 fp=None, changes=None, opts=None):
267 fp=None, changes=None, opts=None):
@@ -314,9 +310,6 b' def diff(repo, node1=None, node2=None, f'
314 return _date2
310 return _date2
315 def read(f):
311 def read(f):
316 return repo.file(f).read(mmap2[f])
312 return repo.file(f).read(mmap2[f])
317 def renamed(f):
318 src = repo.file(f).renamed(mmap2[f])
319 return src and src[0] or None
320 else:
313 else:
321 tz = util.makedate()[1]
314 tz = util.makedate()[1]
322 _date2 = util.datestr()
315 _date2 = util.datestr()
@@ -328,8 +321,6 b' def diff(repo, node1=None, node2=None, f'
328 return _date2
321 return _date2
329 def read(f):
322 def read(f):
330 return repo.wread(f)
323 return repo.wread(f)
331 def renamed(f):
332 return repo.dirstate.copies.get(f)
333
324
334 if repo.ui.quiet:
325 if repo.ui.quiet:
335 r = None
326 r = None
@@ -337,65 +328,16 b' def diff(repo, node1=None, node2=None, f'
337 hexfunc = repo.ui.verbose and hex or short
328 hexfunc = repo.ui.verbose and hex or short
338 r = [hexfunc(node) for node in [node1, node2] if node]
329 r = [hexfunc(node) for node in [node1, node2] if node]
339
330
340 if opts.git:
341 copied = {}
342 for f in added:
343 src = renamed(f)
344 if src:
345 copied[f] = src
346 srcs = [x[1] for x in copied.items()]
347
348 all = modified + added + removed
331 all = modified + added + removed
349 all.sort()
332 all.sort()
350 for f in all:
333 for f in all:
351 to = None
334 to = None
352 tn = None
335 tn = None
353 dodiff = True
354 if f in mmap:
336 if f in mmap:
355 to = repo.file(f).read(mmap[f])
337 to = repo.file(f).read(mmap[f])
356 if f not in removed:
338 if f not in removed:
357 tn = read(f)
339 tn = read(f)
358 if opts.git:
340 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
359 def gitmode(x):
360 return x and '100755' or '100644'
361 def addmodehdr(header, omode, nmode):
362 if omode != nmode:
363 header.append('old mode %s\n' % omode)
364 header.append('new mode %s\n' % nmode)
365
366 a, b = f, f
367 header = []
368 if f in added:
369 if node2:
370 mode = gitmode(mmap2.execf(f))
371 else:
372 mode = gitmode(util.is_exec(repo.wjoin(f), None))
373 if f in copied:
374 a = copied[f]
375 omode = gitmode(mmap.execf(a))
376 addmodehdr(header, omode, mode)
377 op = a in removed and 'rename' or 'copy'
378 header.append('%s from %s\n' % (op, a))
379 header.append('%s to %s\n' % (op, f))
380 to = repo.file(a).read(mmap[a])
381 else:
382 header.append('new file mode %s\n' % mode)
383 elif f in removed:
384 if f in srcs:
385 dodiff = False
386 else:
387 mode = gitmode(mmap.execf(f))
388 header.append('deleted file mode %s\n' % mode)
389 else:
390 omode = gitmode(mmap.execf(f))
391 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
392 addmodehdr(header, omode, nmode)
393 r = None
394 if dodiff:
395 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
396 fp.write(''.join(header))
397 if dodiff:
398 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
399
341
400 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
342 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
401 opts=None):
343 opts=None):
@@ -766,6 +766,19 b' class revlog(object):'
766
766
767 raise RevlogError(_("No match found"))
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 def diff(self, a, b):
782 def diff(self, a, b):
770 """return a delta between two revisions"""
783 """return a delta between two revisions"""
771 return mdiff.textdiff(a, b)
784 return mdiff.textdiff(a, b)
@@ -7,7 +7,7 b''
7
7
8 from i18n import gettext as _
8 from i18n import gettext as _
9 from demandload import *
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 demandload(globals(), "ConfigParser mdiff templater traceback util")
11 demandload(globals(), "ConfigParser mdiff templater traceback util")
12
12
13 class ui(object):
13 class ui(object):
@@ -169,20 +169,6 b' class ui(object):'
169 result[key.lower()] = value
169 result[key.lower()] = value
170 return result
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 def username(self):
172 def username(self):
187 """Return default username to be used in commits.
173 """Return default username to be used in commits.
188
174
@@ -295,62 +281,6 b' class ui(object):'
295
281
296 return t
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 def print_exc(self):
284 def print_exc(self):
355 '''print exception traceback if traceback printing enabled.
285 '''print exception traceback if traceback printing enabled.
356 only to call in exception handler. returns true if traceback
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