# HG changeset patch # User Vadim Gelfer # Date 1155750739 25200 # Node ID addb58e3b41c5880b77f29d28e1671d5d5c1883d # Parent b70740aefa4df12bd0c86d7093c39c139171ba82# Parent 8743188f4d2ed4ef5f2329ff0eaf3d30f6802b7c redo merge with mpm. previous merge at ef8ee4477019 was bad. diff --git a/hgext/mq.py b/hgext/mq.py --- a/hgext/mq.py +++ b/hgext/mq.py @@ -77,7 +77,7 @@ class queue: def diffopts(self): if self._diffopts is None: - self._diffopts = self.ui.diffopts() + self._diffopts = patch.diffopts(self.ui) return self._diffopts def join(self, *p): diff --git a/hgext/notify.py b/hgext/notify.py --- a/hgext/notify.py +++ b/hgext/notify.py @@ -67,8 +67,8 @@ from mercurial.demandload import * from mercurial.i18n import gettext as _ from mercurial.node import * -demandload(globals(), 'email.Parser mercurial:commands,patch,templater,util') -demandload(globals(), 'fnmatch socket time') +demandload(globals(), 'mercurial:commands,patch,templater,util,mail') +demandload(globals(), 'email.Parser fnmatch socket time') # template for single changeset can include email headers. single_template = ''' @@ -229,8 +229,8 @@ class notifier(object): else: self.ui.status(_('notify: sending %d subscribers %d changes\n') % (len(self.subs), count)) - mail = self.ui.sendmail() - mail.sendmail(templater.email(msg['From']), self.subs, msgtext) + mail.sendmail(self.ui, templater.email(msg['From']), + self.subs, msgtext) def diff(self, node, ref): maxdiff = int(self.ui.config('notify', 'maxdiff', 300)) diff --git a/hgext/patchbomb.py b/hgext/patchbomb.py --- a/hgext/patchbomb.py +++ b/hgext/patchbomb.py @@ -241,7 +241,7 @@ def patchbomb(ui, repo, *revs, **opts): ui.write('\n') if not opts['test'] and not opts['mbox']: - mail = ui.sendmail() + mailer = mail.connect(ui) parent = None # Calculate UTC offset @@ -290,7 +290,7 @@ def patchbomb(ui, repo, *revs, **opts): ui.status('Sending ', m['Subject'], ' ...\n') # Exim does not remove the Bcc field del m['Bcc'] - mail.sendmail(sender, to + bcc + cc, m.as_string(0)) + mailer.sendmail(sender, to + bcc + cc, m.as_string(0)) cmdtable = { 'email': diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1347,7 +1347,7 @@ def diff(ui, repo, *pats, **opts): fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) patch.diff(repo, node1, node2, fns, match=matchfn, - opts=ui.diffopts(opts)) + opts=patch.diffopts(ui, opts)) def export(ui, repo, *changesets, **opts): """dump the header and diffs for one or more changesets @@ -1384,7 +1384,8 @@ def export(ui, repo, *changesets, **opts else: ui.note(_('exporting patch:\n')) patch.export(repo, map(repo.lookup, revs), template=opts['output'], - switch_parent=opts['switch_parent'], opts=ui.diffopts(opts)) + switch_parent=opts['switch_parent'], + opts=patch.diffopts(ui, opts)) def forget(ui, repo, *pats, **opts): """don't add the specified files on the next commit (DEPRECATED) diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -65,26 +65,25 @@ class filelog(revlog): return (m["copy"], bin(m["copyrev"])) return False + def size(self, rev): + """return the size of a given revision""" + + # for revisions with renames, we have to go the slow way + node = self.node(rev) + if self.renamed(node): + return len(self.read(node)) + + return revlog.size(self, rev) + def cmp(self, node, text): """compare text with a given file revision""" # for renames, we have to go the slow way if self.renamed(node): t2 = self.read(node) - return t2 == text - - p1, p2 = self.parents(node) - h = hash(text, p1, p2) - - return h != node + return t2 != text - def makenode(self, node, text): - """calculate a file nodeid for text, descended or possibly - unchanged from node""" - - if self.cmp(node, text): - return hash(text, node, nullid) - return node + return revlog.cmp(self, node, text) def annotate(self, node): diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -11,7 +11,7 @@ import os.path import mimetypes from mercurial.demandload import demandload demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile") -demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone") +demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch") demandload(globals(), "mercurial:templater") demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile") from mercurial.node import * @@ -134,7 +134,7 @@ class hgweb(object): modified, added, removed = map(lambda x: filterfiles(files, x), (modified, added, removed)) - diffopts = self.repo.ui.diffopts() + diffopts = patch.diffopts(ui) for f in modified: to = r.file(f).read(mmap1[f]) tn = r.file(f).read(mmap2[f]) diff --git a/mercurial/mail.py b/mercurial/mail.py new file mode 100644 --- /dev/null +++ b/mercurial/mail.py @@ -0,0 +1,68 @@ +# mail.py - mail sending bits for mercurial +# +# Copyright 2006 Matt Mackall +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from i18n import gettext as _ +from demandload import * +demandload(globals(), "os re smtplib templater util") + +def _smtp(ui): + '''send mail using smtp.''' + + local_hostname = ui.config('smtp', 'local_hostname') + s = smtplib.SMTP(local_hostname=local_hostname) + mailhost = ui.config('smtp', 'host') + if not mailhost: + raise util.Abort(_('no [smtp]host in hgrc - cannot send mail')) + mailport = int(ui.config('smtp', 'port', 25)) + self.note(_('sending mail: smtp host %s, port %s\n') % + (mailhost, mailport)) + s.connect(host=mailhost, port=mailport) + if ui.configbool('smtp', 'tls'): + ui.note(_('(using tls)\n')) + s.ehlo() + s.starttls() + s.ehlo() + username = ui.config('smtp', 'username') + password = ui.config('smtp', 'password') + if username and password: + ui.note(_('(authenticating to mail server as %s)\n') % + (username)) + s.login(username, password) + return s + +class _sendmail(object): + '''send mail using sendmail.''' + + def __init__(self, ui, program): + self.ui = ui + self.program = program + + def sendmail(self, sender, recipients, msg): + cmdline = '%s -f %s %s' % ( + self.program, templater.email(sender), + ' '.join(map(templater.email, recipients))) + self.ui.note(_('sending mail: %s\n') % cmdline) + fp = os.popen(cmdline, 'w') + fp.write(msg) + ret = fp.close() + if ret: + raise util.Abort('%s %s' % ( + os.path.basename(self.program.split(None, 1)[0]), + util.explain_exit(ret)[0])) + +def connect(ui): + '''make a mail connection. object returned has one method, sendmail. + call as sendmail(sender, list-of-recipients, msg).''' + + method = ui.config('email', 'method', 'smtp') + if method == 'smtp': + return smtp(ui) + + return sendmail(ui, method) + +def sendmail(ui, sender, recipients, msg): + return connect(ui).sendmail(sender, recipients, msg) diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -10,6 +10,11 @@ from i18n import gettext as _ from demandload import * demandload(globals(), "util os tempfile") +def fmerge(f, local, other, ancestor): + """merge executable flags""" + a, b, c = ancestor.execf(f), local.execf(f), other.execf(f) + return ((a^b) | (a^c)) ^ a + def merge3(repo, fn, my, other, p1, p2): """perform a 3-way merge in the working directory""" @@ -90,9 +95,7 @@ def update(repo, node, branchmerge=False if not force: for f in unknown: if f in m2: - t1 = repo.wread(f) - t2 = repo.file(f).read(m2[f]) - if cmp(t1, t2) != 0: + if repo.file(f).cmp(m2[f], repo.wread(f)): raise util.Abort(_("'%s' already exists in the working" " dir and differs from remote") % f) @@ -100,13 +103,14 @@ def update(repo, node, branchmerge=False # we care about merging repo.ui.note(_("resolving manifests\n")) repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") % - (overwrite, branchmerge, partial and True or False, linear_path)) + (overwrite, branchmerge, bool(partial), linear_path)) repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (short(man), short(m1n), short(m2n))) merge = {} get = {} remove = [] + forget = [] # construct a working dir manifest mw = m1.copy() @@ -114,6 +118,11 @@ def update(repo, node, branchmerge=False for f in added + modified + unknown: mw[f] = "" + # is the wfile new and matches m2? + if (f not in m1 and f in m2 and + not repo.file(f).cmp(m2[f], repo.wread(f))): + mw[f] = m2[f] + mw.set(f, util.is_exec(repo.wjoin(f), mw.execf(f))) for f in deleted + removed: @@ -125,8 +134,8 @@ def update(repo, node, branchmerge=False # the file, then we need to remove it from the dirstate, to # prevent the dirstate from listing the file when it is no # longer in the manifest. - if not partial and linear_path and f not in m2: - repo.dirstate.forget((f,)) + if linear_path and f not in m2: + forget.append(f) # Compare manifests for f, n in mw.iteritems(): @@ -135,25 +144,13 @@ def update(repo, node, branchmerge=False if f in m2: s = 0 - # is the wfile new since m1, and match m2? - if f not in m1: - t1 = repo.wread(f) - t2 = repo.file(f).read(m2[f]) - if cmp(t1, t2) == 0: - n = m2[f] - del t1, t2 - # are files different? if n != m2[f]: a = ma.get(f, nullid) # are both different from the ancestor? if n != a and m2[f] != a: repo.ui.debug(_(" %s versions differ, resolve\n") % f) - # merge executable bits - # "if we changed or they changed, change in merge" - a, b, c = ma.execf(f), mw.execf(f), m2.execf(f) - mode = ((a^b) | (a^c)) ^ a - merge[f] = (mode, m1.get(f, nullid), m2[f]) + merge[f] = (fmerge(f, mw, m2, ma), m1.get(f, nullid), m2[f]) s = 1 # are we clobbering? # is remote's version newer? @@ -172,9 +169,7 @@ def update(repo, node, branchmerge=False repo.ui.debug(_(" updating permissions for %s\n") % f) util.set_exec(repo.wjoin(f), m2.execf(f)) else: - a, b, c = ma.execf(f), mw.execf(f), m2.execf(f) - mode = ((a^b) | (a^c)) ^ a - if mode != b: + if fmerge(f, mw, m2, ma) != mw.execf(f): repo.ui.debug(_(" updating permissions for %s\n") % f) util.set_exec(repo.wjoin(f), mode) @@ -230,6 +225,8 @@ def update(repo, node, branchmerge=False del mw, m1, m2, ma + ### apply phase + if overwrite: for f in merge: get[f] = merge[f][:2] @@ -257,11 +254,6 @@ def update(repo, node, branchmerge=False t = repo.file(f).read(node) repo.wwrite(f, t) util.set_exec(repo.wjoin(f), flag) - if not partial: - if branchmerge: - repo.dirstate.update([f], 'n', st_mtime=-1) - else: - repo.dirstate.update([f], 'n') # merge the tricky bits unresolved = [] @@ -274,19 +266,6 @@ def update(repo, node, branchmerge=False if ret: unresolved.append(f) util.set_exec(repo.wjoin(f), flag) - if not partial: - if branchmerge: - # We've done a branch merge, mark this file as merged - # so that we properly record the merger later - repo.dirstate.update([f], 'm') - else: - # We've update-merged a locally modified file, so - # we set the dirstate to emulate a normal checkout - # of that file some time in the past. Thus our - # merge will appear as a normal local file - # modification. - f_len = len(repo.file(f).read(other)) - repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1) remove.sort() for f in remove: @@ -298,14 +277,40 @@ def update(repo, node, branchmerge=False if inst.errno != errno.ENOENT: repo.ui.warn(_("update failed to remove %s: %s!\n") % (f, inst.strerror)) + + # update dirstate if not partial: + repo.dirstate.setparents(p1, p2) + repo.dirstate.forget(forget) if branchmerge: repo.dirstate.update(remove, 'r') else: repo.dirstate.forget(remove) - if not partial: - repo.dirstate.setparents(p1, p2) + files = get.keys() + files.sort() + for f in files: + if branchmerge: + repo.dirstate.update([f], 'n', st_mtime=-1) + else: + repo.dirstate.update([f], 'n') + + files = merge.keys() + files.sort() + for f in files: + if branchmerge: + # We've done a branch merge, mark this file as merged + # so that we properly record the merger later + repo.dirstate.update([f], 'm') + else: + # We've update-merged a locally modified file, so + # we set the dirstate to emulate a normal checkout + # of that file some time in the past. Thus our + # merge will appear as a normal local file + # modification. + fl = repo.file(f) + f_len = fl.size(fl.rev(other)) + repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1) if show_stats: stats = ((len(get), _("updated")), diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -267,6 +267,20 @@ def patch(patchname, ui, strip=1, cwd=No return (files, fuzz) +def diffopts(ui, opts={}): + return mdiff.diffopts( + text=opts.get('text'), + git=(opts.get('git') or + ui.configbool('diff', 'git', None)), + showfunc=(opts.get('show_function') or + ui.configbool('diff', 'showfunc', None)), + ignorews=(opts.get('ignore_all_space') or + ui.configbool('diff', 'ignorews', None)), + ignorewsamount=(opts.get('ignore_space_change') or + ui.configbool('diff', 'ignorewsamount', None)), + ignoreblanklines=(opts.get('ignore_blank_lines') or + ui.configbool('diff', 'ignoreblanklines', None))) + def diff(repo, node1=None, node2=None, files=None, match=util.always, fp=None, changes=None, opts=None): '''print diff of changes to files between two nodes, or node and diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -766,6 +766,19 @@ class revlog(object): raise RevlogError(_("No match found")) + def cmp(self, node, text): + """compare text with a given file revision""" + p1, p2 = self.parents(node) + return hash(text, p1, p2) != node + + def makenode(self, node, text): + """calculate a file nodeid for text, descended or possibly + unchanged from node""" + + if self.cmp(node, text): + return hash(text, node, nullid) + return node + def diff(self, a, b): """return a delta between two revisions""" return mdiff.textdiff(a, b) diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -7,7 +7,7 @@ from i18n import gettext as _ from demandload import * -demandload(globals(), "errno getpass os re smtplib socket sys tempfile") +demandload(globals(), "errno getpass os re socket sys tempfile") demandload(globals(), "ConfigParser mdiff templater traceback util") class ui(object): @@ -169,20 +169,6 @@ class ui(object): result[key.lower()] = value return result - def diffopts(self, opts={}): - return mdiff.diffopts( - text=opts.get('text'), - showfunc=(opts.get('show_function') or - self.configbool('diff', 'showfunc', None)), - git=(opts.get('git') or - self.configbool('diff', 'git', None)), - ignorews=(opts.get('ignore_all_space') or - self.configbool('diff', 'ignorews', None)), - ignorewsamount=(opts.get('ignore_space_change') or - self.configbool('diff', 'ignorewsamount', None)), - ignoreblanklines=(opts.get('ignore_blank_lines') or - self.configbool('diff', 'ignoreblanklines', None))) - def username(self): """Return default username to be used in commits. @@ -295,62 +281,6 @@ class ui(object): return t - def sendmail(self): - '''send mail message. object returned has one method, sendmail. - call as sendmail(sender, list-of-recipients, msg).''' - - def smtp(): - '''send mail using smtp.''' - - local_hostname = self.config('smtp', 'local_hostname') - s = smtplib.SMTP(local_hostname=local_hostname) - mailhost = self.config('smtp', 'host') - if not mailhost: - raise util.Abort(_('no [smtp]host in hgrc - cannot send mail')) - mailport = int(self.config('smtp', 'port', 25)) - self.note(_('sending mail: smtp host %s, port %s\n') % - (mailhost, mailport)) - s.connect(host=mailhost, port=mailport) - if self.configbool('smtp', 'tls'): - self.note(_('(using tls)\n')) - s.ehlo() - s.starttls() - s.ehlo() - username = self.config('smtp', 'username') - password = self.config('smtp', 'password') - if username and password: - self.note(_('(authenticating to mail server as %s)\n') % - (username)) - s.login(username, password) - return s - - class sendmail(object): - '''send mail using sendmail.''' - - def __init__(self, ui, program): - self.ui = ui - self.program = program - - def sendmail(self, sender, recipients, msg): - cmdline = '%s -f %s %s' % ( - self.program, templater.email(sender), - ' '.join(map(templater.email, recipients))) - self.ui.note(_('sending mail: %s\n') % cmdline) - fp = os.popen(cmdline, 'w') - fp.write(msg) - ret = fp.close() - if ret: - raise util.Abort('%s %s' % ( - os.path.basename(self.program.split(None, 1)[0]), - util.explain_exit(ret)[0])) - - method = self.config('email', 'method', 'smtp') - if method == 'smtp': - mail = smtp() - else: - mail = sendmail(self, method) - return mail - def print_exc(self): '''print exception traceback if traceback printing enabled. only to call in exception handler. returns true if traceback