Merge context patches
authorMatt Mackall <mpm@selenic.com>
Wed, 05 Jul 2006 13:28:25 -0500
changeset 2567 2748253b49c2
parent 2559 bf67d0f6531c (diff)
parent 2566 d8560b458f76 (current diff)
child 2568 52ce0d6bc375
child 2573 82e3b2966862
Merge context patches
mercurial/commands.py
mercurial/localrepo.py
--- a/Makefile
+++ b/Makefile
@@ -37,12 +37,20 @@ clean:
 	rm -f MANIFEST mercurial/__version__.py mercurial/*.so tests/*.err
 	$(MAKE) -C doc clean
 
-install: all
+install: install-bin install-doc
+
+install-bin: build
 	$(PYTHON) setup.py install --prefix="$(PREFIX)" --force
+
+install-doc: doc
 	cd doc && $(MAKE) $(MFLAGS) install
 
-install-home: all
+install-home: install-home-bin install-home-doc
+
+install-home-bin: build
 	$(PYTHON) setup.py install --home="$(HOME)" --force
+
+install-home-doc: doc
 	cd doc && $(MAKE) $(MFLAGS) PREFIX="$(HOME)" install
 
 dist:	tests dist-notests
@@ -57,5 +65,5 @@ test-%:
 	cd tests && $(PYTHON) run-tests.py $@
 
 
-.PHONY: help all local build doc clean install install-home dist dist-notests tests
-
+.PHONY: help all local build doc clean install install-bin install-doc \
+	install-home install-home-bin install-home-doc dist dist-notests tests
--- a/contrib/macosx/Readme.html
+++ b/contrib/macosx/Readme.html
@@ -18,13 +18,10 @@
 <p class="p2"><br></p>
 <p class="p3">This is <i>not</i> a stand-alone version of Mercurial.</p>
 <p class="p2"><br></p>
-<p class="p3">To use it, you must have the “official unofficial” MacPython 2.4.1 installed.</p>
+<p class="p3">To use it, you must have the Universal MacPython 2.4.3 from <a href="http://www.python.org">www.python.org</a> installed.</p>
 <p class="p2"><br></p>
-<p class="p3">You can download MacPython 2.4.1 from here:</p>
-<p class="p4"><span class="s1"><a href="http://python.org/ftp/python/2.4.1/MacPython-OSX-2.4.1-1.dmg">http://python.org/ftp/python/2.4.1/MacPython-OSX-2.4.1-1.dmg</a></span></p>
-<p class="p2"><br></p>
-<p class="p3">For more information on MacPython, go here:</p>
-<p class="p4"><span class="s1"><a href="http://undefined.org/python/">http://undefined.org/python</a></span></p>
+<p class="p3">You can download MacPython 2.4.3 from here:</p>
+<p class="p4"><span class="s1"><a href="http://www.python.org/ftp/python/2.4.3/Universal-MacPython-2.4.3-2006-04-07.dmg">http://www.python.org/ftp/python/2.4.3/Universal-MacPython-2.4.3-2006-04-07.dmg</a></span></p>
 <p class="p2"><br></p>
 <p class="p1"><b>After you install</b></p>
 <p class="p2"><br></p>
--- a/contrib/macosx/Welcome.html
+++ b/contrib/macosx/Welcome.html
@@ -12,6 +12,6 @@
 <body>
 <p class="p1">This is a prepackaged release of <a href="http://www.selenic.com/mercurial">Mercurial</a> for Mac OS X.</p>
 <p class="p2"><br></p>
-<p class="p1">It is based on Mercurial 0.8.</p>
+<p class="p1">It is based on Mercurial 0.9.</p>
 </body>
 </html>
--- a/contrib/mercurial.el
+++ b/contrib/mercurial.el
@@ -653,7 +653,7 @@ The Mercurial mode user interface is bas
 you're already familiar with VC, the same keybindings and functions
 will generally work.
 
-Below is a list of many common SCM tasks.  In the list, `G/L'
+Below is a list of many common SCM tasks.  In the list, `G/L\'
 indicates whether a key binding is global (G) to a repository or local
 (L) to a file.  Many commands take a prefix argument.
 
@@ -682,6 +682,8 @@ Pull changes                          G 
 Update working directory after pull   G    C-c h u      hg-update
 See changes that can be pushed        G    C-c h .      hg-outgoing
 Push changes                          G    C-c h >      hg-push"
+  (unless vc-make-backup-files
+    (set (make-local-variable 'backup-inhibited) t))
   (run-hooks 'hg-mode-hook))
 
 (defun hg-find-file-hook ()
@@ -729,6 +731,8 @@ With a prefix argument, prompt for the p
       (goto-char 0)
       (cd (hg-root path)))
     (when update
+      (unless vc-make-backup-files
+	(set (make-local-variable 'backup-inhibited) t))
       (with-current-buffer buf
 	(hg-mode-line)))))
 
@@ -968,6 +972,7 @@ With a prefix argument, prompt for the p
       (cd (hg-root path)))
     (when update
       (with-current-buffer buf
+	(set (make-local-variable 'backup-inhibited) nil)
 	(hg-mode-line)))))
 
 (defun hg-incoming (&optional repo)
--- a/contrib/win32/mercurial.ini
+++ b/contrib/win32/mercurial.ini
@@ -18,7 +18,8 @@ hgext.win32text =
 
 [encode]
 ; Encode files that don't contain NUL characters.
-** = cleverencode:
+
+; ** = cleverencode:
 
 ; Alternatively, you can explicitly specify each file extension that
 ; you want encoded (any you omit will be left untouched), like this:
@@ -28,7 +29,8 @@ hgext.win32text =
 
 [decode]
 ; Decode files that don't contain NUL characters.
-** = cleverdecode:
+
+; ** = cleverdecode:
 
 ; Alternatively, you can explicitly specify each file extension that
 ; you want decoded (any you omit will be left untouched), like this:
--- a/hgext/hgk.py
+++ b/hgext/hgk.py
@@ -131,7 +131,7 @@ def catcommit(repo, n, prefix, changes=N
     date_ar = changes[2]
     date = int(float(date_ar[0]))
     lines = changes[4].splitlines()
-    if lines[-1].startswith('committer:'):
+    if lines and lines[-1].startswith('committer:'):
         committer = lines[-1].split(': ')[1].rstrip()
     else:
         committer = changes[1]
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -5,6 +5,30 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
+'''patch management and development
+
+This extension lets you work with a stack of patches in a Mercurial
+repository.  It manages two stacks of patches - all known patches, and
+applied patches (subset of known patches).
+
+Known patches are represented as patch files in the .hg/patches
+directory.  Applied patches are both patch files and changesets.
+
+Common tasks (use "hg help command" for more details):
+
+prepare repository to work with patches   qinit
+create new patch                          qnew
+import existing patch                     qimport
+
+print patch series                        qseries
+print applied patches                     qapplied
+print name of top applied patch           qtop
+
+add known patch to applied stack          qpush
+remove patch from applied stack           qpop
+refresh contents of top applied patch     qrefresh
+'''
+
 from mercurial.demandload import *
 demandload(globals(), "os sys re struct traceback errno bz2")
 from mercurial.i18n import gettext as _
@@ -214,7 +238,6 @@ class queue:
                 return pp[0]
             if p1 in arevs:
                 return pp[1]
-            return None
         return pp[0]
 
     def mergepatch(self, repo, mergeq, series, wlock):
@@ -386,15 +409,21 @@ class queue:
             self.ui.write("Local changes found, refresh first\n")
             sys.exit(1)
     def new(self, repo, patch, msg=None, force=None):
-        if not force:
-            self.check_localchanges(repo)
+        commitfiles = []
+        (c, a, r, d, u) = repo.changes(None, None)
+        if c or a or d or r:
+            if not force:
+                raise util.Abort(_("Local changes found, refresh first"))
+            else:
+                commitfiles = c + a + r
         self.check_toppatch(repo)
         wlock = repo.wlock()
         insert = self.series_end()
         if msg:
-            n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock)
+            n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
+                            wlock=wlock)
         else:
-            n = repo.commit([],
+            n = repo.commit(commitfiles,
                             "New patch: %s" % patch, force=True, wlock=wlock)
         if n == None:
             self.ui.warn("repo commit failed\n")
@@ -412,6 +441,8 @@ class queue:
         wlock = None
         r = self.qrepo()
         if r: r.add([patch])
+        if commitfiles:
+            self.refresh(repo, short=True)
 
     def strip(self, repo, rev, update=True, backup="all", wlock=None):
         def limitheads(chlog, stop):
@@ -1076,6 +1107,7 @@ def init(ui, repo, **opts):
     return 0
 
 def commit(ui, repo, *pats, **opts):
+    """commit changes in the queue repository"""
     q = repomap[repo]
     r = q.qrepo()
     if not r: raise util.Abort('no queue repository')
@@ -1257,7 +1289,7 @@ cmdtable = {
          'hg qimport [-e] [-n NAME] [-f] FILE...'),
     "^qinit":
         (init,
-         [('c', 'create-repo', None, 'create patch repository')],
+         [('c', 'create-repo', None, 'create queue repository')],
          'hg qinit [-c]'),
     "qnew":
         (new,
--- a/hgweb.cgi
+++ b/hgweb.cgi
@@ -6,7 +6,11 @@ import cgitb, os, sys
 cgitb.enable()
 
 # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
-from mercurial import hgweb
+from mercurial.hgweb.hgweb_mod import hgweb
+from mercurial.hgweb.request import wsgiapplication
+import mercurial.hgweb.wsgicgi as wsgicgi
 
-h = hgweb.hgweb("/path/to/repo", "repository name")
-h.run()
+def make_web_app():
+    return hgweb("/path/to/repo", "repository name")
+
+wsgicgi.launch(wsgiapplication(make_web_app))
--- a/hgwebdir.cgi
+++ b/hgwebdir.cgi
@@ -6,7 +6,9 @@ import cgitb, sys
 cgitb.enable()
 
 # sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
-from mercurial import hgweb
+from mercurial.hgweb.hgwebdir_mod import hgwebdir
+from mercurial.hgweb.request import wsgiapplication
+import mercurial.hgweb.wsgicgi as wsgicgi
 
 # The config file looks like this.  You can have paths to individual
 # repos, collections of repos in a directory tree, or both.
@@ -27,5 +29,7 @@ from mercurial import hgweb
 # Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
 # or use a dictionary with entries like 'virtual/path': '/real/path'
 
-h = hgweb.hgwebdir("hgweb.config")
-h.run()
+def make_web_app():
+    return hgwebdir("hgweb.config")
+
+wsgicgi.launch(wsgiapplication(make_web_app))
--- a/mercurial/bdiff.c
+++ b/mercurial/bdiff.c
@@ -38,7 +38,7 @@ static uint32_t htonl(uint32_t x)
 #else
 #include <sys/types.h>
 #include <arpa/inet.h>
-#include <stdint.h>
+#include <inttypes.h>
 #endif
 
 struct line {
--- a/mercurial/changelog.py
+++ b/mercurial/changelog.py
@@ -39,21 +39,10 @@ class changelog(revlog):
     def add(self, manifest, list, desc, transaction, p1=None, p2=None,
                   user=None, date=None):
         if date:
-            # validate explicit (probably user-specified) date and
-            # time zone offset. values must fit in signed 32 bits for
-            # current 32-bit linux runtimes. timezones go from UTC-12
-            # to UTC+14
-            try:
-                when, offset = map(int, date.split(' '))
-            except ValueError:
-                raise ValueError(_('invalid date: %r') % date)
-            if abs(when) > 0x7fffffff:
-                raise ValueError(_('date exceeds 32 bits: %d') % when)
-            if offset < -50400 or offset > 43200:
-                raise ValueError(_('impossible time zone offset: %d') % offset)
+            parseddate = "%d %d" % util.parsedate(date)
         else:
-            date = "%d %d" % util.makedate()
+            parseddate = "%d %d" % util.makedate()
         list.sort()
-        l = [hex(manifest), user, date] + list + ["", desc]
+        l = [hex(manifest), user, parseddate] + list + ["", desc]
         text = "\n".join(l)
         return self.addrevision(text, transaction, self.count(), p1, p2)
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -12,7 +12,7 @@ demandload(globals(), "os re sys signal 
 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):
@@ -534,14 +534,22 @@ def show_version(ui):
         "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
     ))
 
-def help_(ui, cmd=None, with_version=False):
-    """show help for a given command or all commands"""
+def help_(ui, name=None, with_version=False):
+    """show help for a command, extension, or list of commands
+
+    With no arguments, print a list of commands and short help.
+    
+    Given a command name, print help for that command.
+
+    Given an extension name, print help for that extension, and the
+    commands it provides."""
     option_lists = []
-    if cmd and cmd != 'shortlist':
+
+    def helpcmd(name):
         if with_version:
             show_version(ui)
             ui.write('\n')
-        aliases, i = find(cmd)
+        aliases, i = findcmd(name)
         # synopsis
         ui.write("%s\n\n" % i[2])
 
@@ -561,30 +569,15 @@ def help_(ui, cmd=None, with_version=Fal
             # options
             if i[1]:
                 option_lists.append(("options", i[1]))
-
-    else:
-        # program name
-        if ui.verbose or with_version:
-            show_version(ui)
-        else:
-            ui.status(_("Mercurial Distributed SCM\n"))
-        ui.status('\n')
-
-        # list of commands
-        if cmd == "shortlist":
-            ui.status(_('basic commands (use "hg help" '
-                        'for the full list or option "-v" for details):\n\n'))
-        elif ui.verbose:
-            ui.status(_('list of commands:\n\n'))
-        else:
-            ui.status(_('list of commands (use "hg help -v" '
-                        'to show aliases and global options):\n\n'))
-
+        
+    def helplist(select=None):
         h = {}
         cmds = {}
         for c, e in table.items():
-            f = c.split("|")[0]
-            if cmd == "shortlist" and not f.startswith("^"):
+            f = c.split("|", 1)[0]
+            if select and not select(f):
+                continue
+            if name == "shortlist" and not f.startswith("^"):
                 continue
             f = f.lstrip("^")
             if not ui.debugflag and f.startswith("debug"):
@@ -605,6 +598,53 @@ def help_(ui, cmd=None, with_version=Fal
             else:
                 ui.write(' %-*s   %s\n' % (m, f, h[f]))
 
+    def helpext(name):
+        try:
+            mod = findext(name)
+        except KeyError:
+            raise UnknownCommand(name)
+
+        doc = (mod.__doc__ or _('No help text available')).splitlines(0)
+        ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
+        for d in doc[1:]:
+            ui.write(d, '\n')
+
+        ui.status('\n')
+        if ui.verbose:
+            ui.status(_('list of commands:\n\n'))
+        else:
+            ui.status(_('list of commands (use "hg help -v %s" '
+                        'to show aliases and global options):\n\n') % name)
+
+        modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
+        helplist(modcmds.has_key)
+
+    if name and name != 'shortlist':
+        try:
+            helpcmd(name)
+        except UnknownCommand:
+            helpext(name)
+
+    else:
+        # program name
+        if ui.verbose or with_version:
+            show_version(ui)
+        else:
+            ui.status(_("Mercurial Distributed SCM\n"))
+        ui.status('\n')
+
+        # list of commands
+        if name == "shortlist":
+            ui.status(_('basic commands (use "hg help" '
+                        'for the full list or option "-v" for details):\n\n'))
+        elif ui.verbose:
+            ui.status(_('list of commands:\n\n'))
+        else:
+            ui.status(_('list of commands (use "hg help -v" '
+                        'to show aliases and global options):\n\n'))
+
+        helplist()
+
     # global options
     if ui.verbose:
         option_lists.append(("global options", globalopts))
@@ -650,7 +690,7 @@ def add(ui, repo, *pats, **opts):
         elif repo.dirstate.state(abs) == '?':
             ui.status(_('adding %s\n') % rel)
             names.append(abs)
-    if not opts['dry_run']:
+    if not opts.get('dry_run'):
         repo.add(names)
 
 def addremove(ui, repo, *pats, **opts):
@@ -908,13 +948,10 @@ def clone(ui, source, dest=None, **opts)
     if os.path.exists(dest):
         raise util.Abort(_("destination '%s' already exists"), dest)
 
-    dest = os.path.realpath(dest)
-
     class Dircleanup(object):
         def __init__(self, dir_):
             self.rmtree = shutil.rmtree
             self.dir_ = dir_
-            os.mkdir(dir_)
         def close(self):
             self.dir_ = None
         def __del__(self):
@@ -927,13 +964,24 @@ def clone(ui, source, dest=None, **opts)
         ui.setconfig("ui", "remotecmd", opts['remotecmd'])
 
     source = ui.expandpath(source)
-
-    d = Dircleanup(dest)
+    src_repo = hg.repository(ui, source)
+
+    dest_repo = None
+    try:
+        dest_repo = hg.repository(ui, dest)
+        raise util.Abort(_("destination '%s' already exists." % dest))
+    except hg.RepoError:
+        dest_repo = hg.repository(ui, dest, create=1)
+
+    dest_path = None
+    d = None
+    if dest_repo.local():
+        dest_path = os.path.realpath(dest)
+        d = Dircleanup(dest_path)
+
     abspath = source
-    other = hg.repository(ui, source)
-
     copy = False
-    if other.dev() != -1:
+    if src_repo.local() and dest_repo.local():
         abspath = os.path.abspath(source)
         if not opts['pull'] and not opts['rev']:
             copy = True
@@ -944,47 +992,57 @@ def clone(ui, source, dest=None, **opts)
             # can end up with extra data in the cloned revlogs that's
             # not pointed to by changesets, thus causing verify to
             # fail
-            l1 = other.lock()
+            l1 = src_repo.lock()
         except lock.LockException:
             copy = False
 
     if copy:
         # we lock here to avoid premature writing to the target
-        os.mkdir(os.path.join(dest, ".hg"))
-        l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
-
+        l2 = lock.lock(os.path.join(dest_path, ".hg", "lock"))
+
+	# we need to remove the (empty) data dir in dest so copyfiles can do it's work
+	os.rmdir( os.path.join(dest_path, ".hg", "data") )
         files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
         for f in files.split():
             src = os.path.join(source, ".hg", f)
-            dst = os.path.join(dest, ".hg", f)
+            dst = os.path.join(dest_path, ".hg", f)
             try:
                 util.copyfiles(src, dst)
             except OSError, inst:
                 if inst.errno != errno.ENOENT:
                     raise
 
-        repo = hg.repository(ui, dest)
+	# we need to re-init the repo after manually copying the data into it
+        dest_repo = hg.repository(ui, dest)
 
     else:
         revs = None
         if opts['rev']:
-            if not other.local():
+            if not src_repo.local():
                 error = _("clone -r not supported yet for remote repositories.")
                 raise util.Abort(error)
             else:
-                revs = [other.lookup(rev) for rev in opts['rev']]
-        repo = hg.repository(ui, dest, create=1)
-        repo.pull(other, heads = revs)
-
-    f = repo.opener("hgrc", "w", text=True)
-    f.write("[paths]\n")
-    f.write("default = %s\n" % abspath)
-    f.close()
-
-    if not opts['noupdate']:
-        doupdate(repo.ui, repo)
-
-    d.close()
+                revs = [src_repo.lookup(rev) for rev in opts['rev']]
+
+        if dest_repo.local():
+            dest_repo.pull(src_repo, heads = revs)
+        elif src_repo.local():
+            src_repo.push(dest_repo, revs = revs)
+        else:
+            error = _("clone from remote to remote not supported.")
+            raise util.Abort(error)
+
+    if dest_repo.local():
+        f = dest_repo.opener("hgrc", "w", text=True)
+        f.write("[paths]\n")
+        f.write("default = %s\n" % abspath)
+        f.close()
+
+        if not opts['noupdate']:
+            doupdate(dest_repo.ui, dest_repo)
+
+    if d:
+        d.close()
 
 def commit(ui, repo, *pats, **opts):
     """commit the specified files or all outstanding changes
@@ -1065,21 +1123,21 @@ def docopy(ui, repo, pats, opts, wlock):
                 ui.warn(_('%s: not overwriting - file exists\n') %
                         reltarget)
                 return
-            if not opts['after'] and not opts['dry_run']:
+            if not opts['after'] and not opts.get('dry_run'):
                 os.unlink(reltarget)
         if opts['after']:
             if not os.path.exists(reltarget):
                 return
         else:
             targetdir = os.path.dirname(reltarget) or '.'
-            if not os.path.isdir(targetdir) and not opts['dry_run']:
+            if not os.path.isdir(targetdir) and not opts.get('dry_run'):
                 os.makedirs(targetdir)
             try:
                 restore = repo.dirstate.state(abstarget) == 'r'
-                if restore and not opts['dry_run']:
+                if restore and not opts.get('dry_run'):
                     repo.undelete([abstarget], wlock)
                 try:
-                    if not opts['dry_run']:
+                    if not opts.get('dry_run'):
                         shutil.copyfile(relsrc, reltarget)
                         shutil.copymode(relsrc, reltarget)
                     restore = False
@@ -1099,7 +1157,7 @@ def docopy(ui, repo, pats, opts, wlock):
         if ui.verbose or not exact:
             ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
         targets[abstarget] = abssrc
-        if abstarget != origsrc and not opts['dry_run']:
+        if abstarget != origsrc and not opts.get('dry_run'):
             repo.copy(origsrc, abstarget, wlock)
         copied.append((abssrc, relsrc, exact))
 
@@ -1225,7 +1283,7 @@ def debugcomplete(ui, cmd='', **opts):
         options = []
         otables = [globalopts]
         if cmd:
-            aliases, entry = find(cmd)
+            aliases, entry = findcmd(cmd)
             otables.append(entry[1])
         for t in otables:
             for o in t:
@@ -1707,11 +1765,16 @@ def import_(ui, repo, patch1, *patches, 
     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).  From and Subject headers of email
+    message are used as default committer and commit message.  All
+    text/plain body parts before first diff are 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 "-".
     """
@@ -1727,79 +1790,98 @@ def import_(ui, repo, patch1, *patches, 
 
     # 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()
+                    if message:
+                        fp.write(message)
+                        fp.write('\n')
+                    for line in payload[:m.start(0)].splitlines():
+                        if line.startswith('# HG changeset patch'):
+                            ui.debug(_('patch generated by hg export\n'))
+                            hgpatch = True
+                            # drop earlier commit message content
+                            fp.seek(0)
+                            fp.truncate()
+                        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')
+                    message = fp.getvalue()
                     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
@@ -1839,7 +1921,10 @@ def incoming(ui, repo, source="default",
                 # use the created uncompressed bundlerepo
                 other = bundlerepo.bundlerepository(ui, repo.root, fname)
 
-        o = other.changelog.nodesbetween(incoming)[0]
+        revs = None
+        if opts['rev']:
+            revs = [other.lookup(rev) for rev in opts['rev']]
+        o = other.changelog.nodesbetween(incoming, revs)[0]
         if opts['newest_first']:
             o.reverse()
         displayer = show_changeset(ui, other, opts)
@@ -1866,8 +1951,6 @@ def init(ui, dest="."):
 
     If no directory is given, the current directory is used.
     """
-    if not os.path.exists(dest):
-        os.mkdir(dest)
     hg.repository(ui, dest, create=1)
 
 def locate(ui, repo, *pats, **opts):
@@ -2049,13 +2132,16 @@ def outgoing(ui, repo, dest=None, **opts
         ui.setconfig("ui", "ssh", opts['ssh'])
     if opts['remotecmd']:
         ui.setconfig("ui", "remotecmd", opts['remotecmd'])
+    revs = None
+    if opts['rev']:
+        revs = [repo.lookup(rev) for rev in opts['rev']]
 
     other = hg.repository(ui, dest)
     o = repo.findoutgoing(other, force=opts['force'])
     if not o:
         ui.status(_("no changes found\n"))
         return
-    o = repo.changelog.nodesbetween(o)[0]
+    o = repo.changelog.nodesbetween(o, revs)[0]
     if opts['newest_first']:
         o.reverse()
     displayer = show_changeset(ui, repo, opts)
@@ -2322,7 +2408,7 @@ def rename(ui, repo, *pats, **opts):
         if ui.verbose or not exact:
             ui.status(_('removing %s\n') % rel)
         names.append(abs)
-    if not opts['dry_run']:
+    if not opts.get('dry_run'):
         repo.remove(names, True, wlock)
     return errs
 
@@ -2986,11 +3072,13 @@ table = {
           ('n', 'newest-first', None, _('show newest record first')),
           ('', 'bundle', '', _('file to store the bundles into')),
           ('p', 'patch', None, _('show patch')),
+          ('r', 'rev', [], _('a specific revision you would like to pull')),
           ('', 'template', '', _('display with template')),
           ('e', 'ssh', '', _('specify ssh command to use')),
           ('', 'remotecmd', '',
            _('specify hg command to run on the remote side'))],
-         _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
+         _('hg incoming [-p] [-n] [-M] [-r REV]...'
+           ' [--bundle FILENAME] [SOURCE]')),
     "^init": (init, [], _('hg init [DEST]')),
     "locate":
         (locate,
@@ -3028,12 +3116,13 @@ table = {
            _('run even when remote repository is unrelated')),
           ('p', 'patch', None, _('show patch')),
           ('', 'style', '', _('display using template map file')),
+          ('r', 'rev', [], _('a specific revision you would like to push')),
           ('n', 'newest-first', None, _('show newest record first')),
           ('', 'template', '', _('display with template')),
           ('e', 'ssh', '', _('specify ssh command to use')),
           ('', 'remotecmd', '',
            _('specify hg command to run on the remote side'))],
-         _('hg outgoing [-M] [-p] [-n] [DEST]')),
+         _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
     "^parents":
         (parents,
          [('b', 'branches', None, _('show branches')),
@@ -3213,7 +3302,7 @@ def findpossible(cmd):
 
     return choice
 
-def find(cmd):
+def findcmd(cmd):
     """Return (aliases, command table entry) for command string."""
     choice = findpossible(cmd)
 
@@ -3250,7 +3339,7 @@ def parse(ui, args):
 
     if args:
         cmd, args = args[0], args[1:]
-        aliases, i = find(cmd)
+        aliases, i = findcmd(cmd)
         cmd = aliases[0]
         defaults = ui.config("defaults", cmd)
         if defaults:
@@ -3277,6 +3366,19 @@ def parse(ui, args):
 
     return (cmd, cmd and i[0] or None, args, options, cmdoptions)
 
+external = {}
+
+def findext(name):
+    '''return module with given extension name'''
+    try:
+        return external[name]
+    except KeyError:
+        dotname = '.' + name
+        for k, v in external.iteritems():
+            if k.endswith('.' + name) or v.__name__ == name:
+                return v
+        raise KeyError(name)
+    
 def dispatch(args):
     for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
         num = getattr(signal, name, None)
@@ -3288,7 +3390,6 @@ def dispatch(args):
         sys.stderr.write(_("abort: %s\n") % inst)
         return -1
 
-    external = []
     for x in u.extensions():
         try:
             if x[1]:
@@ -3305,10 +3406,12 @@ def dispatch(args):
                         mod = getattr(mod, comp)
                     return mod
                 try:
-                    mod = importh("hgext." + x[0])
+                    name = 'hgext.' + x[0]
+                    mod = importh(name)
                 except ImportError:
-                    mod = importh(x[0])
-            external.append(mod)
+                    name = x[0]
+                    mod = importh(name)
+            external[name] = mod
         except (util.SignalInterrupt, KeyboardInterrupt):
             raise
         except Exception, inst:
@@ -3316,7 +3419,7 @@ def dispatch(args):
             if u.print_exc():
                 return 1
 
-    for x in external:
+    for x in external.itervalues():
         uisetup = getattr(x, 'uisetup', None)
         if uisetup:
             uisetup(u)
@@ -3372,7 +3475,7 @@ def dispatch(args):
                     if not repo:
                         repo = hg.repository(u, path=path)
                     u = repo.ui
-                    for x in external:
+                    for x in external.itervalues():
                         if hasattr(x, 'reposetup'):
                             x.reposetup(u, repo)
                 except hg.RepoError:
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -33,6 +33,9 @@ def local_(ui, path, create=0):
         path = path[5:]
     return localrepo.localrepository(ui, path, create)
 
+def ssh_(ui, path, create=0):
+    return sshrepo.sshrepository(ui, path, create)
+
 def old_http(ui, path):
     ui.warn(_("old-http:// syntax is deprecated, "
               "please use static-http:// instead\n"))
@@ -50,7 +53,7 @@ schemes = {
     'http': lambda ui, path: httprepo.httprepository(ui, path),
     'https': lambda ui, path: httprepo.httpsrepository(ui, path),
     'old-http': old_http,
-    'ssh': lambda ui, path: sshrepo.sshrepository(ui, path),
+    'ssh': ssh_,
     'static-http': static_http,
     }
 
@@ -60,11 +63,11 @@ def repository(ui, path=None, create=0):
     if scheme:
         c = scheme.find(':')
         scheme = c >= 0 and scheme[:c]
-    try:
-        ctor = schemes.get(scheme) or schemes['file']
-        if create:
+    ctor = schemes.get(scheme) or schemes['file']
+    if create:
+        try:
             return ctor(ui, path, create)
-        return ctor(ui, path)
-    except TypeError:
-        raise util.Abort(_('cannot create new repository over "%s" protocol') %
-                         scheme)
+        except TypeError:
+            raise util.Abort(_('cannot create new repository over "%s" protocol') %
+                             scheme)
+    return ctor(ui, path)
--- a/mercurial/hgweb/common.py
+++ b/mercurial/hgweb/common.py
@@ -17,7 +17,7 @@ def get_mtime(repo_path):
     else:
         return os.stat(hg_path).st_mtime
 
-def staticfile(directory, fname):
+def staticfile(directory, fname, req):
     """return a file inside directory with guessed content-type header
 
     fname always uses '/' as directory separator and isn't allowed to
@@ -36,7 +36,9 @@ def staticfile(directory, fname):
     try:
         os.stat(path)
         ct = mimetypes.guess_type(path)[0] or "text/plain"
-        return "Content-type: %s\n\n%s" % (ct, file(path).read())
+        req.header([('Content-type', ct),
+                    ('Content-length', os.path.getsize(path))])
+        return file(path).read()
     except (TypeError, OSError):
         # illegal fname or unreadable file
         return ""
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -10,9 +10,8 @@ import os
 import os.path
 import mimetypes
 from mercurial.demandload import demandload
-demandload(globals(), "re zlib ConfigParser cStringIO sys tempfile")
+demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
-demandload(globals(), "mercurial.hgweb.request:hgrequest")
 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
 from mercurial.node import *
 from mercurial.i18n import gettext as _
@@ -651,9 +650,27 @@ class hgweb(object):
             raise Exception("suspicious path")
         return p
 
-    def run(self, req=hgrequest()):
+    def run(self):
+        if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
+            raise RuntimeError("This function is only intended to be called while running as a CGI script.")
+        import mercurial.hgweb.wsgicgi as wsgicgi
+        from request import wsgiapplication
+        def make_web_app():
+            return self
+        wsgicgi.launch(wsgiapplication(make_web_app))
+
+    def run_wsgi(self, req):
         def header(**map):
-            yield self.t("header", **map)
+            header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
+            msg = mimetools.Message(header_file, 0)
+            req.header(msg.items())
+            yield header_file.read()
+
+        def rawfileheader(**map):
+            req.header([('Content-type', map['mimetype']),
+                        ('Content-disposition', 'filename=%s' % map['file']),
+                        ('Content-length', str(len(map['raw'])))])
+            yield ''
 
         def footer(**map):
             yield self.t("footer",
@@ -712,6 +729,7 @@ class hgweb(object):
                                                "repo": self.reponame,
                                                "header": header,
                                                "footer": footer,
+                                               "rawfileheader": rawfileheader,
                                                })
 
         if not req.form.has_key('cmd'):
@@ -724,7 +742,6 @@ class hgweb(object):
             method(req)
         else:
             req.write(self.t("error"))
-        req.done()
 
     def do_changelog(self, req):
         hi = self.repo.changelog.count() - 1
@@ -830,7 +847,7 @@ class hgweb(object):
         static = self.repo.ui.config("web", "static",
                                      os.path.join(self.templatepath,
                                                   "static"))
-        req.write(staticfile(static, fname)
+        req.write(staticfile(static, fname, req)
                   or self.t("error", error="%r not found" % fname))
 
     def do_capabilities(self, req):
@@ -915,10 +932,11 @@ class hgweb(object):
 
                 try:
                     ret = self.repo.addchangegroup(fp, 'serve')
-                    req.write('%d\n' % ret)
-                    req.write(sys.stdout.getvalue())
                 finally:
+                    val = sys.stdout.getvalue()
                     sys.stdout = old_stdout
+                req.write('%d\n' % ret)
+                req.write(val)
             finally:
                 lock.release()
         finally:
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -8,10 +8,9 @@
 
 import os
 from mercurial.demandload import demandload
-demandload(globals(), "ConfigParser")
+demandload(globals(), "ConfigParser mimetools cStringIO")
 demandload(globals(), "mercurial:ui,hg,util,templater")
 demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb")
-demandload(globals(), "mercurial.hgweb.request:hgrequest")
 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
 from mercurial.i18n import gettext as _
 
@@ -47,9 +46,21 @@ class hgwebdir(object):
                         self.repos.append((name.lstrip(os.sep), repo))
             self.repos.sort()
 
-    def run(self, req=hgrequest()):
+    def run(self):
+        if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
+            raise RuntimeError("This function is only intended to be called while running as a CGI script.")
+        import mercurial.hgweb.wsgicgi as wsgicgi
+        from request import wsgiapplication
+        def make_web_app():
+            return self
+        wsgicgi.launch(wsgiapplication(make_web_app))
+
+    def run_wsgi(self, req):
         def header(**map):
-            yield tmpl("header", **map)
+            header_file = cStringIO.StringIO(''.join(tmpl("header", **map)))
+            msg = mimetools.Message(header_file, 0)
+            req.header(msg.items())
+            yield header_file.read()
 
         def footer(**map):
             yield tmpl("footer", motd=self.motd, **map)
@@ -122,7 +133,7 @@ class hgwebdir(object):
             real = dict(self.repos).get(virtual)
             if real:
                 try:
-                    hgweb(real).run(req)
+                    hgweb(real).run_wsgi(req)
                 except IOError, inst:
                     req.write(tmpl("error", error=inst.strerror))
                 except hg.RepoError, inst:
@@ -133,7 +144,7 @@ class hgwebdir(object):
             if req.form.has_key('static'):
                 static = os.path.join(templater.templatepath(), "static")
                 fname = req.form['static'][0]
-                req.write(staticfile(static, fname)
+                req.write(staticfile(static, fname, req)
                           or tmpl("error", error="%r not found" % fname))
             else:
                 sortable = ["name", "description", "contact", "lastchange"]
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -10,13 +10,48 @@ from mercurial.demandload import demandl
 demandload(globals(), "socket sys cgi os errno")
 from mercurial.i18n import gettext as _
 
-class hgrequest(object):
-    def __init__(self, inp=None, out=None, env=None):
-        self.inp = inp or sys.stdin
-        self.out = out or sys.stdout
-        self.env = env or os.environ
+class wsgiapplication(object):
+    def __init__(self, destmaker):
+        self.destmaker = destmaker
+
+    def __call__(self, wsgienv, start_response):
+        return _wsgirequest(self.destmaker(), wsgienv, start_response)
+
+class _wsgioutputfile(object):
+    def __init__(self, request):
+        self.request = request
+
+    def write(self, data):
+        self.request.write(data)
+    def writelines(self, lines):
+        for line in lines:
+            self.write(line)
+    def flush(self):
+        return None
+    def close(self):
+        return None
+
+class _wsgirequest(object):
+    def __init__(self, destination, wsgienv, start_response):
+        version = wsgienv['wsgi.version']
+        if (version < (1,0)) or (version >= (2, 0)):
+            raise RuntimeError("Unknown and unsupported WSGI version %d.%d" \
+                               % version)
+        self.inp = wsgienv['wsgi.input']
+        self.out = _wsgioutputfile(self)
+        self.server_write = None
+        self.err = wsgienv['wsgi.errors']
+        self.threaded = wsgienv['wsgi.multithread']
+        self.multiprocess = wsgienv['wsgi.multiprocess']
+        self.run_once = wsgienv['wsgi.run_once']
+        self.env = wsgienv
         self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
-        self.will_close = True
+        self.start_response = start_response
+        self.headers = []
+        destination.run_wsgi(self)
+
+    def __iter__(self):
+        return iter([])
 
     def read(self, count=-1):
         return self.inp.read(count)
@@ -27,23 +62,22 @@ class hgrequest(object):
                 for part in thing:
                     self.write(part)
             else:
+                thing = str(thing)
+                if self.server_write is None:
+                    if not self.headers:
+                        raise RuntimeError("request.write called before headers sent (%s)." % thing)
+                    self.server_write = self.start_response('200 Script output follows',
+                                                            self.headers)
+                    self.start_response = None
+                    self.headers = None
                 try:
-                    self.out.write(str(thing))
+                    self.server_write(thing)
                 except socket.error, inst:
                     if inst[0] != errno.ECONNRESET:
                         raise
 
-    def done(self):
-        if self.will_close:
-            self.inp.close()
-            self.out.close()
-        else:
-            self.out.flush()
-
     def header(self, headers=[('Content-type','text/html')]):
-        for header in headers:
-            self.out.write("%s: %s\r\n" % header)
-        self.out.write("\r\n")
+        self.headers.extend(headers)
 
     def httphdr(self, type, filename=None, length=0, headers={}):
         headers = headers.items()
@@ -51,12 +85,6 @@ class hgrequest(object):
         if filename:
             headers.append(('Content-disposition', 'attachment; filename=%s' %
                             filename))
-        # we do not yet support http 1.1 chunked transfer, so we have
-        # to force connection to close if content-length not known
         if length:
             headers.append(('Content-length', str(length)))
-            self.will_close = False
-        else:
-            headers.append(('Connection', 'close'))
-            self.will_close = True
         self.header(headers)
--- a/mercurial/hgweb/server.py
+++ b/mercurial/hgweb/server.py
@@ -10,7 +10,7 @@ from mercurial.demandload import demandl
 import os, sys, errno
 demandload(globals(), "urllib BaseHTTPServer socket SocketServer")
 demandload(globals(), "mercurial:ui,hg,util,templater")
-demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:hgrequest")
+demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:wsgiapplication")
 from mercurial.i18n import gettext as _
 
 def _splitURI(uri):
@@ -25,6 +25,17 @@ def _splitURI(uri):
         path, query = uri, ''
     return urllib.unquote(path), query
 
+class _error_logger(object):
+    def __init__(self, handler):
+        self.handler = handler
+    def flush(self):
+        pass
+    def write(str):
+        self.writelines(str.split('\n'))
+    def writelines(seq):
+        for msg in seq:
+            self.handler.log_error("HG error:  %s", msg)
+
 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
     def __init__(self, *args, **kargs):
         self.protocol_version = 'HTTP/1.1'
@@ -76,17 +87,72 @@ class _hgwebhandler(object, BaseHTTPServ
         length = self.headers.getheader('content-length')
         if length:
             env['CONTENT_LENGTH'] = length
-        accept = []
-        for line in self.headers.getallmatchingheaders('accept'):
-            if line[:1] in "\t\n\r ":
-                accept.append(line.strip())
-            else:
-                accept = accept + line[7:].split(',')
-        env['HTTP_ACCEPT'] = ','.join(accept)
+        for header in [h for h in self.headers.keys() \
+                       if h not in ('content-type', 'content-length')]:
+            hkey = 'HTTP_' + header.replace('-', '_').upper()
+            hval = self.headers.getheader(header)
+            hval = hval.replace('\n', '').strip()
+            if hval:
+                env[hkey] = hval
+        env['SERVER_PROTOCOL'] = self.request_version
+        env['wsgi.version'] = (1, 0)
+        env['wsgi.url_scheme'] = 'http'
+        env['wsgi.input'] = self.rfile
+        env['wsgi.errors'] = _error_logger(self)
+        env['wsgi.multithread'] = isinstance(self.server,
+                                             SocketServer.ThreadingMixIn)
+        env['wsgi.multiprocess'] = isinstance(self.server,
+                                              SocketServer.ForkingMixIn)
+        env['wsgi.run_once'] = 0
+
+        self.close_connection = True
+        self.saved_status = None
+        self.saved_headers = []
+        self.sent_headers = False
+        self.length = None
+        req = self.server.reqmaker(env, self._start_response)
+        for data in req:
+            if data:
+                self._write(data)
 
-        req = hgrequest(self.rfile, self.wfile, env)
-        self.send_response(200, "Script output follows")
-        self.close_connection = self.server.make_and_run_handler(req)
+    def send_headers(self):
+        if not self.saved_status:
+            raise AssertionError("Sending headers before start_response() called")
+        saved_status = self.saved_status.split(None, 1)
+        saved_status[0] = int(saved_status[0])
+        self.send_response(*saved_status)
+        should_close = True
+        for h in self.saved_headers:
+            self.send_header(*h)
+            if h[0].lower() == 'content-length':
+                should_close = False
+                self.length = int(h[1])
+        if should_close:
+            self.send_header('Connection', 'close')
+        self.close_connection = should_close
+        self.end_headers()
+        self.sent_headers = True
+
+    def _start_response(self, http_status, headers, exc_info=None):
+        code, msg = http_status.split(None, 1)
+        code = int(code)
+        self.saved_status = http_status
+        bad_headers = ('connection', 'transfer-encoding')
+        self.saved_headers = [ h for h in headers \
+                               if h[0].lower() not in bad_headers ]
+        return self._write
+
+    def _write(self, data):
+        if not self.saved_status:
+            raise AssertionError("data written before start_response() called")
+        elif not self.sent_headers:
+            self.send_headers()
+        if self.length is not None:
+            if len(data) > self.length:
+                raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
+            self.length = self.length - len(data)
+        self.wfile.write(data)
+        self.wfile.flush()
 
 def create_server(ui, repo):
     use_threads = True
@@ -126,8 +192,9 @@ def create_server(ui, repo):
             self.webdir_conf = webdir_conf
             self.webdirmaker = hgwebdir
             self.repoviewmaker = hgweb
+            self.reqmaker = wsgiapplication(self.make_handler)
 
-        def make_and_run_handler(self, req):
+        def make_handler(self):
             if self.webdir_conf:
                 hgwebobj = self.webdirmaker(self.webdir_conf)
             elif self.repo is not None:
@@ -135,8 +202,7 @@ def create_server(ui, repo):
                                                              repo.origroot))
             else:
                 raise hg.RepoError(_('no repo found'))
-            hgwebobj.run(req)
-            return req.will_close
+            return hgwebobj
 
     class IPv6HTTPServer(MercurialHTTPServer):
         address_family = getattr(socket, 'AF_INET6', None)
@@ -144,7 +210,7 @@ def create_server(ui, repo):
         def __init__(self, *args, **kwargs):
             if self.address_family is None:
                 raise hg.RepoError(_('IPv6 not available on this system'))
-            super(IPv6HTTPServer, self).__init__(*args, **kargs)
+            super(IPv6HTTPServer, self).__init__(*args, **kwargs)
 
     if use_ipv6:
         return IPv6HTTPServer((address, port), _hgwebhandler)
new file mode 100644
--- /dev/null
+++ b/mercurial/hgweb/wsgicgi.py
@@ -0,0 +1,70 @@
+# hgweb/wsgicgi.py - CGI->WSGI translator
+#
+# Copyright 2006 Eric Hopper <hopper@omnifarious.org>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+#
+# This was originally copied from the public domain code at
+# http://www.python.org/dev/peps/pep-0333/#the-server-gateway-side
+
+import os, sys
+
+def launch(application):
+
+    environ = dict(os.environ.items())
+    environ['wsgi.input']        = sys.stdin
+    environ['wsgi.errors']       = sys.stderr
+    environ['wsgi.version']      = (1,0)
+    environ['wsgi.multithread']  = False
+    environ['wsgi.multiprocess'] = True
+    environ['wsgi.run_once']    = True
+
+    if environ.get('HTTPS','off') in ('on','1'):
+        environ['wsgi.url_scheme'] = 'https'
+    else:
+        environ['wsgi.url_scheme'] = 'http'
+
+    headers_set = []
+    headers_sent = []
+    out = sys.stdout
+
+    def write(data):
+        if not headers_set:
+             raise AssertionError("write() before start_response()")
+
+        elif not headers_sent:
+             # Before the first output, send the stored headers
+             status, response_headers = headers_sent[:] = headers_set
+             out.write('Status: %s\r\n' % status)
+             for header in response_headers:
+                 out.write('%s: %s\r\n' % header)
+             out.write('\r\n')
+
+        out.write(data)
+        out.flush()
+
+    def start_response(status,response_headers,exc_info=None):
+        if exc_info:
+            try:
+                if headers_sent:
+                    # Re-raise original exception if headers sent
+                    raise exc_info[0], exc_info[1], exc_info[2]
+            finally:
+                exc_info = None     # avoid dangling circular ref
+        elif headers_set:
+            raise AssertionError("Headers already set!")
+
+        headers_set[:] = [status,response_headers]
+        return write
+
+    result = application(environ, start_response)
+    try:
+        for data in result:
+            if data:    # don't send headers until body appears
+                write(data)
+        if not headers_sent:
+            write('')   # send headers now if body was empty
+    finally:
+        if hasattr(result,'close'):
+            result.close()
--- a/mercurial/httprepo.py
+++ b/mercurial/httprepo.py
@@ -20,16 +20,22 @@ class passwordmgr(urllib2.HTTPPasswordMg
     def find_user_password(self, realm, authuri):
         authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
             self, realm, authuri)
-        if authinfo != (None, None):
-            return authinfo
+        user, passwd = authinfo
+        if user and passwd:
+            return (user, passwd)
 
         if not self.ui.interactive:
             raise util.Abort(_('http authorization required'))
 
         self.ui.write(_("http authorization required\n"))
         self.ui.status(_("realm: %s\n") % realm)
-        user = self.ui.prompt(_("user:"), default=None)
-        passwd = self.ui.getpass()
+        if user:
+            self.ui.status(_("user: %s\n") % user)
+        else:
+            user = self.ui.prompt(_("user:"), default=None)
+
+        if not passwd:
+            passwd = self.ui.getpass()
 
         self.add_password(realm, authuri, user, passwd)
         return (user, passwd)
@@ -85,6 +91,22 @@ class httphandler(keepalive.HTTPHandler)
     def http_open(self, req):
         return self.do_open(httpconnection, req)
 
+class httpsconnection(httplib.HTTPSConnection):
+    # must be able to send big bundle as stream.
+
+    def send(self, data):
+        if isinstance(data, str):
+            httplib.HTTPSConnection.send(self, data)
+        else:
+            # if auth required, some data sent twice, so rewind here
+            data.seek(0)
+            for chunk in util.filechunkiter(data):
+                httplib.HTTPSConnection.send(self, chunk)
+
+class httpshandler(urllib2.HTTPSHandler):
+    def https_open(self, req):
+        return self.do_open(httpsconnection, req)
+
 class httprepository(remoterepository):
     def __init__(self, ui, path):
         self.caps = None
@@ -148,12 +170,13 @@ class httprepository(remoterepository):
 
         passmgr = passwordmgr(ui)
         if user:
-            ui.debug(_('will use user %s, password %s for http auth\n') %
-                     (user, '*' * len(passwd)))
+            ui.debug(_('http auth: user %s, password %s\n') %
+                     (user, passwd and '*' * len(passwd) or 'not set'))
             passmgr.add_password(None, host, user, passwd or '')
 
         opener = urllib2.build_opener(
             handler,
+            httpshandler(),
             urllib2.HTTPBasicAuthHandler(passmgr),
             urllib2.HTTPDigestAuthHandler(passmgr))
 
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -74,6 +74,8 @@ class localrepository(object):
         self.transhandle = None
 
         if create:
+	    if not os.path.exists(path):
+		os.mkdir(path)
             os.mkdir(self.path)
             os.mkdir(self.join("data"))
 
@@ -1651,8 +1653,8 @@ class localrepository(object):
         linear_path = (pa == p1 or pa == p2)
 
         if allow and linear_path:
-            raise util.Abort(_("there is nothing to merge, "
-                               "just use 'hg update'"))
+            raise util.Abort(_("there is nothing to merge, just use "
+                               "'hg update' or look at 'hg heads'"))
         if allow and not forcemerge:
             if modified or added or removed:
                 raise util.Abort(_("outstanding uncommitted changes"))
--- a/mercurial/mpatch.c
+++ b/mercurial/mpatch.c
@@ -43,7 +43,7 @@ static uint32_t ntohl(uint32_t x)
 /* not windows */
 # include <sys/types.h>
 # include <arpa/inet.h>
-# include <stdint.h>
+# include <inttypes.h>
 #endif
 
 static char mpatch_doc[] = "Efficient binary patching.";
--- a/mercurial/sshrepo.py
+++ b/mercurial/sshrepo.py
@@ -12,7 +12,7 @@ from demandload import *
 demandload(globals(), "hg os re stat util")
 
 class sshrepository(remoterepository):
-    def __init__(self, ui, path):
+    def __init__(self, ui, path, create=0):
         self.url = path
         self.ui = ui
 
@@ -30,6 +30,25 @@ class sshrepository(remoterepository):
 
         sshcmd = self.ui.config("ui", "ssh", "ssh")
         remotecmd = self.ui.config("ui", "remotecmd", "hg")
+
+        if create:
+            try:
+                self.validate_repo(ui, sshcmd, args, remotecmd)
+                return # the repo is good, nothing more to do
+            except hg.RepoError:
+                pass
+
+            cmd = '%s %s "%s init %s"'
+            cmd = cmd % (sshcmd, args, remotecmd, self.path)
+
+            ui.note('running %s\n' % cmd)
+            res = os.system(cmd)
+            if res != 0:
+                raise hg.RepoError(_("could not create remote repo"))
+
+        self.validate_repo(ui, sshcmd, args, remotecmd)
+
+    def validate_repo(self, ui, sshcmd, args, remotecmd):
         cmd = '%s %s "%s -R %s serve --stdio"'
         cmd = cmd % (sshcmd, args, remotecmd, self.path)
 
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -202,7 +202,7 @@ def fill(text, width):
     if para_re is None:
         para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
         space_re = re.compile(r'  +')
-        
+
     def findparas():
         start = 0
         while True:
@@ -221,10 +221,21 @@ def fill(text, width):
         fp.write(rest)
     return fp.getvalue()
 
+def firstline(text):
+    '''return the first line of text'''
+    try:
+        return text.splitlines(1)[0].rstrip('\r\n')
+    except IndexError:
+        return ''
+
 def isodate(date):
     '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
     return util.datestr(date, format='%Y-%m-%d %H:%M')
 
+def hgdate(date):
+    '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
+    return "%d %d" % date
+
 def nl2br(text):
     '''replace raw newlines with xhtml line breaks.'''
     return text.replace('\n', '<br/>\n')
@@ -280,8 +291,9 @@ common_filters = {
     "escape": lambda x: cgi.escape(x, True),
     "fill68": lambda x: fill(x, width=68),
     "fill76": lambda x: fill(x, width=76),
-    "firstline": lambda x: x.splitlines(1)[0].rstrip('\r\n'),
+    "firstline": firstline,
     "tabindent": lambda x: indent(x, '\t'),
+    "hgdate": hgdate,
     "isodate": isodate,
     "obfuscate": obfuscate,
     "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--",
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -99,10 +99,10 @@ class ui(object):
         """Return a list of comma/space separated strings"""
         result = self.config(section, name)
         if result is None:
-            return []
-        else:
-            return result.replace(",", " ").split()
-
+            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)):
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -859,6 +859,49 @@ def datestr(date=None, format='%a %b %d 
         s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
     return s
 
+def strdate(string, format='%a %b %d %H:%M:%S %Y'):
+    """parse a localized time string and return a (unixtime, offset) tuple.
+    if the string cannot be parsed, ValueError is raised."""
+    def hastimezone(string):
+        return (string[-4:].isdigit() and
+               (string[-5] == '+' or string[-5] == '-') and
+               string[-6].isspace())
+
+    if hastimezone(string):
+        date, tz = string[:-6], string[-5:]
+        tz = int(tz)
+        offset = - 3600 * (tz / 100) - 60 * (tz % 100)
+    else:
+        date, offset = string, 0
+    when = int(time.mktime(time.strptime(date, format))) + offset
+    return when, offset
+
+def parsedate(string, formats=('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M')):
+    """parse a localized time string and return a (unixtime, offset) tuple.
+    The date may be a "unixtime offset" string or in one of the specified
+    formats."""
+    try:
+        when, offset = map(int, string.split(' '))
+    except ValueError:
+        for format in formats:
+            try:
+                when, offset = strdate(string, format)
+            except ValueError:
+                pass
+            else:
+                break
+        else:
+            raise ValueError(_('invalid date: %r') % string)
+    # validate explicit (probably user-specified) date and
+    # time zone offset. values must fit in signed 32 bits for
+    # current 32-bit linux runtimes. timezones go from UTC-12
+    # to UTC+14
+    if abs(when) > 0x7fffffff:
+        raise ValueError(_('date exceeds 32 bits: %d') % when)
+    if offset < -50400 or offset > 43200:
+        raise ValueError(_('impossible time zone offset: %d') % offset)
+    return when, offset
+
 def shortuser(user):
     """Return a short representation of a user name or email address."""
     f = user.find('@')
--- a/templates/changeset-raw.tmpl
+++ b/templates/changeset-raw.tmpl
@@ -1,7 +1,7 @@
 #header#
 # HG changeset patch
 # User #author#
-# Date #date|date#
+# Date #date|hgdate#
 # Node ID #node#
 #parent%changesetparent#
 #desc#
--- a/templates/map-raw
+++ b/templates/map-raw
@@ -5,10 +5,10 @@ difflineplus = '#line#'
 difflineminus = '#line#'
 difflineat = '#line#'
 diffline = '#line#'
-changesetparent = '# parent: #node#'
-changesetchild = '# child: #node#'
+changesetparent = '# Parent #node#'
+changesetchild = '# Child #node#'
 filenodelink = ''
-filerevision = 'Content-Type: #mimetype#\nContent-Disposition: filename=#file#\n\n#raw#'
+filerevision = '#rawfileheader##raw#'
 fileline = '#line#'
 diffblock = '#lines#'
 filediff = filediff-raw.tmpl
new file mode 100755
--- /dev/null
+++ b/tests/get-with-headers.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+__doc__ = """This does HTTP get requests given a host:port and path and returns
+a subset of the headers plus the body of the result."""
+
+import httplib, sys
+headers = [h.lower() for h in sys.argv[3:]]
+conn = httplib.HTTPConnection(sys.argv[1])
+conn.request("GET", sys.argv[2])
+response = conn.getresponse()
+print response.status, response.reason
+for h in headers:
+    if response.getheader(h, None) is not None:
+        print "%s: %s" % (h, response.getheader(h))
+print
+sys.stdout.write(response.read())
--- a/tests/test-globalopts
+++ b/tests/test-globalopts
@@ -62,7 +62,7 @@ echo %% --time
 hg --cwd a --time tip 2>&1 | grep '^Time:' | sed 's/[0-9][0-9]*/x/g'
 
 echo %% --version
-hg --version -q | sed 's/version \([a-f0-9+]*\|unknown\)/version xxx/'
+hg --version -q | sed 's/version [^)]*/version xxx/'
 
 echo %% -h/--help
 hg -h
--- a/tests/test-globalopts.out
+++ b/tests/test-globalopts.out
@@ -127,7 +127,7 @@ list of commands (use "hg help -v" to sh
  export     dump the header and diffs for one or more changesets
  grep       search for a pattern in specified files and revisions
  heads      show current repository heads
- help       show help for a given command or all commands
+ help       show help for a command, extension, or list of commands
  identify   print information about the working copy
  import     import an ordered set of patches
  incoming   show new changesets found in source
@@ -173,7 +173,7 @@ list of commands (use "hg help -v" to sh
  export     dump the header and diffs for one or more changesets
  grep       search for a pattern in specified files and revisions
  heads      show current repository heads
- help       show help for a given command or all commands
+ help       show help for a command, extension, or list of commands
  identify   print information about the working copy
  import     import an ordered set of patches
  incoming   show new changesets found in source
--- a/tests/test-help.out
+++ b/tests/test-help.out
@@ -51,7 +51,7 @@ list of commands (use "hg help -v" to sh
  export     dump the header and diffs for one or more changesets
  grep       search for a pattern in specified files and revisions
  heads      show current repository heads
- help       show help for a given command or all commands
+ help       show help for a command, extension, or list of commands
  identify   print information about the working copy
  import     import an ordered set of patches
  incoming   show new changesets found in source
@@ -93,7 +93,7 @@ list of commands (use "hg help -v" to sh
  export     dump the header and diffs for one or more changesets
  grep       search for a pattern in specified files and revisions
  heads      show current repository heads
- help       show help for a given command or all commands
+ help       show help for a command, extension, or list of commands
  identify   print information about the working copy
  import     import an ordered set of patches
  incoming   show new changesets found in source
--- a/tests/test-http-proxy
+++ b/tests/test-http-proxy
@@ -26,5 +26,5 @@ http_proxy=http://user:passwd@localhost:
 echo %% bad host:port for proxy
 http_proxy=localhost:20061 hg clone --config http_proxy.always=True http://localhost:20059/ f
 
-kill $(cat proxy.pid a/hg.pid)
+kill `cat proxy.pid a/hg.pid`
 exit 0
--- a/tests/test-hup
+++ b/tests/test-hup
@@ -7,7 +7,7 @@ hg serve --stdio < p &
 P=$!
 (echo lock; echo addchangegroup; sleep 5) > p &
 Q=$!
-sleep 1
+sleep 3
 kill -HUP $P
 wait
 ls .hg
new file mode 100755
--- /dev/null
+++ b/tests/test-import
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+hg init a
+echo line 1 > a/a
+hg --cwd a ci -d '0 0' -Ama
+
+echo line 2 >> a/a
+hg --cwd a ci -u someone -d '1 0' -m'second change'
+
+echo % import exported patch
+hg clone -r0 a b
+hg --cwd a export tip > tip.patch
+hg --cwd b import ../tip.patch
+echo % message should be same
+hg --cwd b tip | grep 'second change'
+echo % committer should be same
+hg --cwd b tip | grep someone
+rm -rf b
+
+echo % import of plain diff should fail without message
+hg clone -r0 a b
+hg --cwd a diff -r0:1 > tip.patch
+hg --cwd b import ../tip.patch
+rm -rf b
+
+echo % import of plain diff should be ok with message
+hg clone -r0 a b
+hg --cwd a diff -r0:1 > tip.patch
+hg --cwd b import -mpatch ../tip.patch
+rm -rf b
+
+echo % import from stdin
+hg clone -r0 a b
+hg --cwd a export tip | hg --cwd b import -
+rm -rf b
+
+echo % override commit message
+hg clone -r0 a b
+hg --cwd a export tip | hg --cwd b import -m 'override' -
+hg --cwd b tip | grep override
+rm -rf b
+
+cat > mkmsg.py <<EOF
+import email.Message, sys
+msg = email.Message.Message()
+msg.set_payload('email commit message\n' + open('tip.patch').read())
+msg['Subject'] = 'email patch'
+msg['From'] = 'email patcher'
+sys.stdout.write(msg.as_string())
+EOF
+
+echo % plain diff in email, subject, message body
+hg clone -r0 a b
+hg --cwd a diff -r0:1 > tip.patch
+python mkmsg.py > msg.patch
+hg --cwd b import ../msg.patch
+hg --cwd b tip | grep email
+rm -rf b
+
+echo % plain diff in email, no subject, message body
+hg clone -r0 a b
+grep -v '^Subject:' msg.patch | hg --cwd b import -
+rm -rf b
+
+echo % plain diff in email, subject, no message body
+hg clone -r0 a b
+grep -v '^email ' msg.patch | hg --cwd b import -
+rm -rf b
+
+echo % plain diff in email, no subject, no message body, should fail
+hg clone -r0 a b
+grep -v '^\(Subject\|email\)' msg.patch | hg --cwd b import -
+rm -rf b
+
+echo % hg export in email, should use patch header
+hg clone -r0 a b
+hg --cwd a export tip > tip.patch
+python mkmsg.py | hg --cwd b import -
+hg --cwd b tip | grep second
+rm -rf b
+
new file mode 100644
--- /dev/null
+++ b/tests/test-import.out
@@ -0,0 +1,103 @@
+adding a
+% import exported patch
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../tip.patch
+patching file a
+% message should be same
+summary:     second change
+% committer should be same
+user:        someone
+% import of plain diff should fail without message
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../tip.patch
+patching file a
+transaction abort!
+rollback completed
+% import of plain diff should be ok with message
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../tip.patch
+patching file a
+% import from stdin
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+% override commit message
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+summary:     override
+% plain diff in email, subject, message body
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../msg.patch
+patching file a
+user:        email patcher
+summary:     email patch
+% plain diff in email, no subject, message body
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+% plain diff in email, subject, no message body
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+% plain diff in email, no subject, no message body, should fail
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+transaction abort!
+rollback completed
+% hg export in email, should use patch header
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying patch from stdin
+patching file a
+summary:     second change
--- a/tests/test-incoming-outgoing
+++ b/tests/test-incoming-outgoing
@@ -14,8 +14,10 @@ cd ..
 hg init new
 # http incoming
 http_proxy= hg -R new incoming http://localhost:20059/
+http_proxy= hg -R new incoming -r 4 http://localhost:20059/
 # local incoming
 hg -R new incoming test
+hg -R new incoming -r 4 test
 
 # test with --bundle
 http_proxy= hg -R new incoming --bundle test.hg http://localhost:20059/
@@ -42,5 +44,6 @@ hg verify
 cd ..
 hg -R test-dev outgoing test
 http_proxy= hg -R test-dev outgoing http://localhost:20059/
+http_proxy= hg -R test-dev outgoing -r 11 http://localhost:20059/
 
 kill `cat test/hg.pid`
--- a/tests/test-incoming-outgoing.out
+++ b/tests/test-incoming-outgoing.out
@@ -75,6 +75,31 @@ user:        test
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     4
 
+changeset:   0:9cb21d99fe27
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     0
+
+changeset:   1:d717f5dfad6a
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     1
+
+changeset:   2:c0d6b86da426
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     2
+
+changeset:   3:dfacbd43b3fe
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     3
+
+changeset:   4:1f3a964b6022
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     4
+
 changeset:   5:c028bcc7a28a
 user:        test
 date:        Mon Jan 12 13:46:40 1970 +0000
@@ -121,6 +146,31 @@ user:        test
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     4
 
+changeset:   0:9cb21d99fe27
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     0
+
+changeset:   1:d717f5dfad6a
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     1
+
+changeset:   2:c0d6b86da426
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     2
+
+changeset:   3:dfacbd43b3fe
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     3
+
+changeset:   4:1f3a964b6022
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     4
+
 changeset:   5:c028bcc7a28a
 user:        test
 date:        Mon Jan 12 13:46:40 1970 +0000
@@ -270,3 +320,19 @@ user:        test
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     13
 
+searching for changes
+changeset:   9:3741c3ad1096
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     9
+
+changeset:   10:de4143c8d9a5
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     10
+
+changeset:   11:0e1c188b9a7a
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     11
+
new file mode 100755
--- /dev/null
+++ b/tests/test-oldcgi
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+hg init test
+
+cat >hgweb.cgi <<HGWEB
+#!/usr/bin/env python
+#
+# An example CGI script to use hgweb, edit as necessary
+
+import cgitb, os, sys
+cgitb.enable()
+
+# sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
+from mercurial import hgweb
+
+h = hgweb.hgweb("test", "Empty test repository")
+h.run()
+HGWEB
+chmod 755 hgweb.cgi
+
+cat >hgweb.config <<HGWEBDIRCONF
+[paths]
+test = test
+HGWEBDIRCONF
+
+cat >hgwebdir.cgi <<HGWEBDIR
+#!/usr/bin/env python
+#
+# An example CGI script to export multiple hgweb repos, edit as necessary
+
+import cgitb, sys
+cgitb.enable()
+
+# sys.path.insert(0, "/path/to/python/lib") # if not a system-wide install
+from mercurial import hgweb
+
+# The config file looks like this.  You can have paths to individual
+# repos, collections of repos in a directory tree, or both.
+#
+# [paths]
+# virtual/path = /real/path
+# virtual/path = /real/path
+#
+# [collections]
+# /prefix/to/strip/off = /root/of/tree/full/of/repos
+#
+# collections example: say directory tree /foo contains repos /foo/bar,
+# /foo/quux/baz.  Give this config section:
+#   [collections]
+#   /foo = /foo
+# Then repos will list as bar and quux/baz.
+
+# Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
+# or use a dictionary with entries like 'virtual/path': '/real/path'
+
+h = hgweb.hgwebdir("hgweb.config")
+h.run()
+HGWEBDIR
+chmod 755 hgwebdir.cgi
+
+DOCUMENT_ROOT="/var/www/hg"; export DOCUMENT_ROOT
+GATEWAY_INTERFACE="CGI/1.1"; export GATEWAY_INTERFACE
+HTTP_ACCEPT="text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; export HTTP_ACCEPT
+HTTP_ACCEPT_CHARSET="ISO-8859-1,utf-8;q=0.7,*;q=0.7"; export HTTP_ACCEPT_CHARSET
+HTTP_ACCEPT_ENCODING="gzip,deflate"; export HTTP_ACCEPT_ENCODING
+HTTP_ACCEPT_LANGUAGE="en-us,en;q=0.5"; export HTTP_ACCEPT_LANGUAGE
+HTTP_CACHE_CONTROL="max-age=0"; export HTTP_CACHE_CONTROL
+HTTP_CONNECTION="keep-alive"; export HTTP_CONNECTION
+HTTP_HOST="hg.omnifarious.org"; export HTTP_HOST
+HTTP_KEEP_ALIVE="300"; export HTTP_KEEP_ALIVE
+HTTP_USER_AGENT="Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.0.4) Gecko/20060608 Ubuntu/dapper-security Firefox/1.5.0.4"; export HTTP_USER_AGENT
+PATH_INFO="/"; export PATH_INFO
+PATH_TRANSLATED="/var/www/hg/index.html"; export PATH_TRANSLATED
+QUERY_STRING=""; export QUERY_STRING
+REMOTE_ADDR="127.0.0.2"; export REMOTE_ADDR
+REMOTE_PORT="44703"; export REMOTE_PORT
+REQUEST_METHOD="GET"; export REQUEST_METHOD
+REQUEST_URI="/test/"; export REQUEST_URI
+SCRIPT_FILENAME="/home/hopper/hg_public/test.cgi"; export SCRIPT_FILENAME
+SCRIPT_NAME="/test"; export SCRIPT_NAME
+SCRIPT_URI="http://hg.omnifarious.org/test/"; export SCRIPT_URI
+SCRIPT_URL="/test/"; export SCRIPT_URL
+SERVER_ADDR="127.0.0.1"; export SERVER_ADDR
+SERVER_ADMIN="eric@localhost"; export SERVER_ADMIN
+SERVER_NAME="hg.omnifarious.org"; export SERVER_NAME
+SERVER_PORT="80"; export SERVER_PORT
+SERVER_PROTOCOL="HTTP/1.1"; export SERVER_PROTOCOL
+SERVER_SIGNATURE="<address>Apache/2.0.53 (Fedora) Server at hg.omnifarious.org Port 80</address>\; export SERVER_SIGNATURE
+"
+SERVER_SOFTWARE="Apache/2.0.53 (Fedora)"; export SERVER_SOFTWARE
+./hgweb.cgi >page1 2>&1 ; echo $?
+./hgwebdir.cgi >page2 2>&1 ; echo $?
+PATH_INFO="/test/"
+PATH_TRANSLATED="/var/something/test.cgi"
+REQUEST_URI="/test/test/"
+SCRIPT_URI="http://hg.omnifarious.org/test/test/"
+SCRIPT_URL="/test/test/"
+./hgwebdir.cgi >page3 2>&1 ; echo $?
+fgrep -i error page1 page2 page3 && exit 1
+exit 0
new file mode 100644
--- /dev/null
+++ b/tests/test-oldcgi.out
@@ -0,0 +1,3 @@
+0
+0
+0
new file mode 100755
--- /dev/null
+++ b/tests/test-parse-date
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+hg init
+echo "test-parse-date" > a
+hg add a
+hg ci -d "2006-02-01 13:00:30" -m "rev 0"
+echo "hi!" >> a
+hg ci -d "2006-02-01 13:00:30 -0500" -m "rev 1"
+hg tag -d "2006-04-15 13:30" "Hi"
+hg backout --merge -d "2006-04-15 13:30 +0200" -m "rev 3" 1
+hg ci -d "1150000000 14400" -m "rev 4 (merge)"
+echo "fail" >> a
+hg ci -d "should fail" -m "fail"
+hg ci -d "100000000000000000 1400" -m "fail"
+hg ci -d "100000 1400000" -m "fail"
+hg log --template '{date|date}\n'
new file mode 100644
--- /dev/null
+++ b/tests/test-parse-date.out
@@ -0,0 +1,19 @@
+reverting a
+changeset 3:107ce1ee2b43 backs out changeset 1:25a1420a55f8
+merging with changeset 2:99a1acecff55
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+abort: invalid date: 'should fail'
+transaction abort!
+rollback completed
+abort: date exceeds 32 bits: 100000000000000000
+transaction abort!
+rollback completed
+abort: impossible time zone offset: 1400000
+transaction abort!
+rollback completed
+Sun Jun 11 00:26:40 2006 -0400
+Sat Apr 15 13:30:00 2006 +0200
+Sat Apr 15 13:30:00 2006 +0000
+Wed Feb 01 13:00:30 2006 -0500
+Wed Feb 01 13:00:30 2006 +0000
--- a/tests/test-pull-pull-corruption2
+++ b/tests/test-pull-pull-corruption2
@@ -20,5 +20,6 @@ hg -R version2 pull source1 &
 sleep 1
 
 hg clone --pull -U version2 corrupted
+wait
 hg -R corrupted verify
 hg -R version2 verify
new file mode 100755
--- /dev/null
+++ b/tests/test-ui-config
@@ -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 "---"
new file mode 100644
--- /dev/null
+++ b/tests/test-ui-config.out
@@ -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']
+---
--- a/tests/test-up-local-change.out
+++ b/tests/test-up-local-change.out
@@ -43,7 +43,7 @@ user:        test
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     1
 
-abort: there is nothing to merge, just use 'hg update'
+abort: there is nothing to merge, just use 'hg update' or look at 'hg heads'
 failed
 changeset:   0:33aaa84a386b
 user:        test
new file mode 100755
--- /dev/null
+++ b/tests/test-webraw
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+hg init test
+cd test
+cat >sometext.txt <<ENDSOME
+This is just some random text
+that will go inside the file and take a few lines.
+It is very boring to read, but computers don't
+care about things like that.
+ENDSOME
+hg add sometext.txt
+hg commit -d "1 0" -m "Just some text"
+hg serve -p 20059 -A access.log -E error.log -d --pid-file=hg.pid
+("$TESTDIR/get-with-headers.py" localhost:20059 '/?f=f165dc289438;file=sometext.txt;style=raw' content-type content-length content-disposition) >getoutput.txt &
+
+sleep 5
+kill `cat hg.pid`
+sleep 1 # wait for server to scream and die
+cat getoutput.txt
+cat access.log error.log | \
+  sed 's/^[^ ]*\( [^[]*\[\)[^]]*\(\].*\)$/host\1date\2/'
new file mode 100644
--- /dev/null
+++ b/tests/test-webraw.out
@@ -0,0 +1,10 @@
+200 Script output follows
+content-type: text/plain
+content-length: 157
+content-disposition: filename=sometext.txt
+
+This is just some random text
+that will go inside the file and take a few lines.
+It is very boring to read, but computers don't
+care about things like that.
+host - - [date] "GET /?f=f165dc289438;file=sometext.txt;style=raw HTTP/1.1" 200 -