changeset 5848:e90a7a3c28a4

Merge with mpm
author Bryan O'Sullivan <bos@serpentine.com>
date Fri, 11 Jan 2008 16:51:54 -0800
parents e52383c7e7ab (current diff) 02884e56c217 (diff)
children a76395713691
files mercurial/util_win32.py
diffstat 21 files changed, 283 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/convert/common.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/hgext/convert/common.py	Fri Jan 11 16:51:54 2008 -0800
@@ -1,5 +1,6 @@
 # common code for the convert extension
 import base64, errno
+import os
 import cPickle as pickle
 from mercurial import util
 from mercurial.i18n import _
@@ -212,7 +213,7 @@
     def postrun(self):
         pass
 
-    def _run(self, cmd, *args, **kwargs):
+    def _cmdline(self, cmd, *args, **kwargs):
         cmdline = [self.command, cmd] + list(args)
         for k, v in kwargs.iteritems():
             if len(k) == 1:
@@ -230,7 +231,10 @@
         cmdline += ['<', util.nulldev]
         cmdline = ' '.join(cmdline)
         self.ui.debug(cmdline, '\n')
+        return cmdline
 
+    def _run(self, cmd, *args, **kwargs):
+        cmdline = self._cmdline(cmd, *args, **kwargs)
         self.prerun()
         try:
             return util.popen(cmdline)
@@ -256,6 +260,47 @@
         self.checkexit(status, output)
         return output
 
+    def getargmax(self):
+        if '_argmax' in self.__dict__:
+            return self._argmax
+
+        # POSIX requires at least 4096 bytes for ARG_MAX
+        self._argmax = 4096
+        try:
+            self._argmax = os.sysconf("SC_ARG_MAX")
+        except:
+            pass
+
+        # Windows shells impose their own limits on command line length,
+        # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
+        # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
+        # details about cmd.exe limitations.
+
+        # Since ARG_MAX is for command line _and_ environment, lower our limit
+        # (and make happy Windows shells while doing this).
+
+        self._argmax = self._argmax/2 - 1
+        return self._argmax
+
+    def limit_arglist(self, arglist, cmd, *args, **kwargs):
+        limit = self.getargmax() - len(self._cmdline(cmd, *args, **kwargs))
+        bytes = 0
+        fl = []
+        for fn in arglist:
+            b = len(fn) + 3
+            if bytes + b < limit or len(fl) == 0:
+                fl.append(fn)
+                bytes += b
+            else:
+                yield fl
+                fl = [fn]
+                bytes = b
+        if fl:
+            yield fl
+
+    def xargs(self, arglist, cmd, *args, **kwargs):
+        for l in self.limit_arglist(arglist, cmd, *args, **kwargs):
+            self.run0(cmd, *(list(args) + l), **kwargs)
 
 class mapfile(dict):
     def __init__(self, ui, path):
--- a/hgext/convert/hg.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/hgext/convert/hg.py	Fri Jan 11 16:51:54 2008 -0800
@@ -180,7 +180,7 @@
             except hg.RepoError, inst:
                 tagparent = nullid
             self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
-                                date, tagparent, nullid)
+                                date, tagparent, nullid, extra=extra)
             return hex(self.repo.changelog.tip())
 
     def setfilemapmode(self, active):
--- a/hgext/convert/subversion.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/hgext/convert/subversion.py	Fri Jan 11 16:51:54 2008 -0800
@@ -53,7 +53,7 @@
     if os.path.isdir(path):
         path = os.path.normpath(os.path.abspath(path))
         if os.name == 'nt':
-            path = '/' + path.replace('\\', '/')
+            path = '/' + util.normpath(path)
         return 'file://%s' % path
     return path
 
@@ -415,7 +415,9 @@
                 if ent.copyfrom_path:
                     copyfrom_path = get_entry_from_path(ent.copyfrom_path)
                     if copyfrom_path:
-                        self.ui.debug("Copied to %s from %s@%s\n" % (entry, copyfrom_path, ent.copyfrom_rev))
+                        self.ui.debug("Copied to %s from %s@%s\n" %
+                                      (entrypath, copyfrom_path,
+                                       ent.copyfrom_rev))
                         # It's probably important for hg that the source
                         # exists in the revision's parent, not just the
                         # ent.copyfrom_rev
@@ -707,27 +709,6 @@
 class svn_sink(converter_sink, commandline):
     commit_re = re.compile(r'Committed revision (\d+).', re.M)
 
-    # iterates sublist of given list for concatenated length is within limit
-    def limit_arglist(self, files):
-        if os.name != 'nt':
-            yield files
-            return
-        # When I tested on WinXP, limit = 2500 is NG, 2400 is OK
-        limit = 2000
-        bytes = 0
-        fl = []
-        for fn in files:
-            b = len(fn) + 1
-            if bytes + b < limit:
-                fl.append(fn)
-                bytes += b
-            else:
-                yield fl
-                fl = [fn]
-                bytes = b
-        if fl:
-            yield fl
-
     def prerun(self):
         if self.wc:
             os.chdir(self.wc)
@@ -770,7 +751,7 @@
                               os.path.basename(path))
                     commandline(ui, 'svnadmin').run0('create', path)
                     created = path
-                path = path.replace('\\', '/')
+                path = util.normpath(path)
                 if not path.startswith('/'):
                     path = '/' + path
                 path = 'file://' + path
@@ -866,14 +847,12 @@
                     if not os.path.exists(self.wjoin(d, '.svn', 'entries'))]
         if add_dirs:
             add_dirs.sort()
-            for fl in self.limit_arglist(add_dirs):
-                self.run('add', non_recursive=True, quiet=True, *fl)
+            self.xargs(add_dirs, 'add', non_recursive=True, quiet=True)
         return add_dirs
 
     def add_files(self, files):
         if files:
-            for fl in self.limit_arglist(files):
-                self.run('add', quiet=True, *fl)
+            self.xargs(files, 'add', quiet=True)
         return files
 
     def tidy_dirs(self, names):
@@ -907,18 +886,15 @@
                 self._copyfile(s, d)
             self.copies = []
         if self.delete:
-            for fl in self.limit_arglist(self.delete):
-                self.run0('delete', *fl)
+            self.xargs(self.delete, 'delete')
             self.delete = []
         entries.update(self.add_files(files.difference(entries)))
         entries.update(self.tidy_dirs(entries))
         if self.delexec:
-            for fl in self.limit_arglist(self.delexec):
-                self.run0('propdel', 'svn:executable', *fl)
+            self.xargs(self.delexec, 'propdel', 'svn:executable')
             self.delexec = []
         if self.setexec:
-            for fl in self.limit_arglist(self.setexec):
-                self.run0('propset', 'svn:executable', '*', *fl)
+            self.xargs(self.setexec, 'propset', 'svn:executable', '*')
             self.setexec = []
 
         fd, messagefile = tempfile.mkstemp(prefix='hg-convert-')
--- a/hgext/keyword.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/hgext/keyword.py	Fri Jan 11 16:51:54 2008 -0800
@@ -1,6 +1,6 @@
 # keyword.py - $Keyword$ expansion for Mercurial
 #
-# Copyright 2007 Christian Ebert <blacktrash@gmx.net>
+# Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -150,9 +150,8 @@
 
     def process(self, node, data, expand):
         '''Returns a tuple: data, count.
-        Count is number of keywords/keyword substitutions, indicates
-        to caller whether to act on file containing data.
-        Keywords in data are expanded, if templater was initialized.'''
+        Count is number of keywords/keyword substitutions,
+        telling caller whether to act on file containing data.'''
         if util.binary(data):
             return data, None
         if expand:
@@ -175,8 +174,7 @@
         _kwtemplater.path = path
 
     def kwctread(self, node, expand):
-        '''Reads expanding and counting keywords
-        (only called from kwtemplater.overwrite).'''
+        '''Reads expanding and counting keywords, called from _overwrite.'''
         data = super(kwfilelog, self).read(node)
         return _kwtemplater.process(node, data, expand)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/win32mbcs.py	Fri Jan 11 16:51:54 2008 -0800
@@ -0,0 +1,158 @@
+# win32mbcs.py -- MBCS filename support for Mercurial on Windows
+#
+# Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
+#
+# Version: 0.1
+# Author:  Shun-ichi Goto <shunichi.goto@gmail.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+#
+"""Allow to use shift_jis/big5 filenames on Windows.
+
+There is a well known issue "0x5c problem" on Windows.  It is a
+trouble on handling path name as raw encoded byte sequence of
+problematic encodings like shift_jis or big5.  The primary intent
+of this extension is to allow using such a encoding on Mercurial
+without strange file operation error.
+
+By enabling this extension, hook mechanism is activated and some
+functions are altered.  Usually, this encoding is your local encoding
+on your system by default. So you can get benefit simply by enabling
+this extension.
+
+The encoding for filename is same one for terminal by default.  You
+can change the encoding by setting HGENCODING environment variable.
+
+This extension is usefull for:
+ * Japanese Windows user using shift_jis encoding.
+ * Chinese Windows user using big5 encoding.
+ * Users who want to use a repository created with such a encoding.
+
+Note: Unix people does not need to use this extension.
+
+"""
+
+import os
+from mercurial.i18n import _
+from mercurial import util
+
+__all__ = ['install', 'uninstall', 'reposetup']
+
+
+# codec and alias names of sjis and big5 to be faked.
+_problematic_encodings = util.frozenset([
+        'big5', 'big5-tw', 'csbig5',
+        'big5hkscs', 'big5-hkscs', 'hkscs',
+        'cp932', '932', 'ms932', 'mskanji', 'ms-kanji',
+        'shift_jis', 'csshiftjis', 'shiftjis', 'sjis', 's_jis',
+        'shift_jis_2004', 'shiftjis2004', 'sjis_2004', 'sjis2004',
+        'shift_jisx0213', 'shiftjisx0213', 'sjisx0213', 's_jisx0213',
+        ])
+
+# attribute name to store original function
+_ORIGINAL = '_original'
+
+_ui = None
+
+def decode_with_check(arg):
+    if isinstance(arg, tuple):
+        return tuple(map(decode_with_check, arg))
+    elif isinstance(arg, list):
+        return map(decode_with_check, arg)
+    elif isinstance(arg, str):
+        uarg = arg.decode(util._encoding)
+        if arg == uarg.encode(util._encoding):
+            return uarg
+        else:
+            raise UnicodeError("Not local encoding")
+    else:
+        return arg
+
+def encode_with_check(arg):
+    if isinstance(arg, tuple):
+        return tuple(map(encode_with_check, arg))
+    elif isinstance(arg, list):
+        return map(encode_with_check, arg)
+    elif isinstance(arg, unicode):
+        ret = arg.encode(util._encoding)
+        return ret
+    else:
+        return arg
+
+def wrap(func):
+    
+    def wrapped(*args):
+        # check argument is unicode, then call original
+        for arg in args:
+            if isinstance(arg, unicode):
+                return func(*args)
+        # make decoded argument list into uargs
+        try:
+            args = decode_with_check(args)
+        except UnicodeError, exc:
+            # If not encoded with _local_fs_encoding, report it then
+            # continue with calling original function.
+            _ui.warn(_("WARNING: [win32mbcs] filename conversion fail for" +
+                     " %s: '%s'\n") % (util._encoding, args))
+            return func(*args)
+        # call as unicode operation, then return with encoding
+        return encode_with_check(func(*args))
+
+    # fake is only for relevant environment.
+    if hasattr(func, _ORIGINAL) or \
+            util._encoding.lower() not in _problematic_encodings:
+        return func
+    else:
+        f = wrapped
+        f.__name__ = func.__name__
+        setattr(f, _ORIGINAL, func)   # hold original to restore
+        return f
+
+def unwrap(func):
+    return getattr(func, _ORIGINAL, func)
+
+def install():
+    # wrap some python functions and mercurial functions
+    # to handle raw bytes on Windows.
+    # NOTE: dirname and basename is safe because they use result
+    # of os.path.split()
+    global _ui
+    if not _ui:
+        from mercurial import ui
+        _ui = ui.ui()
+    os.path.join = wrap(os.path.join)
+    os.path.split = wrap(os.path.split) 
+    os.path.splitext = wrap(os.path.splitext)
+    os.path.splitunc = wrap(os.path.splitunc)
+    os.path.normpath = wrap(os.path.normpath)
+    os.path.normcase = wrap(os.path.normcase)
+    os.makedirs = wrap(os.makedirs)
+    util.endswithsep = wrap(util.endswithsep)
+    util.splitpath = wrap(util.splitpath)
+
+def uninstall():
+    # restore original functions.
+    os.path.join = unwrap(os.path.join)
+    os.path.split = unwrap(os.path.split) 
+    os.path.splitext = unwrap(os.path.splitext)
+    os.path.splitunc = unwrap(os.path.splitunc)
+    os.path.normpath = unwrap(os.path.normpath)
+    os.path.normcase = unwrap(os.path.normcase)
+    os.makedirs = unwrap(os.makedirs)
+    util.endswithsep = unwrap(util.endswithsep)
+    util.splitpath = unwrap(util.splitpath)
+
+
+def reposetup(ui, repo):
+    # TODO: decide use of config section for this extension
+    global _ui
+    _ui = ui
+    if not os.path.supports_unicode_filenames:
+        ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
+        return
+    # install features of this extension
+    install()
+    ui.debug(_("[win32mbcs] activeted with encoding: %s\n") % util._encoding)
+
+# win32mbcs.py ends here
--- a/mercurial/archival.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/archival.py	Fri Jan 11 16:51:54 2008 -0800
@@ -15,7 +15,7 @@
     safe for consumers.'''
 
     if prefix:
-        prefix = prefix.replace('\\', '/')
+        prefix = util.normpath(prefix)
     else:
         if not isinstance(dest, str):
             raise ValueError('dest must be string if no prefix')
--- a/mercurial/cmdutil.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/cmdutil.py	Fri Jan 11 16:51:54 2008 -0800
@@ -462,7 +462,7 @@
         if len(pats) > 1 or util.patkind(pats[0], None)[0]:
             raise util.Abort(_('with multiple sources, destination must be an '
                                'existing directory'))
-        if dest.endswith(os.sep) or os.altsep and dest.endswith(os.altsep):
+        if util.endswithsep(dest):
             raise util.Abort(_('destination %s is not a directory') % dest)
 
     tfn = targetpathfn
@@ -902,7 +902,7 @@
 
 def finddate(ui, repo, date):
     """Find the tipmost changeset that matches the given date spec"""
-    df = util.matchdate(date + " to " + date)
+    df = util.matchdate(date)
     get = util.cachefunc(lambda r: repo.changectx(r).changeset())
     changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
     results = {}
--- a/mercurial/commands.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/commands.py	Fri Jan 11 16:51:54 2008 -0800
@@ -2393,7 +2393,7 @@
         s.serve_forever()
 
     parentui = ui.parentui or ui
-    optlist = ("name templates style address port ipv6"
+    optlist = ("name templates style address port prefix ipv6"
                " accesslog errorlog webdir_conf certificate")
     for o in optlist.split():
         if opts[o]:
@@ -3024,6 +3024,7 @@
           ('E', 'errorlog', '', _('name of error log file to write to')),
           ('p', 'port', 0, _('port to use (default: 8000)')),
           ('a', 'address', '', _('address to use')),
+          ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
           ('n', 'name', '',
            _('name to show in web pages (default: working dir)')),
           ('', 'webdir-conf', '', _('name of the webdir config file'
--- a/mercurial/dirstate.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/dirstate.py	Fri Jan 11 16:51:54 2008 -0800
@@ -74,7 +74,7 @@
         if cwd == self._root: return ''
         # self._root ends with a path separator if self._root is '/' or 'C:\'
         rootsep = self._root
-        if not rootsep.endswith(os.sep):
+        if not util.endswithsep(rootsep):
             rootsep += os.sep
         if cwd.startswith(rootsep):
             return cwd[len(rootsep):]
@@ -87,7 +87,7 @@
             cwd = self.getcwd()
         path = util.pathto(self._root, cwd, f)
         if self._slash:
-            return path.replace(os.sep, '/')
+            return util.normpath(path)
         return path
 
     def __getitem__(self, key):
@@ -410,7 +410,7 @@
 
         # self._root may end with a path separator when self._root == '/'
         common_prefix_len = len(self._root)
-        if not self._root.endswith(os.sep):
+        if not util.endswithsep(self._root):
             common_prefix_len += 1
 
         normpath = util.normpath
--- a/mercurial/hgweb/hgweb_mod.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/hgweb/hgweb_mod.py	Fri Jan 11 16:51:54 2008 -0800
@@ -8,7 +8,7 @@
 
 import os, mimetypes, re, mimetools, cStringIO
 from mercurial.node import *
-from mercurial import mdiff, ui, hg, util, archival, patch
+from mercurial import mdiff, ui, hg, util, archival, patch, hook
 from mercurial import revlog, templater
 from common import ErrorResponse, get_mtime, style_map, paritygen, get_contact
 from request import wsgirequest
@@ -85,6 +85,7 @@
         else:
             self.repo = repo
 
+        hook.redirect(True)
         self.mtime = -1
         self.reponame = name
         self.archives = 'zip', 'gz', 'bz2'
--- a/mercurial/hgweb/server.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/hgweb/server.py	Fri Jan 11 16:51:54 2008 -0800
@@ -76,7 +76,7 @@
         self.do_POST()
 
     def do_hgweb(self):
-        path_info, query = _splitURI(self.path)
+        path, query = _splitURI(self.path)
 
         env = {}
         env['GATEWAY_INTERFACE'] = 'CGI/1.1'
@@ -84,8 +84,8 @@
         env['SERVER_NAME'] = self.server.server_name
         env['SERVER_PORT'] = str(self.server.server_port)
         env['REQUEST_URI'] = self.path
-        env['SCRIPT_NAME'] = ''
-        env['PATH_INFO'] = path_info
+        env['SCRIPT_NAME'] = self.server.prefix
+        env['PATH_INFO'] = path[len(self.server.prefix):]
         env['REMOTE_HOST'] = self.client_address[0]
         env['REMOTE_ADDR'] = self.client_address[0]
         if query:
@@ -206,6 +206,7 @@
         myui = repo.ui
     address = myui.config("web", "address", "")
     port = int(myui.config("web", "port", 8000))
+    prefix = myui.config("web", "prefix", "").rstrip("/")
     use_ipv6 = myui.configbool("web", "ipv6")
     webdir_conf = myui.config("web", "webdir_conf")
     ssl_cert = myui.config("web", "certificate")
@@ -254,6 +255,7 @@
                 addr = socket.gethostname()
 
             self.addr, self.port = addr, port
+            self.prefix = prefix
 
             if ssl_cert:
                 try:
--- a/mercurial/hook.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/hook.py	Fri Jan 11 16:51:54 2008 -0800
@@ -6,7 +6,7 @@
 # of the GNU General Public License, incorporated herein by reference.
 
 from i18n import _
-import util
+import util, os, sys
 
 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
     '''call python hook. hook is callable object, looked up as
@@ -79,8 +79,18 @@
         ui.warn(_('warning: %s hook %s\n') % (name, desc))
     return r
 
+_redirect = False
+def redirect(state):
+    _redirect = state
+
 def hook(ui, repo, name, throw=False, **args):
     r = False
+
+    if _redirect:
+        # temporarily redirect stdout to stderr
+        oldstdout = os.dup(sys.stdout.fileno())
+        os.dup2(sys.stderr.fileno(), sys.stdout.fileno())
+
     hooks = [(hname, cmd) for hname, cmd in ui.configitems("hooks")
              if hname.split(".", 1)[0] == name and cmd]
     hooks.sort()
@@ -94,3 +104,6 @@
             r = _exthook(ui, repo, hname, cmd, args, throw) or r
     return r
 
+    if _redirect:
+        os.dup2(oldstdout, sys.stdout.fileno())
+        os.close(oldstdout)
--- a/mercurial/sshserver.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/sshserver.py	Fri Jan 11 16:51:54 2008 -0800
@@ -8,7 +8,7 @@
 
 from i18n import _
 from node import *
-import os, streamclone, sys, tempfile, util
+import os, streamclone, sys, tempfile, util, hook
 
 class sshserver(object):
     def __init__(self, ui, repo):
@@ -18,6 +18,7 @@
         self.fin = sys.stdin
         self.fout = sys.stdout
 
+        hook.redirect(True)
         sys.stdout = sys.stderr
 
         # Prevent insertion/deletion of CRs
--- a/mercurial/util.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/util.py	Fri Jan 11 16:51:54 2008 -0800
@@ -328,7 +328,7 @@
         if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
             return os.path.join(root, localpath(n2))
         n2 = '/'.join((pconvert(root), n2))
-    a, b = n1.split(os.sep), n2.split('/')
+    a, b = splitpath(n1), n2.split('/')
     a.reverse()
     b.reverse()
     while a and b and a[-1] == b[-1]:
@@ -341,7 +341,7 @@
     """return the canonical path of myname, given cwd and root"""
     if root == os.sep:
         rootsep = os.sep
-    elif root.endswith(os.sep):
+    elif endswithsep(root):
         rootsep = root
     else:
         rootsep = root + os.sep
@@ -692,7 +692,7 @@
         if path in self.audited:
             return
         normpath = os.path.normcase(path)
-        parts = normpath.split(os.sep)
+        parts = splitpath(normpath)
         if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
             or os.pardir in parts):
             raise Abort(_("path contains illegal component: %s") % path)
@@ -713,14 +713,15 @@
                       os.path.isdir(os.path.join(curpath, '.hg'))):
                     raise Abort(_('path %r is inside repo %r') %
                                 (path, prefix))
-
+        parts.pop()
         prefixes = []
-        for c in strutil.rfindall(normpath, os.sep):
-            prefix = normpath[:c]
+        for n in range(len(parts)):
+            prefix = os.sep.join(parts)
             if prefix in self.auditeddir:
                 break
             check(prefix)
             prefixes.append(prefix)
+            parts.pop()
 
         self.audited.add(path)
         # only add prefixes to the cache after checking everything: we don't
@@ -882,6 +883,18 @@
     """return True if patches should be applied in binary mode by default."""
     return os.name == 'nt'
 
+def endswithsep(path):
+    '''Check path ends with os.sep or os.altsep.'''
+    return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
+
+def splitpath(path):
+    '''Split path by os.sep.
+    Note that this function does not use os.altsep because this is
+    an alternative of simple "xxx.split(os.sep)".
+    It is recommended to use os.path.normpath() before using this
+    function if need.'''
+    return path.split(os.sep)
+
 # Platform specific variants
 if os.name == 'nt':
     import msvcrt
@@ -979,7 +992,7 @@
         msvcrt.setmode(fd.fileno(), os.O_BINARY)
 
     def pconvert(path):
-        return path.replace("\\", "/")
+        return '/'.join(splitpath(path))
 
     def localpath(path):
         return path.replace('/', '\\')
--- a/mercurial/util_win32.py	Fri Jan 11 21:20:27 2008 +0100
+++ b/mercurial/util_win32.py	Fri Jan 11 16:51:54 2008 -0800
@@ -227,6 +227,9 @@
     # but does not work at all. wrap win32 file api instead.
 
     def __init__(self, name, mode='rb'):
+        self.closed = False
+        self.name = name
+        self.mode = mode
         access = 0
         if 'r' in mode or '+' in mode:
             access |= win32file.GENERIC_READ
@@ -250,9 +253,6 @@
                                                0)
         except pywintypes.error, err:
             raise WinIOError(err, name)
-        self.closed = False
-        self.name = name
-        self.mode = mode
 
     def __iter__(self):
         for line in self.read().splitlines(True):
--- a/templates/error.tmpl	Fri Jan 11 21:20:27 2008 +0100
+++ b/templates/error.tmpl	Fri Jan 11 16:51:54 2008 -0800
@@ -6,7 +6,7 @@
 <h2>Mercurial Error</h2>
 
 <p>
-An error occured while processing your request:
+An error occurred while processing your request:
 </p>
 <p>
 #error|escape#
--- a/templates/gitweb/error.tmpl	Fri Jan 11 21:20:27 2008 +0100
+++ b/templates/gitweb/error.tmpl	Fri Jan 11 16:51:54 2008 -0800
@@ -17,7 +17,7 @@
 
 <div class="page_body">
 <br/>
-<i>An error occured while processing your request</i><br/>
+<i>An error occurred while processing your request</i><br/>
 <br/>
 {error|escape}
 </div>
--- a/templates/gitweb/map	Fri Jan 11 21:20:27 2008 +0100
+++ b/templates/gitweb/map	Fri Jan 11 16:51:54 2008 -0800
@@ -24,10 +24,10 @@
 filelog = filelog.tmpl
 fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><span class="linenr">   #linenumber#</span> #line|escape#</pre></div>'
 annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="#url#annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
-difflineplus = '<div style="color:#008800;">#line|escape#</div>'
-difflineminus = '<div style="color:#cc0000;">#line|escape#</div>'
-difflineat = '<div style="color:#990099;">#line|escape#</div>'
-diffline = '<div>#line|escape#</div>'
+difflineplus = '<span style="color:#008800;">#line|escape#</span>'
+difflineminus = '<span style="color:#cc0000;">#line|escape#</span>'
+difflineat = '<span style="color:#990099;">#line|escape#</span>'
+diffline = '<span>#line|escape#</span>'
 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
 changesetparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
 filerevparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
--- a/tests/test-debugcomplete.out	Fri Jan 11 21:20:27 2008 +0100
+++ b/tests/test-debugcomplete.out	Fri Jan 11 16:51:54 2008 -0800
@@ -129,6 +129,7 @@
 --noninteractive
 --pid-file
 --port
+--prefix
 --profile
 --quiet
 --repository
--- a/tests/test-hgweb.out	Fri Jan 11 21:20:27 2008 +0100
+++ b/tests/test-hgweb.out	Fri Jan 11 16:51:54 2008 -0800
@@ -35,7 +35,7 @@
 <h2>Mercurial Error</h2>
 
 <p>
-An error occured while processing your request:
+An error occurred while processing your request:
 </p>
 <p>
 Not Found
--- a/tests/test-keyword	Fri Jan 11 21:20:27 2008 +0100
+++ b/tests/test-keyword	Fri Jan 11 16:51:54 2008 -0800
@@ -173,8 +173,8 @@
 # remove path to temp dir
 hg incoming | sed -e 's/^\(comparing with \).*\(test-keyword.*\)/\1\2/'
 
-sed -i.bak -e 's/Id.*/& rejecttest/' a
-rm a.bak
+sed -e 's/Id.*/& rejecttest/' a > a.new
+mv a.new a
 echo % commit rejecttest
 hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
 echo % export