merge with wsgi changes.
authorVadim Gelfer <vadim.gelfer@gmail.com>
Tue, 27 Jun 2006 00:10:41 -0700
changeset 2509 6350b01d173f
parent 2508 ab460a3f0e3a (current diff)
parent 2504 158d3d2ae070 (diff)
child 2510 cbff06469488
merge with wsgi changes.
mercurial/hgweb/hgweb_mod.py
mercurial/hgweb/hgwebdir_mod.py
--- a/hgext/mq.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/hgext/mq.py	Tue Jun 27 00:10:41 2006 -0700
@@ -1000,6 +1000,7 @@
             self.ui.warn("-n option not valid when importing multiple files\n")
             sys.exit(1)
         i = 0
+        added = []
         for filename in files:
             if existing:
                 if not patch:
@@ -1028,8 +1029,12 @@
             self.read_series(self.full_series)
             self.ui.warn("adding %s to series file\n" % patch)
             i += 1
+            added.append(patch)
             patch = None
         self.series_dirty = 1
+        qrepo = self.qrepo()
+        if qrepo:
+            qrepo.add(added)
 
 def delete(ui, repo, patch, **opts):
     """remove a patch from the series file"""
--- a/mercurial/commands.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/mercurial/commands.py	Tue Jun 27 00:10:41 2006 -0700
@@ -12,7 +12,7 @@
 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
 demandload(globals(), "fnmatch mdiff random signal tempfile time")
 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
-demandload(globals(), "archival changegroup")
+demandload(globals(), "archival cStringIO changegroup email.Parser")
 demandload(globals(), "hgweb.server sshserver")
 
 class UnknownCommand(Exception):
@@ -836,11 +836,16 @@
         return '%d:%s' % (repo.changelog.rev(node), short(node))
     ui.status(_('changeset %s backs out changeset %s\n') %
               (nice(repo.changelog.tip()), nice(node)))
-    if opts['merge'] and op1 != node:
-        ui.status(_('merging with changeset %s\n') % nice(op1))
-        doupdate(ui, repo, hex(op1), **opts)
-
-def bundle(ui, repo, fname, dest="default-push", **opts):
+    if op1 != node:
+        if opts['merge']:
+            ui.status(_('merging with changeset %s\n') % nice(op1))
+            doupdate(ui, repo, hex(op1), **opts)
+        else:
+            ui.status(_('the backout changeset is a new head - '
+                        'do not forget to merge\n'))
+            ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
+
+def bundle(ui, repo, fname, dest=None, **opts):
     """create a changegroup file
 
     Generate a compressed changegroup file collecting all changesets
@@ -855,7 +860,7 @@
     Unlike import/export, this exactly preserves all changeset
     contents including permissions, rename data, and revision history.
     """
-    dest = ui.expandpath(dest)
+    dest = ui.expandpath(dest or 'default-push', dest or 'default')
     other = hg.repository(ui, dest)
     o = repo.findoutgoing(other, force=opts['force'])
     cg = repo.changegroup(o, 'bundle')
@@ -1714,11 +1719,15 @@
     If there are outstanding changes in the working directory, import
     will abort unless given the -f flag.
 
-    If a patch looks like a mail message (its first line starts with
-    "From " or looks like an RFC822 header), it will not be applied
-    unless the -f option is used.  The importer neither parses nor
-    discards mail headers, so use -f only to override the "mailness"
-    safety check, not to import a real mail message.
+    You can import a patch straight from a mail message.  Even patches
+    as attachments work (body part must be type text/plain or
+    text/x-patch to be used).  Sender and subject line of email
+    message are used as default committer and commit message.  Any
+    text/plain body part before first diff is added to commit message.
+
+    If imported patch was generated by hg export, user and description
+    from patch override values from message headers and body.  Values
+    given on command line with -m and -u override these.
 
     To read a patch from standard input, use patch name "-".
     """
@@ -1734,79 +1743,93 @@
 
     # attempt to detect the start of a patch
     # (this heuristic is borrowed from quilt)
-    diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
+    diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
                         'retrieving revision [0-9]+(\.[0-9]+)*$|' +
-                        '(---|\*\*\*)[ \t])')
+                        '(---|\*\*\*)[ \t])', re.MULTILINE)
 
     for patch in patches:
         pf = os.path.join(d, patch)
 
-        message = []
+        message = None
         user = None
         date = None
         hgpatch = False
+
+        p = email.Parser.Parser()
         if pf == '-':
-            f = sys.stdin
-            fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
-            pf = tmpname
-            tmpfp = os.fdopen(fd, 'w')
+            msg = p.parse(sys.stdin)
             ui.status(_("applying patch from stdin\n"))
         else:
-            f = open(pf)
-            tmpfp, tmpname = None, None
+            msg = p.parse(file(pf))
             ui.status(_("applying %s\n") % patch)
+
+        fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
+        tmpfp = os.fdopen(fd, 'w')
         try:
-            while True:
-                line = f.readline()
-                if not line: break
-                if tmpfp: tmpfp.write(line)
-                line = line.rstrip()
-                if (not message and not hgpatch and
-                       mailre.match(line) and not opts['force']):
-                    if len(line) > 35:
-                        line = line[:32] + '...'
-                    raise util.Abort(_('first line looks like a '
-                                       'mail header: ') + line)
-                if diffre.match(line):
+            message = msg['Subject']
+            if message:
+                message = message.replace('\n\t', ' ')
+                ui.debug('Subject: %s\n' % message)
+            user = msg['From']
+            if user:
+                ui.debug('From: %s\n' % user)
+            diffs_seen = 0
+            ok_types = ('text/plain', 'text/x-patch')
+            for part in msg.walk():
+                content_type = part.get_content_type()
+                ui.debug('Content-Type: %s\n' % content_type)
+                if content_type not in ok_types:
+                    continue
+                payload = part.get_payload(decode=True)
+                m = diffre.search(payload)
+                if m:
+                    ui.debug(_('found patch at byte %d\n') % m.start(0))
+                    diffs_seen += 1
+                    hgpatch = False
+                    fp = cStringIO.StringIO()
+                    for line in payload[:m.start(0)].splitlines():
+                        if line.startswith('# HG changeset patch'):
+                            ui.debug(_('patch generated by hg export\n'))
+                            hgpatch = True
+                        elif hgpatch:
+                            if line.startswith('# User '):
+                                user = line[7:]
+                                ui.debug('From: %s\n' % user)
+                            elif line.startswith("# Date "):
+                                date = line[7:]
+                        if not line.startswith('# '):
+                            fp.write(line)
+                            fp.write('\n')
+                            hgpatch = False
+                    message = fp.getvalue() or message
                     if tmpfp:
-                        for chunk in util.filechunkiter(f):
-                            tmpfp.write(chunk)
-                    break
-                elif hgpatch:
-                    # parse values when importing the result of an hg export
-                    if line.startswith("# User "):
-                        user = line[7:]
-                        ui.debug(_('User: %s\n') % user)
-                    elif line.startswith("# Date "):
-                        date = line[7:]
-                    elif not line.startswith("# ") and line:
-                        message.append(line)
-                        hgpatch = False
-                elif line == '# HG changeset patch':
-                    hgpatch = True
-                    message = []       # We may have collected garbage
-                elif message or line:
-                    message.append(line)
+                        tmpfp.write(payload)
+                        if not payload.endswith('\n'):
+                            tmpfp.write('\n')
+                elif not diffs_seen and message and content_type == 'text/plain':
+                    message += '\n' + payload
 
             if opts['message']:
                 # pickup the cmdline msg
                 message = opts['message']
             elif message:
                 # pickup the patch msg
-                message = '\n'.join(message).rstrip()
+                message = message.strip()
             else:
                 # launch the editor
                 message = None
             ui.debug(_('message:\n%s\n') % message)
 
-            if tmpfp: tmpfp.close()
-            files = util.patch(strip, pf, ui)
-
+            tmpfp.close()
+            if not diffs_seen:
+                raise util.Abort(_('no diffs found'))
+
+            files = util.patch(strip, tmpname, ui)
             if len(files) > 0:
                 addremove_lock(ui, repo, files, {})
             repo.commit(files, message, user, date)
         finally:
-            if tmpname: os.unlink(tmpname)
+            os.unlink(tmpname)
 
 def incoming(ui, repo, source="default", **opts):
     """show new changesets found in source
@@ -2042,7 +2065,7 @@
     """
     return doupdate(ui, repo, node=node, merge=True, **opts)
 
-def outgoing(ui, repo, dest="default-push", **opts):
+def outgoing(ui, repo, dest=None, **opts):
     """show changesets not found in destination
 
     Show changesets not found in the specified destination repository or
@@ -2051,7 +2074,7 @@
 
     See pull for valid destination format details.
     """
-    dest = ui.expandpath(dest)
+    dest = ui.expandpath(dest or 'default-push', dest or 'default')
     if opts['ssh']:
         ui.setconfig("ui", "ssh", opts['ssh'])
     if opts['remotecmd']:
@@ -2174,7 +2197,7 @@
     modheads = repo.pull(other, heads=revs, force=opts['force'])
     return postincoming(ui, repo, modheads, opts['update'])
 
-def push(ui, repo, dest="default-push", **opts):
+def push(ui, repo, dest=None, **opts):
     """push changes to the specified destination
 
     Push changes from the local repository to the given destination.
@@ -2196,7 +2219,7 @@
     Look at the help text for the pull command for important details
     about ssh:// URLs.
     """
-    dest = ui.expandpath(dest)
+    dest = ui.expandpath(dest or 'default-push', dest or 'default')
 
     if opts['ssh']:
         ui.setconfig("ui", "ssh", opts['ssh'])
--- a/mercurial/hgweb/hgweb_mod.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/mercurial/hgweb/hgweb_mod.py	Tue Jun 27 00:10:41 2006 -0700
@@ -49,8 +49,7 @@
             self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
 
     def archivelist(self, nodeid):
-        allowed = (self.repo.ui.config("web", "allow_archive", "")
-                   .replace(",", " ").split())
+        allowed = self.repo.ui.configlist("web", "allow_archive")
         for i in self.archives:
             if i in allowed or self.repo.ui.configbool("web", "allow" + i):
                 yield {"type" : i, "node" : nodeid, "url": ""}
@@ -816,7 +815,7 @@
     def do_archive(self, req):
         changeset = self.repo.lookup(req.form['node'][0])
         type_ = req.form['type'][0]
-        allowed = self.repo.ui.config("web", "allow_archive", "").split()
+        allowed = self.repo.ui.configlist("web", "allow_archive")
         if (type_ in self.archives and (type_ in allowed or
             self.repo.ui.configbool("web", "allow" + type_, False))):
             self.archive(req, changeset, type_)
@@ -844,15 +843,11 @@
 
         user = req.env.get('REMOTE_USER')
 
-        deny = self.repo.ui.config('web', 'deny_' + op, '')
-        deny = deny.replace(',', ' ').split()
-
+        deny = self.repo.ui.configlist('web', 'deny_' + op)
         if deny and (not user or deny == ['*'] or user in deny):
             return False
 
-        allow = self.repo.ui.config('web', 'allow_' + op, '')
-        allow = allow.replace(',', ' ').split()
-
+        allow = self.repo.ui.configlist('web', 'allow_' + op)
         return (allow and (allow == ['*'] or user in allow)) or default
 
     def do_unbundle(self, req):
--- a/mercurial/hgweb/hgwebdir_mod.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/mercurial/hgweb/hgwebdir_mod.py	Tue Jun 27 00:10:41 2006 -0700
@@ -59,8 +59,7 @@
                                              "footer": footer})
 
         def archivelist(ui, nodeid, url):
-            allowed = (ui.config("web", "allow_archive", "")
-                       .replace(",", " ").split())
+            allowed = ui.configlist("web", "allow_archive")
             for i in ['zip', 'gz', 'bz2']:
                 if i in allowed or ui.configbool("web", "allow" + i):
                     yield {"type" : i, "node": nodeid, "url": url}
--- a/mercurial/httprepo.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/mercurial/httprepo.py	Tue Jun 27 00:10:41 2006 -0700
@@ -120,9 +120,8 @@
 
             # see if we should use a proxy for this url
             no_list = [ "localhost", "127.0.0.1" ]
-            no_list.extend([p.strip().lower() for
-                            p in ui.config("http_proxy", "no", '').split(',')
-                            if p.strip()])
+            no_list.extend([p.lower() for
+                            p in ui.configlist("http_proxy", "no")])
             no_list.extend([p.strip().lower() for
                             p in os.getenv("no_proxy", '').split(',')
                             if p.strip()])
--- a/mercurial/localrepo.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/mercurial/localrepo.py	Tue Jun 27 00:10:41 2006 -0700
@@ -619,7 +619,7 @@
 
         modified, added, removed, deleted, unknown, ignored = [],[],[],[],[],[]
         compareworking = False
-        if not node1 or node1 == self.dirstate.parents()[0]:
+        if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
             compareworking = True
 
         if not compareworking:
--- a/mercurial/lsprof.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/mercurial/lsprof.py	Tue Jun 27 00:10:41 2006 -0700
@@ -4,7 +4,13 @@
 # small modifications made
 
 import sys
-from _lsprof import Profiler, profiler_entry, profiler_subentry
+try:
+    from _lsprof import Profiler, profiler_entry, profiler_subentry
+except ImportError, inst:
+    import packagescan
+    if packagescan.scan_in_progress:
+        raise packagescan.SkipPackage('_lsprof not available')
+    raise
 
 __all__ = ['profile', 'Stats']
 
--- a/mercurial/packagescan.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/mercurial/packagescan.py	Tue Jun 27 00:10:41 2006 -0700
@@ -60,8 +60,16 @@
                 if type(scope[f]) == types.ModuleType:
                     requiredmodules[scope[f].__name__] = 1
 
+class SkipPackage(Exception):
+    def __init__(self, reason):
+        self.reason = reason
+
+scan_in_progress = False
+
 def scan(libpath,packagename):
     """ helper for finding all required modules of package <packagename> """
+    global scan_in_progress
+    scan_in_progress = True
     # Use the package in the build directory
     libpath = os.path.abspath(libpath)
     sys.path.insert(0,libpath)
@@ -85,7 +93,11 @@
         tmp = {}
         mname,ext = os.path.splitext(m)
         fullname = packagename+'.'+mname
-        __import__(fullname,tmp,tmp)
+        try:
+            __import__(fullname,tmp,tmp)
+        except SkipPackage, inst:
+            print >> sys.stderr, 'skipping %s: %s' % (fullname, inst.reason)
+            continue
         requiredmodules[fullname] = 1
     # Import all extension modules and by that run the fake demandload
     for m in extmodulefiles:
--- a/mercurial/revlog.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/mercurial/revlog.py	Tue Jun 27 00:10:41 2006 -0700
@@ -477,6 +477,13 @@
         if self.version == REVLOGV0:
             return d
         return [ self.node(x) for x in d ]
+    def parentrevs(self, rev):
+        if rev == -1:
+            return (-1, -1)
+        d = self.index[rev][-3:-1]
+        if self.version == REVLOGV0:
+            return [ self.rev(x) for x in d ]
+        return d
     def start(self, rev):
         if rev < 0:
             return -1
@@ -706,19 +713,19 @@
         """
         if start is None:
             start = nullid
-        reachable = {start: 1}
-        heads = {start: 1}
         startrev = self.rev(start)
+        reachable = {startrev: 1}
+        heads = {startrev: 1}
 
+        parentrevs = self.parentrevs
         for r in xrange(startrev + 1, self.count()):
-            n = self.node(r)
-            for pn in self.parents(n):
-                if pn in reachable:
-                    reachable[n] = 1
-                    heads[n] = 1
-                if pn in heads:
-                    del heads[pn]
-        return heads.keys()
+            for p in parentrevs(r):
+                if p in reachable:
+                    reachable[r] = 1
+                    heads[r] = 1
+                if p in heads:
+                    del heads[p]
+        return [self.node(r) for r in heads]
 
     def children(self, node):
         """find the children of a given node"""
--- a/mercurial/ui.py	Tue Jun 27 00:09:37 2006 -0700
+++ b/mercurial/ui.py	Tue Jun 27 00:10:41 2006 -0700
@@ -95,6 +95,15 @@
         else:
             return self.parentui.config(section, name, default)
 
+    def configlist(self, section, name, default=None):
+        """Return a list of comma/space separated strings"""
+        result = self.config(section, name)
+        if result is None:
+            result = default or []
+        if isinstance(result, basestring):
+            result = result.replace(",", " ").split()
+        return result
+
     def configbool(self, section, name, default=False):
         if self.overlay.has_key((section, name)):
             return self.overlay[(section, name)]
@@ -197,12 +206,15 @@
         if not self.verbose: user = util.shortuser(user)
         return user
 
-    def expandpath(self, loc):
+    def expandpath(self, loc, default=None):
         """Return repository location relative to cwd or from [paths]"""
         if loc.find("://") != -1 or os.path.exists(loc):
             return loc
 
-        return self.config("paths", loc, loc)
+        path = self.config("paths", loc)
+        if not path and default is not None:
+            path = self.config("paths", default)
+        return path or loc
 
     def write(self, *args):
         if self.header:
--- a/tests/test-archive	Tue Jun 27 00:09:37 2006 -0700
+++ b/tests/test-archive	Tue Jun 27 00:10:41 2006 -0700
@@ -15,9 +15,7 @@
 hg commit -m 3
 echo "[web]" >> .hg/hgrc
 echo "name = test-archive" >> .hg/hgrc
-echo "allowzip = true" >> .hg/hgrc
-echo "allowgz = true" >> .hg/hgrc
-echo "allowbz2 = true" >> .hg/hgrc
+echo "allow_archive = gz bz2, zip" >> .hg/hgrc
 hg serve -p 20059 -d --pid-file=hg.pid
 
 TIP=`hg id -v | cut -f1 -d' '`
--- a/tests/test-backout	Tue Jun 27 00:09:37 2006 -0700
+++ b/tests/test-backout	Tue Jun 27 00:10:41 2006 -0700
@@ -48,4 +48,16 @@
 hg commit -d '4 0' -m d
 cat a
 
+echo '# backout should not back out subsequent changesets'
+hg init onecs
+cd onecs
+echo 1 > a
+hg commit -d '0 0' -A -m a
+echo 2 >> a
+hg commit -d '1 0' -m b
+echo 1 > b
+hg commit -d '2 0' -A -m c
+hg backout -d '3 0' 1
+hg locate b
+
 exit 0
--- a/tests/test-backout.out	Tue Jun 27 00:09:37 2006 -0700
+++ b/tests/test-backout.out	Tue Jun 27 00:10:41 2006 -0700
@@ -21,3 +21,11 @@
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 line 1
+# backout should not back out subsequent changesets
+adding a
+adding b
+reverting a
+changeset 3:4cbb1e70196a backs out changeset 1:22bca4c721e5
+the backout changeset is a new head - do not forget to merge
+(use "backout -m" if you want to auto-merge)
+b: No such file or directory
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-default-push	Tue Jun 27 00:10:41 2006 -0700
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+hg init a
+echo a > a/a
+hg --cwd a ci -Ama
+
+hg clone a c
+
+hg clone a b
+echo b >> b/a
+hg --cwd b ci -mb
+
+echo % push should push to default when default-push not set
+hg --cwd b push | sed 's/pushing to.*/pushing/'
+
+echo % push should push to default-push when set
+echo 'default-push = ../c' >> b/.hg/hgrc
+hg --cwd b push | sed 's/pushing to.*/pushing/'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-default-push.out	Tue Jun 27 00:10:41 2006 -0700
@@ -0,0 +1,17 @@
+adding a
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% push should push to default when default-push not set
+pushing
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+% push should push to default-push when set
+pushing
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-ui-config	Tue Jun 27 00:10:41 2006 -0700
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+from mercurial import ui
+
+testui = ui.ui()
+testui.updateopts(config=[
+    'values.string=string value',
+    'values.bool1=true',
+    'values.bool2=false',
+    'lists.list1=foo',
+    'lists.list2=foo bar baz',
+    'lists.list3=alice, bob',
+    'lists.list4=foo bar baz alice, bob',
+])
+
+print repr(testui.configitems('values'))
+print repr(testui.configitems('lists'))
+print "---"
+print repr(testui.config('values', 'string'))
+print repr(testui.config('values', 'bool1'))
+print repr(testui.config('values', 'bool2'))
+print repr(testui.config('values', 'unknown'))
+print "---"
+try:
+    print repr(testui.configbool('values', 'string'))
+except ValueError, why:
+    print why
+print repr(testui.configbool('values', 'bool1'))
+print repr(testui.configbool('values', 'bool2'))
+print repr(testui.configbool('values', 'bool2', True))
+print repr(testui.configbool('values', 'unknown'))
+print repr(testui.configbool('values', 'unknown', True))
+print "---"
+print repr(testui.configlist('lists', 'list1'))
+print repr(testui.configlist('lists', 'list2'))
+print repr(testui.configlist('lists', 'list3'))
+print repr(testui.configlist('lists', 'list4'))
+print repr(testui.configlist('lists', 'list4', ['foo']))
+print repr(testui.configlist('lists', 'unknown'))
+print repr(testui.configlist('lists', 'unknown', ''))
+print repr(testui.configlist('lists', 'unknown', 'foo'))
+print repr(testui.configlist('lists', 'unknown', ['foo']))
+print repr(testui.configlist('lists', 'unknown', 'foo bar'))
+print repr(testui.configlist('lists', 'unknown', 'foo, bar'))
+print repr(testui.configlist('lists', 'unknown', ['foo bar']))
+print repr(testui.configlist('lists', 'unknown', ['foo', 'bar']))
+print "---"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-ui-config.out	Tue Jun 27 00:10:41 2006 -0700
@@ -0,0 +1,29 @@
+[('bool1', 'true'), ('bool2', 'false'), ('string', 'string value')]
+[('list1', 'foo'), ('list2', 'foo bar baz'), ('list3', 'alice, bob'), ('list4', 'foo bar baz alice, bob')]
+---
+'string value'
+'true'
+'false'
+None
+---
+Not a boolean: string value
+True
+False
+False
+False
+True
+---
+['foo']
+['foo', 'bar', 'baz']
+['alice', 'bob']
+['foo', 'bar', 'baz', 'alice', 'bob']
+['foo', 'bar', 'baz', 'alice', 'bob']
+[]
+[]
+['foo']
+['foo']
+['foo', 'bar']
+['foo', 'bar']
+['foo bar']
+['foo', 'bar']
+---