Merge backout
authorPatrick Mezard <pmezard@gmail.com>
Fri, 04 Apr 2008 23:13:32 +0200
changeset 6474 6ed371423d34
parent 6467 65029a3aafc2 (diff)
parent 6473 9c897ffd3637 (current diff)
child 6475 93e4bb8ca275
child 6479 31abcae33b4f
Merge backout
--- a/.hgsigs	Fri Apr 04 23:09:54 2008 +0200
+++ b/.hgsigs	Fri Apr 04 23:13:32 2008 +0200
@@ -4,3 +4,4 @@
 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4=
 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I=
 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU=
+bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
--- a/.hgtags	Fri Apr 04 23:09:54 2008 +0200
+++ b/.hgtags	Fri Apr 04 23:13:32 2008 +0200
@@ -16,3 +16,4 @@
 27230c29bfec36d5540fbe1c976810aefecfd1d2 0.9.3
 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0.9.4
 23889160905a1b09fffe1c07378e9fc1827606eb 0.9.5
+bae2e9c838e90a393bae3973a7850280413e091a 1.0
--- a/contrib/churn.py	Fri Apr 04 23:09:54 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-# churn.py - create a graph showing who changed the most lines
-#
-# Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-#
-#
-# Aliases map file format is simple one alias per line in the following
-# format:
-#
-# <alias email> <actual email>
-
-from mercurial.i18n import gettext as _
-from mercurial import mdiff, cmdutil, util, node
-import os, sys
-
-def get_tty_width():
-    if 'COLUMNS' in os.environ:
-        try:
-            return int(os.environ['COLUMNS'])
-        except ValueError:
-            pass
-    try:
-        import termios, array, fcntl
-        for dev in (sys.stdout, sys.stdin):
-            try:
-                fd = dev.fileno()
-                if not os.isatty(fd):
-                    continue
-                arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
-                return array.array('h', arri)[1]
-            except ValueError:
-                pass
-    except ImportError:
-        pass
-    return 80
-
-def __gather(ui, repo, node1, node2):
-    def dirtywork(f, mmap1, mmap2):
-        lines = 0
-
-        to = mmap1 and repo.file(f).read(mmap1[f]) or None
-        tn = mmap2 and repo.file(f).read(mmap2[f]) or None
-
-        diff = mdiff.unidiff(to, "", tn, "", f, f).split("\n")
-
-        for line in diff:
-            if not line:
-                continue # skip EOF
-            if line.startswith(" "):
-                continue # context line
-            if line.startswith("--- ") or line.startswith("+++ "):
-                continue # begining of diff
-            if line.startswith("@@ "):
-                continue # info line
-
-            # changed lines
-            lines += 1
-
-        return lines
-
-    ##
-
-    lines = 0
-
-    changes = repo.status(node1, node2, None, util.always)[:5]
-
-    modified, added, removed, deleted, unknown = changes
-
-    who = repo.changelog.read(node2)[1]
-    who = util.email(who) # get the email of the person
-
-    mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
-    mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
-    for f in modified:
-        lines += dirtywork(f, mmap1, mmap2)
-
-    for f in added:
-        lines += dirtywork(f, None, mmap2)
-
-    for f in removed:
-        lines += dirtywork(f, mmap1, None)
-
-    for f in deleted:
-        lines += dirtywork(f, mmap1, mmap2)
-
-    for f in unknown:
-        lines += dirtywork(f, mmap1, mmap2)
-
-    return (who, lines)
-
-def gather_stats(ui, repo, amap, revs=None, progress=False):
-    stats = {}
-
-    cl    = repo.changelog
-
-    if not revs:
-        revs = range(0, cl.count())
-
-    nr_revs = len(revs)
-    cur_rev = 0
-
-    for rev in revs:
-        cur_rev += 1 # next revision
-
-        node2    = cl.node(rev)
-        node1    = cl.parents(node2)[0]
-
-        if cl.parents(node2)[1] != node.nullid:
-            ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
-            continue
-
-        who, lines = __gather(ui, repo, node1, node2)
-
-        # remap the owner if possible
-        if who in amap:
-            ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
-            who = amap[who]
-
-        if not who in stats:
-            stats[who] = 0
-        stats[who] += lines
-
-        ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
-
-        if progress:
-            nr_revs = max(nr_revs, 1)
-            if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
-                ui.write("\rGenerating stats: %d%%" % (int(100.0*cur_rev/nr_revs),))
-                sys.stdout.flush()
-
-    if progress:
-        ui.write("\r")
-        sys.stdout.flush()
-
-    return stats
-
-def churn(ui, repo, **opts):
-    "Graphs the number of lines changed"
-
-    def pad(s, l):
-        if len(s) < l:
-            return s + " " * (l-len(s))
-        return s[0:l]
-
-    def graph(n, maximum, width, char):
-        maximum = max(1, maximum)
-        n = int(n * width / float(maximum))
-
-        return char * (n)
-
-    def get_aliases(f):
-        aliases = {}
-
-        for l in f.readlines():
-            l = l.strip()
-            alias, actual = l.split(" ")
-            aliases[alias] = actual
-
-        return aliases
-
-    amap = {}
-    aliases = opts.get('aliases')
-    if aliases:
-        try:
-            f = open(aliases,"r")
-        except OSError, e:
-            print "Error: " + e
-            return
-
-        amap = get_aliases(f)
-        f.close()
-
-    revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])]
-    revs.sort()
-    stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
-
-    # make a list of tuples (name, lines) and sort it in descending order
-    ordered = stats.items()
-    if not ordered:
-        return
-    ordered.sort(lambda x, y: cmp(y[1], x[1]))
-    max_churn = ordered[0][1]
-
-    tty_width = get_tty_width()
-    ui.note(_("assuming %i character terminal\n") % tty_width)
-    tty_width -= 1
-
-    max_user_width = max([len(user) for user, churn in ordered])
-
-    graph_width = tty_width - max_user_width - 1 - 6 - 2 - 2
-
-    for user, churn in ordered:
-        print "%s %6d %s" % (pad(user, max_user_width),
-                             churn,
-                             graph(churn, max_churn, graph_width, '*'))
-
-cmdtable = {
-    "churn":
-    (churn,
-     [('r', 'rev', [], _('limit statistics to the specified revisions')),
-      ('', 'aliases', '', _('file with email aliases')),
-      ('', 'progress', None, _('show progress'))],
-    'hg churn [-r revision range] [-a file] [--progress]'),
-}
--- a/contrib/convert-repo	Fri Apr 04 23:09:54 2008 +0200
+++ b/contrib/convert-repo	Fri Apr 04 23:13:32 2008 +0200
@@ -17,9 +17,11 @@
 opts = {}
 args = []
 try:
-    args = fancyopts.fancyopts(sys.argv[1:], options, opts)
-except fancyopts.getopt.GetoptError, inst:
-    u.warn('Usage:\n%s' % help)
+    args = list(fancyopts.fancyopts(sys.argv[1:], options, opts))
+    args += [None]*(3 - len(args))
+    src, dest, revmapfile = args
+except (fancyopts.getopt.GetoptError, ValueError), inst:
+    u.warn('Usage:\n%s\n' % help)
     sys.exit(-1)
 
-convert._convert(u, *args, **opts)
+convert.convert(u, src, dest, revmapfile, **opts)
--- a/contrib/hgk	Fri Apr 04 23:09:54 2008 +0200
+++ b/contrib/hgk	Fri Apr 04 23:13:32 2008 +0200
@@ -343,11 +343,24 @@
 proc readrefs {} {
     global tagids idtags headids idheads tagcontents env curid
 
-    set curid [exec $env(HG) --config ui.report_untrusted=false id]
+    set status [catch {exec $env(HG) --config ui.report_untrusted=false id} curid]
+    if { $status != 0 } {
+        puts $::errorInfo
+        if { ![string equal $::errorCode NONE] } {
+            exit 2
+        }
+    }
     regexp -- {[[:xdigit:]]+} $curid curid
 
-    set tags [exec $env(HG) --config ui.report_untrusted=false tags]
+    set status [catch {exec $env(HG) --config ui.report_untrusted=false tags} tags]
+    if { $status != 0 } {
+        puts $::errorInfo
+        if { ![string equal $::errorCode NONE] } {
+            exit 2
+        }
+    }
     regsub -all "\r\n" $tags "\n" tags
+
     set lines [split $tags "\n"]
     foreach f $lines {
 	regexp {(\S+)$} $f full
@@ -2326,8 +2339,12 @@
 
     $cflist delete 0 end
     $cflist insert end "Comments"
+    if {$nparents($id) <= 1} {
+    set parent "null"
     if {$nparents($id) == 1} {
-	startdiff [concat $id $parents($id)]
+        set parent $parents($id)
+    }
+	startdiff [concat $id $parent]
     } elseif {$nparents($id) > 1} {
 	mergediff $id
     }
--- a/doc/hgrc.5.txt	Fri Apr 04 23:09:54 2008 +0200
+++ b/doc/hgrc.5.txt	Fri Apr 04 23:13:32 2008 +0200
@@ -17,37 +17,50 @@
 
 Mercurial reads configuration data from several files, if they exist.
 The names of these files depend on the system on which Mercurial is
-installed. Windows registry keys contain PATH-like strings, every
-part must reference a Mercurial.ini file or be a directory where *.rc
-files will be read.
+installed.  *.rc files from a single directory are read in
+alphabetical order, later ones overriding earlier ones.  Where
+multiple paths are given below, settings from later paths override
+earlier ones.
 
-(Unix)    <install-root>/etc/mercurial/hgrc.d/*.rc::
-(Unix)    <install-root>/etc/mercurial/hgrc::
+(Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
+(Unix) <install-root>/etc/mercurial/hgrc::
     Per-installation configuration files, searched for in the
-    directory where Mercurial is installed.  For example, if installed
-    in /shared/tools, Mercurial will look in
-    /shared/tools/etc/mercurial/hgrc.  Options in these files apply to
-    all Mercurial commands executed by any user in any directory.
+    directory where Mercurial is installed.  <install-root> is the
+    parent directory of the hg executable (or symlink) being run.
+    For example, if installed in /shared/tools/bin/hg, Mercurial will
+    look in /shared/tools/etc/mercurial/hgrc.  Options in these files
+    apply to all Mercurial commands executed by any user in any
+    directory.
 
-(Unix)    /etc/mercurial/hgrc.d/*.rc::
-(Unix)    /etc/mercurial/hgrc::
-(Windows) HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial::
-  or::
-(Windows) C:\Mercurial\Mercurial.ini::
+(Unix) /etc/mercurial/hgrc.d/*.rc::
+(Unix) /etc/mercurial/hgrc::
     Per-system configuration files, for the system on which Mercurial
     is running.  Options in these files apply to all Mercurial
     commands executed by any user in any directory.  Options in these
     files override per-installation options.
 
-(Unix)    $HOME/.hgrc::
-(Windows) C:\Documents and Settings\USERNAME\Mercurial.ini::
-(Windows) $HOME\Mercurial.ini::
-    Per-user configuration file, for the user running Mercurial.
-    Options in this file apply to all Mercurial commands executed by
-    any user in any directory.  Options in this file override
+(Windows) <install-dir>\Mercurial.ini::
+  or else::
+(Windows) HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial::
+  or else::
+(Windows) C:\Mercurial\Mercurial.ini::
+    Per-installation/system configuration files, for the system on
+    which Mercurial is running.  Options in these files apply to all
+    Mercurial commands executed by any user in any directory.
+    Registry keys contain PATH-like strings, every part of which must
+    reference a Mercurial.ini file or be a directory where *.rc files
+    will be read.
+
+(Unix) $HOME/.hgrc::
+(Windows) %HOME%\Mercurial.ini::
+(Windows) %HOME%\.hgrc::
+(Windows) %USERPROFILE%\Mercurial.ini::
+(Windows) %USERPROFILE%\.hgrc::
+    Per-user configuration file(s), for the user running Mercurial.
+    On Windows 9x, %HOME% is replaced by %APPDATA%.
+    Options in these files apply to all Mercurial commands executed
+    by this user in any directory.  Options in thes files override
     per-installation and per-system options.
-    On Windows system, one of these is chosen exclusively according
-    to definition of HOME environment variable.
 
 (Unix, Windows) <repo>/.hg/hgrc::
     Per-repository configuration options that only apply in a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/churn.py	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,206 @@
+# churn.py - create a graph showing who changed the most lines
+#
+# Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+#
+#
+# Aliases map file format is simple one alias per line in the following
+# format:
+#
+# <alias email> <actual email>
+
+from mercurial.i18n import gettext as _
+from mercurial import mdiff, cmdutil, util, node
+import os, sys
+
+def get_tty_width():
+    if 'COLUMNS' in os.environ:
+        try:
+            return int(os.environ['COLUMNS'])
+        except ValueError:
+            pass
+    try:
+        import termios, array, fcntl
+        for dev in (sys.stdout, sys.stdin):
+            try:
+                fd = dev.fileno()
+                if not os.isatty(fd):
+                    continue
+                arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
+                return array.array('h', arri)[1]
+            except ValueError:
+                pass
+    except ImportError:
+        pass
+    return 80
+
+def __gather(ui, repo, node1, node2):
+    def dirtywork(f, mmap1, mmap2):
+        lines = 0
+
+        to = mmap1 and repo.file(f).read(mmap1[f]) or None
+        tn = mmap2 and repo.file(f).read(mmap2[f]) or None
+
+        diff = mdiff.unidiff(to, "", tn, "", f, f).split("\n")
+
+        for line in diff:
+            if not line:
+                continue # skip EOF
+            if line.startswith(" "):
+                continue # context line
+            if line.startswith("--- ") or line.startswith("+++ "):
+                continue # begining of diff
+            if line.startswith("@@ "):
+                continue # info line
+
+            # changed lines
+            lines += 1
+
+        return lines
+
+    ##
+
+    lines = 0
+
+    changes = repo.status(node1, node2, None, util.always)[:5]
+
+    modified, added, removed, deleted, unknown = changes
+
+    who = repo.changelog.read(node2)[1]
+    who = util.email(who) # get the email of the person
+
+    mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
+    mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
+    for f in modified:
+        lines += dirtywork(f, mmap1, mmap2)
+
+    for f in added:
+        lines += dirtywork(f, None, mmap2)
+
+    for f in removed:
+        lines += dirtywork(f, mmap1, None)
+
+    for f in deleted:
+        lines += dirtywork(f, mmap1, mmap2)
+
+    for f in unknown:
+        lines += dirtywork(f, mmap1, mmap2)
+
+    return (who, lines)
+
+def gather_stats(ui, repo, amap, revs=None, progress=False):
+    stats = {}
+
+    cl    = repo.changelog
+
+    if not revs:
+        revs = range(0, cl.count())
+
+    nr_revs = len(revs)
+    cur_rev = 0
+
+    for rev in revs:
+        cur_rev += 1 # next revision
+
+        node2    = cl.node(rev)
+        node1    = cl.parents(node2)[0]
+
+        if cl.parents(node2)[1] != node.nullid:
+            ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
+            continue
+
+        who, lines = __gather(ui, repo, node1, node2)
+
+        # remap the owner if possible
+        if who in amap:
+            ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
+            who = amap[who]
+
+        if not who in stats:
+            stats[who] = 0
+        stats[who] += lines
+
+        ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
+
+        if progress:
+            nr_revs = max(nr_revs, 1)
+            if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
+                ui.write("\rGenerating stats: %d%%" % (int(100.0*cur_rev/nr_revs),))
+                sys.stdout.flush()
+
+    if progress:
+        ui.write("\r")
+        sys.stdout.flush()
+
+    return stats
+
+def churn(ui, repo, **opts):
+    "Graphs the number of lines changed"
+
+    def pad(s, l):
+        if len(s) < l:
+            return s + " " * (l-len(s))
+        return s[0:l]
+
+    def graph(n, maximum, width, char):
+        maximum = max(1, maximum)
+        n = int(n * width / float(maximum))
+
+        return char * (n)
+
+    def get_aliases(f):
+        aliases = {}
+
+        for l in f.readlines():
+            l = l.strip()
+            alias, actual = l.split()
+            aliases[alias] = actual
+
+        return aliases
+
+    amap = {}
+    aliases = opts.get('aliases')
+    if aliases:
+        try:
+            f = open(aliases,"r")
+        except OSError, e:
+            print "Error: " + e
+            return
+
+        amap = get_aliases(f)
+        f.close()
+
+    revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])]
+    revs.sort()
+    stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
+
+    # make a list of tuples (name, lines) and sort it in descending order
+    ordered = stats.items()
+    if not ordered:
+        return
+    ordered.sort(lambda x, y: cmp(y[1], x[1]))
+    max_churn = ordered[0][1]
+
+    tty_width = get_tty_width()
+    ui.note(_("assuming %i character terminal\n") % tty_width)
+    tty_width -= 1
+
+    max_user_width = max([len(user) for user, churn in ordered])
+
+    graph_width = tty_width - max_user_width - 1 - 6 - 2 - 2
+
+    for user, churn in ordered:
+        print "%s %6d %s" % (pad(user, max_user_width),
+                             churn,
+                             graph(churn, max_churn, graph_width, '*'))
+
+cmdtable = {
+    "churn":
+    (churn,
+     [('r', 'rev', [], _('limit statistics to the specified revisions')),
+      ('', 'aliases', '', _('file with email aliases')),
+      ('', 'progress', None, _('show progress'))],
+    'hg churn [-r revision range] [-a file] [--progress]'),
+}
--- a/hgext/convert/__init__.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/convert/__init__.py	Fri Apr 04 23:13:32 2008 +0200
@@ -19,6 +19,7 @@
     - Darcs
     - git
     - Subversion
+    - Monotone
     - GNU Arch
 
     Accepted destination formats:
--- a/hgext/convert/common.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/convert/common.py	Fri Apr 04 23:13:32 2008 +0200
@@ -18,10 +18,13 @@
     s = base64.decodestring(s)
     return pickle.loads(s)
 
-def checktool(exe, name=None):
+class MissingTool(Exception): pass
+
+def checktool(exe, name=None, abort=True):
     name = name or exe
     if not util.find_exe(exe):
-        raise util.Abort('cannot find required "%s" tool' % name)
+        exc = abort and util.Abort or MissingTool
+        raise exc(_('cannot find required "%s" tool') % name)
 
 class NoRepo(Exception): pass
 
--- a/hgext/convert/convcmd.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/convert/convcmd.py	Fri Apr 04 23:13:32 2008 +0200
@@ -5,12 +5,13 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-from common import NoRepo, SKIPREV, mapfile
+from common import NoRepo, MissingTool, SKIPREV, mapfile
 from cvs import convert_cvs
 from darcs import darcs_source
 from git import convert_git
 from hg import mercurial_source, mercurial_sink
 from subversion import debugsvnlog, svn_source, svn_sink
+from monotone import monotone_source
 from gnuarch import gnuarch_source
 import filemap
 
@@ -32,6 +33,7 @@
     ('svn', svn_source),
     ('hg', mercurial_source),
     ('darcs', darcs_source),
+    ('mtn', monotone_source),
     ('gnuarch', gnuarch_source),
     ]
 
@@ -46,7 +48,7 @@
         try:
             if not type or name == type:
                 return source(ui, path, rev)
-        except NoRepo, inst:
+        except (NoRepo, MissingTool), inst:
             exceptions.append(inst)
     if not ui.quiet:
         for inst in exceptions:
--- a/hgext/convert/cvs.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/convert/cvs.py	Fri Apr 04 23:13:32 2008 +0200
@@ -14,7 +14,9 @@
         if not os.path.exists(cvs):
             raise NoRepo("%s does not look like a CVS checkout" % path)
 
-        for tool in ('cvsps', 'cvs'):
+        self.cmd = ui.config('convert', 'cvsps', 'cvsps -A -u --cvs-direct -q')
+        cvspsexe = self.cmd.split(None, 1)[0]
+        for tool in (cvspsexe, 'cvs'):
             checktool(tool)
 
         self.changeset = {}
@@ -34,7 +36,7 @@
             return
 
         maxrev = 0
-        cmd = 'cvsps -A -u --cvs-direct -q'
+        cmd = self.cmd
         if self.rev:
             # TODO: handle tags
             try:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/convert/monotone.py	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,198 @@
+# monotone support for the convert extension
+
+import os, re, time
+from mercurial import util
+from common import NoRepo, MissingTool, commit, converter_source, checktool
+from common import commandline
+from mercurial.i18n import _
+
+class monotone_source(converter_source, commandline):
+    def __init__(self, ui, path=None, rev=None):
+        converter_source.__init__(self, ui, path, rev)
+        commandline.__init__(self, ui, 'mtn')
+
+        self.ui = ui
+        self.path = path
+
+        # regular expressions for parsing monotone output
+        space    = r'\s*'
+        name     = r'\s+"((?:[^"]|\\")*)"\s*'
+        value    = name
+        revision = r'\s+\[(\w+)\]\s*'
+        lines    = r'(?:.|\n)+'
+
+        self.dir_re      = re.compile(space + "dir" + name)
+        self.file_re     = re.compile(space + "file" + name + "content" + revision)
+        self.add_file_re = re.compile(space + "add_file" + name + "content" + revision)
+        self.patch_re    = re.compile(space + "patch" + name + "from" + revision + "to" + revision)
+        self.rename_re   = re.compile(space + "rename" + name + "to" + name)
+        self.delete_re   = re.compile(space + "delete" + name)
+        self.tag_re      = re.compile(space + "tag" + name + "revision" + revision)
+        self.cert_re     = re.compile(lines + space + "name" + name + "value" + value)
+
+        attr = space + "file" + lines + space + "attr" + space
+        self.attr_execute_re = re.compile(attr  + '"mtn:execute"' + space + '"true"')
+
+        # cached data
+        self.manifest_rev = None
+        self.manifest = None
+        self.files = None
+        self.dirs  = None
+
+        norepo = NoRepo (_("%s does not look like a monotone repo") % path)
+        if not os.path.exists(path):
+            raise norepo
+
+        checktool('mtn', abort=False)
+
+        # test if there are any revisions
+        self.rev = None
+        try:
+            self.getheads()
+        except:
+            raise norepo
+        self.rev = rev
+
+    def mtnrun(self, *args, **kwargs):
+        kwargs['d'] = self.path
+        return self.run0('automate', *args, **kwargs)
+
+    def mtnloadmanifest(self, rev):
+        if self.manifest_rev == rev:
+            return
+        self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
+        self.manifest_rev = rev
+        self.files = {}
+        self.dirs = {}
+
+        for e in self.manifest:
+            m = self.file_re.match(e)
+            if m:
+                attr = ""
+                name = m.group(1)
+                node = m.group(2)
+                if self.attr_execute_re.match(e):
+                    attr += "x"
+                self.files[name] = (node, attr)
+            m = self.dir_re.match(e)
+            if m:
+                self.dirs[m.group(1)] = True
+
+    def mtnisfile(self, name, rev):
+        # a non-file could be a directory or a deleted or renamed file
+        self.mtnloadmanifest(rev)
+        try:
+            self.files[name]
+            return True
+        except KeyError:
+            return False
+
+    def mtnisdir(self, name, rev):
+        self.mtnloadmanifest(rev)
+        try:
+            self.dirs[name]
+            return True
+        except KeyError:
+            return False
+
+    def mtngetcerts(self, rev):
+        certs = {"author":"<missing>", "date":"<missing>",
+            "changelog":"<missing>", "branch":"<missing>"}
+        cert_list = self.mtnrun("certs", rev).split("\n\n")
+        for e in cert_list:
+            m = self.cert_re.match(e)
+            if m:
+                certs[m.group(1)] = m.group(2)
+        return certs
+
+    def mtnrenamefiles(self, files, fromdir, todir):
+        renamed = {}
+        for tofile in files:
+            suffix = tofile.lstrip(todir)
+            if todir + suffix == tofile:
+                renamed[tofile] = (fromdir + suffix).lstrip("/")
+        return renamed
+
+
+    # implement the converter_source interface:
+
+    def getheads(self):
+        if not self.rev:
+            return self.mtnrun("leaves").splitlines()
+        else:
+            return [self.rev]
+
+    def getchanges(self, rev):
+        #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
+        revision = self.mtnrun("get_revision", rev).split("\n\n")
+        files = {}
+        copies = {}
+        for e in revision:
+            m = self.add_file_re.match(e)
+            if m:
+                files[m.group(1)] = rev
+            m = self.patch_re.match(e)
+            if m:
+                files[m.group(1)] = rev
+
+            # Delete/rename is handled later when the convert engine
+            # discovers an IOError exception from getfile,
+            # but only if we add the "from" file to the list of changes.
+            m = self.delete_re.match(e)
+            if m:
+                files[m.group(1)] = rev
+            m = self.rename_re.match(e)
+            if m:
+                toname = m.group(2)
+                fromname = m.group(1)
+                if self.mtnisfile(toname, rev):
+                    copies[toname] = fromname
+                    files[toname] = rev
+                    files[fromname] = rev
+                if self.mtnisdir(toname, rev):
+                    renamed = self.mtnrenamefiles(self.files, fromname, toname)
+                    for tofile, fromfile in renamed.items():
+                        self.ui.debug (_("copying file in renamed dir from '%s' to '%s'") % (fromfile, tofile), '\n')
+                        files[tofile] = rev
+                    for fromfile in renamed.values():
+                        files[fromfile] = rev
+        return (files.items(), copies)
+
+    def getmode(self, name, rev):
+        self.mtnloadmanifest(rev)
+        try:
+            node, attr = self.files[name]
+            return attr
+        except KeyError:
+            return ""
+
+    def getfile(self, name, rev):
+        if not self.mtnisfile(name, rev):
+            raise IOError() # file was deleted or renamed
+        try:
+            return self.mtnrun("get_file_of", name, r=rev)
+        except:
+            raise IOError() # file was deleted or renamed
+
+    def getcommit(self, rev):
+        certs   = self.mtngetcerts(rev)
+        return commit(
+            author=certs["author"],
+            date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
+            desc=certs["changelog"],
+            rev=rev,
+            parents=self.mtnrun("parents", rev).splitlines(),
+            branch=certs["branch"])
+
+    def gettags(self):
+        tags = {}
+        for e in self.mtnrun("tags").split("\n\n"):
+            m = self.tag_re.match(e)
+            if m:
+                tags[m.group(1)] = m.group(2)
+        return tags
+
+    def getchangedfiles(self, rev, i):
+        # This function is only needed to support --filemap
+        # ... and we don't support that
+        raise NotImplementedError()
--- a/hgext/convert/subversion.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/convert/subversion.py	Fri Apr 04 23:13:32 2008 +0200
@@ -95,6 +95,10 @@
     else:
         pickle.dump(None, fp, protocol)
     fp.close()
+    # With large history, cleanup process goes crazy and suddenly
+    # consumes *huge* amount of memory. The output file being closed,
+    # there is no need for clean termination.
+    os._exit(0)
 
 def debugsvnlog(ui, **opts):
     """Fetch SVN log in a subprocess and channel them back to parent to
@@ -259,7 +263,7 @@
         rev = optrev(self.last_changed)
         oldmodule = ''
         trunk = getcfgpath('trunk', rev)
-        tags = getcfgpath('tags', rev)
+        self.tags = getcfgpath('tags', rev)
         branches = getcfgpath('branches', rev)
 
         # If the project has a trunk or branches, we will extract heads
@@ -274,7 +278,8 @@
 
         # First head in the list is the module's head
         self.heads = [self.head]
-        self.tags = '%s/%s' % (oldmodule , (tags or 'tags'))
+        if self.tags is not None:
+            self.tags = '%s/%s' % (oldmodule , (self.tags or 'tags'))
 
         # Check if branches bring a few more heads to the list
         if branches:
@@ -365,18 +370,58 @@
         if self.tags is None:
             return tags
 
-        start = self.revnum(self.head)
+        # svn tags are just a convention, project branches left in a
+        # 'tags' directory. There is no other relationship than
+        # ancestry, which is expensive to discover and makes them hard
+        # to update incrementally.  Worse, past revisions may be
+        # referenced by tags far away in the future, requiring a deep
+        # history traversal on every calculation.  Current code
+        # performs a single backward traversal, tracking moves within
+        # the tags directory (tag renaming) and recording a new tag
+        # everytime a project is copied from outside the tags
+        # directory. It also lists deleted tags, this behaviour may
+        # change in the future.
+        pendings = []
+        tagspath = self.tags
+        start = svn.ra.get_latest_revnum(self.ra)
         try:
-            for entry in get_log(self.url, [self.tags], self.startrev, start):
-                orig_paths, revnum, author, date, message = entry
-                for path in orig_paths:
-                    if not path.startswith(self.tags+'/'):
+            for entry in get_log(self.url, [self.tags], start, self.startrev):
+                origpaths, revnum, author, date, message = entry
+                copies = [(e.copyfrom_path, e.copyfrom_rev, p) for p,e 
+                          in origpaths.iteritems() if e.copyfrom_path]
+                copies.sort()
+                # Apply moves/copies from more specific to general
+                copies.reverse()
+
+                srctagspath = tagspath
+                if copies and copies[-1][2] == tagspath:
+                    # Track tags directory moves
+                    srctagspath = copies.pop()[0]
+
+                for source, sourcerev, dest in copies:
+                    if not dest.startswith(tagspath + '/'):
                         continue
-                    ent = orig_paths[path]
-                    source = ent.copyfrom_path
-                    rev = ent.copyfrom_rev
-                    tag = path.split('/')[-1]
-                    tags[tag] = self.revid(rev, module=source)
+                    for tag in pendings:
+                        if tag[0].startswith(dest):
+                            tagpath = source + tag[0][len(dest):]
+                            tag[:2] = [tagpath, sourcerev]
+                            break
+                    else:
+                        pendings.append([source, sourcerev, dest.split('/')[-1]])
+
+                # Tell tag renamings from tag creations
+                remainings = []
+                for source, sourcerev, tagname in pendings:
+                    if source.startswith(srctagspath):
+                        remainings.append([source, sourcerev, tagname])
+                        continue
+                    # From revision may be fake, get one with changes
+                    tagid = self.latest(source, sourcerev)
+                    if tagid:
+                        tags[tagname] = tagid
+                pendings = remainings
+                tagspath = srctagspath
+
         except SubversionException, (inst, num):
             self.ui.note('no tags found at revision %d\n' % start)
         return tags
@@ -417,6 +462,11 @@
         a change being reported. Return None if computed module does not
         belong to rootmodule subtree.
         """
+        if not path.startswith(self.rootmodule):
+            # Requests on foreign branches may be forbidden at server level
+            self.ui.debug(_('ignoring foreign branch %r\n') % path)
+            return None
+
         if not stop:
             stop = svn.ra.get_latest_revnum(self.ra)
         try:
--- a/hgext/fetch.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/fetch.py	Fri Apr 04 23:13:32 2008 +0200
@@ -67,7 +67,7 @@
                         util.removeauth(other.url())))
             force_editor = opts.get('force_editor') or opts.get('edit')
             n = repo.commit(mod + add + rem, message,
-                            opts['user'], opts['date'],
+                            opts['user'], opts['date'], force=True,
                             force_editor=force_editor)
             ui.status(_('new changeset %d:%s merges remote changes '
                         'with local\n') % (repo.changelog.rev(n),
--- a/hgext/imerge.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/imerge.py	Fri Apr 04 23:13:32 2008 +0200
@@ -120,7 +120,7 @@
         # this could be greatly improved
         realmerge = os.environ.get('HGMERGE')
         if not interactive:
-            os.environ['HGMERGE'] = 'merge'
+            os.environ['HGMERGE'] = 'internal:merge'
 
         # The filemerge ancestor algorithm does not work if self.wctx
         # already has two parents (in normal merge it doesn't yet). But
--- a/hgext/inotify/linux/_inotify.c	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/inotify/linux/_inotify.c	Fri Apr 04 23:13:32 2008 +0200
@@ -19,10 +19,10 @@
 {
     PyObject *ret = NULL;
     int fd = -1;
-    
+
      if (!PyArg_ParseTuple(args, ":init"))
 	goto bail;
-    
+
     Py_BEGIN_ALLOW_THREADS
     fd = inotify_init();
     Py_END_ALLOW_THREADS
@@ -31,19 +31,19 @@
 	PyErr_SetFromErrno(PyExc_OSError);
 	goto bail;
     }
-	
+
     ret = PyInt_FromLong(fd);
     if (ret == NULL)
 	goto bail;
 
     goto done;
-    
+
 bail:
     if (fd != -1)
 	close(fd);
 
     Py_CLEAR(ret);
-    
+
 done:
     return ret;
 }
@@ -74,17 +74,17 @@
 	PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
 	goto bail;
     }
-    
+
     ret = PyInt_FromLong(wd);
     if (ret == NULL)
 	goto bail;
-    
+
     goto done;
-    
+
 bail:
     if (wd != -1)
 	inotify_rm_watch(fd, wd);
-    
+
     Py_CLEAR(ret);
 
 done:
@@ -110,7 +110,7 @@
     uint32_t wd;
     int fd;
     int r;
-    
+
     if (!PyArg_ParseTuple(args, "iI:remove_watch", &fd, &wd))
 	goto bail;
 
@@ -122,14 +122,14 @@
 	PyErr_SetFromErrno(PyExc_OSError);
 	goto bail;
     }
-    
+
     Py_INCREF(Py_None);
-    
+
     goto done;
-    
+
 bail:
     Py_CLEAR(ret);
-    
+
 done:
     return ret;
 }
@@ -184,7 +184,7 @@
 
     if (ret == NULL)
 	goto bail;
-    
+
     for (i = 0; bit_names[i].bit; i++) {
 	if (mask & bit_names[i].bit) {
 	    if (bit_names[i].pyname == NULL) {
@@ -197,26 +197,26 @@
 		goto bail;
 	}
     }
-    
+
     goto done;
-    
+
 bail:
     Py_CLEAR(ret);
 
 done:
     return ret;
 }
-    
+
 static PyObject *pydecode_mask(PyObject *self, PyObject *args)
 {
     int mask;
-    
+
     if (!PyArg_ParseTuple(args, "i:decode_mask", &mask))
 	return NULL;
 
     return decode_mask(mask);
 }
-    
+
 PyDoc_STRVAR(
     decode_mask_doc,
     "decode_mask(mask) -> list_of_strings\n"
@@ -233,7 +233,7 @@
 
     if (!pyname || !pyval)
 	goto bail;
-    
+
     PyDict_SetItem(dict, pyname, pyval);
 
 bail:
@@ -278,28 +278,28 @@
     PyObject *cookie;
     PyObject *name;
 };
-    
+
 static PyObject *event_wd(PyObject *self, void *x)
 {
     struct event *evt = (struct event *) self;
     Py_INCREF(evt->wd);
     return evt->wd;
 }
-    
+
 static PyObject *event_mask(PyObject *self, void *x)
 {
     struct event *evt = (struct event *) self;
     Py_INCREF(evt->mask);
     return evt->mask;
 }
-    
+
 static PyObject *event_cookie(PyObject *self, void *x)
 {
     struct event *evt = (struct event *) self;
     Py_INCREF(evt->cookie);
     return evt->cookie;
 }
-    
+
 static PyObject *event_name(PyObject *self, void *x)
 {
     struct event *evt = (struct event *) self;
@@ -334,7 +334,7 @@
     Py_XDECREF(evt->mask);
     Py_XDECREF(evt->cookie);
     Py_XDECREF(evt->name);
-    
+
     (*evt->ob_type->tp_free)(evt);
 }
 
@@ -353,17 +353,17 @@
     pymasks = decode_mask(PyInt_AsLong(evt->mask));
     if (pymasks == NULL)
 	goto bail;
-    
+
     pymask = _PyString_Join(join, pymasks);
     if (pymask == NULL)
 	goto bail;
-    
+
     maskstr = PyString_AsString(pymask);
-    
+
     if (evt->name != Py_None) {
 	PyObject *pyname = PyString_Repr(evt->name, 1);
 	char *name = pyname ? PyString_AsString(pyname) : "???";
-	
+
 	if (cookie == -1)
 	    ret = PyString_FromFormat("event(wd=%d, mask=%s, name=%s)",
 				      wd, maskstr, name);
@@ -386,7 +386,7 @@
     goto done;
 bail:
     Py_CLEAR(ret);
-    
+
 done:
     Py_XDECREF(pymask);
     Py_XDECREF(pymasks);
@@ -436,7 +436,7 @@
     0,                         /* tp_alloc */
     event_new,          /* tp_new */
 };
-    
+
 PyObject *read_events(PyObject *self, PyObject *args)
 {
     PyObject *ctor_args = NULL;
@@ -448,22 +448,22 @@
     int fd;
 
     if (!PyArg_ParseTuple(args, "i|O:read", &fd, &pybufsize))
-        goto bail;
+	goto bail;
 
     if (pybufsize && pybufsize != Py_None)
 	bufsize = PyInt_AsLong(pybufsize);
-    
+
     ret = PyList_New(0);
     if (ret == NULL)
 	goto bail;
-    
+
     if (bufsize <= 0) {
 	int r;
-	
+
 	Py_BEGIN_ALLOW_THREADS
 	r = ioctl(fd, FIONREAD, &bufsize);
 	Py_END_ALLOW_THREADS
-	
+
 	if (r == -1) {
 	    PyErr_SetFromErrno(PyExc_OSError);
 	    goto bail;
@@ -475,16 +475,16 @@
 	static long name_max;
 	static long name_fd = -1;
 	long min;
-	
+
 	if (name_fd != fd) {
 	    name_fd = fd;
 	    Py_BEGIN_ALLOW_THREADS
 	    name_max = fpathconf(fd, _PC_NAME_MAX);
 	    Py_END_ALLOW_THREADS
 	}
-	
+
 	min = sizeof(struct inotify_event) + name_max + 1;
-	
+
 	if (bufsize < min) {
 	    PyErr_Format(PyExc_ValueError, "bufsize must be at least %d",
 			 (int) min);
@@ -493,7 +493,7 @@
     }
 
     buf = alloca(bufsize);
-    
+
     Py_BEGIN_ALLOW_THREADS
     nread = read(fd, buf, bufsize);
     Py_END_ALLOW_THREADS
@@ -507,9 +507,9 @@
 
     if (ctor_args == NULL)
 	goto bail;
-    
+
     pos = 0;
-    
+
     while (pos < nread) {
 	struct inotify_event *in = (struct inotify_event *) (buf + pos);
 	struct event *evt;
@@ -555,12 +555,12 @@
 
 	goto bail;
     }
-    
+
     goto done;
 
 bail:
     Py_CLEAR(ret);
-    
+
 done:
     Py_XDECREF(ctor_args);
 
@@ -597,12 +597,12 @@
     PyObject *mod, *dict;
 
     if (PyType_Ready(&event_type) == -1)
-        return;
+	return;
 
     mod = Py_InitModule3("_inotify", methods, doc);
 
     dict = PyModule_GetDict(mod);
-    
+
     if (dict)
 	define_consts(dict);
 }
--- a/hgext/inotify/linux/watcher.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/inotify/linux/watcher.py	Fri Apr 04 23:13:32 2008 +0200
@@ -69,7 +69,7 @@
         self.mask = raw.mask
         self.cookie = raw.cookie
         self.name = raw.name
-    
+
     def __repr__(self):
         r = repr(self.raw)
         return 'Event(path=' + repr(self.path) + ', ' + r[r.find('(')+1:]
@@ -155,7 +155,7 @@
 
     def path(self, path):
         '''Return a (watch descriptor, event mask) pair for the given path.
-        
+
         If the path is not being watched, return None.'''
 
         return self._paths.get(path)
@@ -167,7 +167,7 @@
         this watcher, return None.'''
 
         return self._wds.get(wd)
-        
+
     def read(self, bufsize=None):
         '''Read a list of queued inotify events.
 
--- a/hgext/inotify/server.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/inotify/server.py	Fri Apr 04 23:13:32 2008 +0200
@@ -316,7 +316,7 @@
             root, fn = self.split(wfn)
             del self.dir(self.statustrees[key], root)[fn]
             del self.dir(self.tree, root)[fn]
-        
+
     def scan(self, topdir=''):
         self.handle_timeout()
         ds = self.repo.dirstate._map.copy()
@@ -359,7 +359,7 @@
 
     def walk(self, states, tree, prefix=''):
         # This is the "inner loop" when talking to the client.
-        
+
         for name, val in tree.iteritems():
             path = join(prefix, name)
             try:
@@ -384,7 +384,7 @@
             self.repo.dirstate.ignorefunc = None
             self.ui.note('rescanning due to .hgignore change\n')
             self.scan()
-        
+
     def getstat(self, wpath):
         try:
             return self.statcache[wpath]
@@ -394,7 +394,7 @@
             except OSError, err:
                 if err.errno != errno.ENOENT:
                     raise
-        
+
     def stat(self, wpath):
         try:
             st = os.lstat(join(self.wprefix, wpath))
@@ -404,7 +404,7 @@
         except OSError, err:
             self.statcache.pop(wpath, None)
             raise
-            
+
     def created(self, wpath):
         if wpath == '.hgignore':
             self.update_hgignore()
@@ -435,7 +435,7 @@
             return
 
         self.updatestatus(wpath, None)
-        
+
     def schedule_work(self, wpath, evt):
         self.eventq.setdefault(wpath, [])
         prev = self.eventq[wpath]
@@ -454,7 +454,7 @@
             self.modified(wpath)
         elif evt == 'd':
             self.deleted(wpath)
-            
+
     def process_create(self, wpath, evt):
         if self.ui.debugflag:
             self.ui.note(_('%s event: created %s\n') %
@@ -583,7 +583,7 @@
             return
 
         names = cs.read().split('\0')
-        
+
         states = names.pop()
 
         self.ui.note(_('answering query for %r\n') % states)
--- a/hgext/mq.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/mq.py	Fri Apr 04 23:13:32 2008 +0200
@@ -766,6 +766,9 @@
     def push(self, repo, patch=None, force=False, list=False,
              mergeq=None):
         wlock = repo.wlock()
+        if repo.dirstate.parents()[0] != repo.changelog.tip():
+            self.ui.status(_("(working directory not at tip)\n"))
+
         try:
             patch = self.lookup(patch)
             # Suppose our series file is: A B C and the current 'top'
@@ -1613,7 +1616,10 @@
                 destrev = heads.keys()
                 destrev.append(sr.changelog.parents(qbase)[0])
     elif sr.capable('lookup'):
-        qbase = sr.lookup('qbase')
+        try:
+            qbase = sr.lookup('qbase')
+        except RepoError:
+            pass
     ui.note(_('cloning main repo\n'))
     sr, dr = hg.clone(ui, sr.url(), dest,
                       pull=opts['pull'],
--- a/hgext/notify.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/hgext/notify.py	Fri Apr 04 23:13:32 2008 +0200
@@ -234,8 +234,6 @@
 
     def diff(self, node, ref):
         maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
-        if maxdiff == 0:
-            return
         prev = self.repo.changelog.parents(node)[0]
         self.ui.pushbuffer()
         patch.diff(self.repo, prev, ref)
@@ -245,6 +243,8 @@
             # s may be nil, don't include the header if it is
             if s:
                 self.ui.write('\ndiffstat:\n\n%s' % s)
+        if maxdiff == 0:
+            return
         if maxdiff > 0 and len(difflines) > maxdiff:
             self.ui.write(_('\ndiffs (truncated from %d to %d lines):\n\n') %
                           (len(difflines), maxdiff))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/pager.py	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,35 @@
+# pager.py - display output using a pager
+#
+# Copyright 2008 David Soria Parra <dsp@php.net>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+#
+# To load the extension, add it to your .hgrc file:
+#
+#   [extension]
+#   hgext.pager =
+#
+# To set the pager that should be used, set the application variable:
+#
+#   [pager]
+#   pager = LESS='FSRX' less
+#
+# If no pager is set, the pager extensions uses the environment
+# variable $PAGER. If neither pager.pager, nor $PAGER is set, no pager
+# is used.
+#
+# If you notice "BROKEN PIPE" error messages, you can disable them
+# by setting:
+#
+#   [pager]
+#   quiet = True
+
+import sys, os, signal
+
+def uisetup(ui):
+    p = ui.config("pager", "pager", os.environ.get("PAGER"))
+    if p and sys.stdout.isatty() and '--debugger' not in sys.argv:
+        if ui.configbool('pager', 'quiet'):
+            signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+        sys.stderr = sys.stdout = os.popen(p, "wb")
--- a/mercurial/bundlerepo.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/bundlerepo.py	Fri Apr 04 23:13:32 2008 +0200
@@ -12,8 +12,8 @@
 
 from node import hex, nullid, short
 from i18n import _
-import changegroup, util, os, struct, bz2, tempfile, mdiff
-import localrepo, changelog, manifest, filelog, revlog
+import changegroup, util, os, struct, bz2, tempfile, shutil, mdiff
+import repo, localrepo, changelog, manifest, filelog, revlog
 
 class bundlerevlog(revlog.revlog):
     def __init__(self, opener, indexfile, bundlefile,
@@ -153,7 +153,13 @@
 
 class bundlerepository(localrepo.localrepository):
     def __init__(self, ui, path, bundlename):
-        localrepo.localrepository.__init__(self, ui, path)
+        self._tempparent = None
+        try:
+            localrepo.localrepository.__init__(self, ui, path)
+        except repo.RepoError:
+            self._tempparent = tempfile.mkdtemp()
+            tmprepo = localrepo.instance(ui,self._tempparent,1)
+            localrepo.localrepository.__init__(self, ui, self._tempparent)
 
         if path:
             self._url = 'bundle:' + path + '+' + bundlename
@@ -221,9 +227,6 @@
     def url(self):
         return self._url
 
-    def dev(self):
-        return -1
-
     def file(self, f):
         if not self.bundlefilespos:
             self.bundlefile.seek(self.filestart)
@@ -255,6 +258,11 @@
         tempfile = getattr(self, 'tempfile', None)
         if tempfile is not None:
             os.unlink(tempfile)
+        if self._tempparent:
+            shutil.rmtree(self._tempparent, True)
+
+    def cancopy(self):
+        return False
 
 def instance(ui, path, create):
     if create:
--- a/mercurial/changelog.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/changelog.py	Fri Apr 04 23:13:32 2008 +0200
@@ -108,6 +108,9 @@
         # if we're doing an initial clone, divert to another file
         if self._delaycount == 0:
             self._delayname = fp.name
+            if not self.count():
+                # make sure to truncate the file
+                mode = mode.replace('a', 'w')
             return self._realopener(name + ".a", mode)
         # otherwise, divert to memory
         return appender(fp, self._delaybuf)
--- a/mercurial/cmdutil.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/cmdutil.py	Fri Apr 04 23:13:32 2008 +0200
@@ -470,7 +470,7 @@
     if len(pats) == 1:
         raise util.Abort(_('no destination specified'))
     dest = pats.pop()
-    destdirexists = os.path.isdir(dest)
+    destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
     if not destdirexists:
         if len(pats) > 1 or util.patkind(pats[0], None)[0]:
             raise util.Abort(_('with multiple sources, destination must be an '
--- a/mercurial/commands.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/commands.py	Fri Apr 04 23:13:32 2008 +0200
@@ -6,10 +6,10 @@
 # of the GNU General Public License, incorporated herein by reference.
 
 from node import hex, nullid, nullrev, short
-from repo import RepoError
+from repo import RepoError, NoCapability
 from i18n import _
 import os, re, sys, urllib
-import hg, util, revlog, bundlerepo, extensions
+import hg, util, revlog, bundlerepo, extensions, copies
 import difflib, patch, time, help, mdiff, tempfile
 import version, socket
 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
@@ -227,7 +227,10 @@
             raise util.Abort(_('cannot use --parent on non-merge changeset'))
         parent = p1
 
+    # the backout should appear on the same branch
+    branch = repo.dirstate.branch()
     hg.clean(repo, node, show_stats=False)
+    repo.dirstate.setbranch(branch)
     revert_opts = opts.copy()
     revert_opts['date'] = None
     revert_opts['all'] = True
@@ -539,6 +542,9 @@
     If a list of files is omitted, all changes reported by "hg status"
     will be committed.
 
+    If you are committing the result of a merge, do not provide any
+    file names or -I/-X filters.
+
     If no commit message is specified, the configured editor is started to
     enter a message.
 
@@ -547,7 +553,20 @@
     def commitfunc(ui, repo, files, message, match, opts):
         return repo.commit(files, message, opts['user'], opts['date'], match,
                            force_editor=opts.get('force_editor'))
-    cmdutil.commit(ui, repo, commitfunc, pats, opts)
+
+    node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
+    if not node:
+        return
+    cl = repo.changelog
+    rev = cl.rev(node)
+    parents = cl.parentrevs(rev)
+    if rev - 1 in parents:
+        # one of the parents was the old tip
+        return
+    if (parents == (nullrev, nullrev) or
+        len(cl.heads(cl.node(parents[0]))) > 1 and
+        (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
+        ui.status(_('created new head\n'))
 
 def copy(ui, repo, *pats, **opts):
     """mark files as copied for the next commit
@@ -574,15 +593,17 @@
     if len(args) == 3:
         index, rev1, rev2 = args
         r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
+        lookup = r.lookup
     elif len(args) == 2:
         if not repo:
             raise util.Abort(_("There is no Mercurial repository here "
                                "(.hg not found)"))
         rev1, rev2 = args
         r = repo.changelog
+        lookup = repo.lookup
     else:
         raise util.Abort(_('either two or three arguments required'))
-    a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
+    a = r.ancestor(lookup(rev1), lookup(rev2))
     ui.write("%d:%s\n" % (r.rev(a), hex(a)))
 
 def debugcomplete(ui, cmd='', **opts):
@@ -695,23 +716,26 @@
     finally:
         del wlock
 
-def debugstate(ui, repo):
+def debugstate(ui, repo, nodates=None):
     """show the contents of the current dirstate"""
     k = repo.dirstate._map.items()
     k.sort()
+    timestr = ""
+    showdate = not nodates
     for file_, ent in k:
-        if ent[3] == -1:
-            # Pad or slice to locale representation
-            locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0)))
-            timestr = 'unset'
-            timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
-        else:
-            timestr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ent[3]))
+        if showdate:
+            if ent[3] == -1:
+                # Pad or slice to locale representation
+                locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
+                timestr = 'unset'
+                timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
+            else:
+                timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
         if ent[1] & 020000:
             mode = 'lnk'
         else:
             mode = '%3o' % (ent[1] & 0777)
-        ui.write("%c %s %10d %s %s\n" % (ent[0], mode, ent[2], timestr, file_))
+        ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
     for f in repo.dirstate.copies():
         ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
 
@@ -2024,7 +2048,7 @@
     if revs:
         try:
             revs = [other.lookup(rev) for rev in revs]
-        except repo.NoCapability:
+        except NoCapability:
             error = _("Other repository doesn't support revision lookup, "
                       "so a rev cannot be specified.")
             raise util.Abort(error)
@@ -2118,51 +2142,74 @@
 
     Schedule the indicated files for removal from the repository.
 
-    This only removes files from the current branch, not from the
-    entire project history.  If the files still exist in the working
-    directory, they will be deleted from it.  If invoked with --after,
-    files are marked as removed, but not actually unlinked unless --force
-    is also given. Without exact file names, --after will only mark
-    files as removed if they are no longer in the working directory.
+    This only removes files from the current branch, not from the entire
+    project history. -A can be used to remove only files that have already
+    been deleted, -f can be used to force deletion, and -Af can be used
+    to remove files from the next revision without deleting them.
+
+    The following table details the behavior of remove for different file
+    states (columns) and option combinations (rows). The file states are
+    Added, Clean, Modified and Missing (as reported by hg status). The
+    actions are Warn, Remove (from branch) and Delete (from disk).
+
+           A  C  M  !
+    none   W  RD W  R
+    -f     R  RD RD R
+    -A     W  W  W  R
+    -Af    R  R  R  R
 
     This command schedules the files to be removed at the next commit.
     To undo a remove before that, see hg revert.
-
-    Modified files and added files are not removed by default.  To
-    remove them, use the -f/--force option.
     """
-    if not opts['after'] and not pats:
+
+    after, force = opts.get('after'), opts.get('force')
+    if not pats and not after:
         raise util.Abort(_('no files specified'))
+
     files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
-    exact = dict.fromkeys(files)
     mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
     modified, added, removed, deleted, unknown = mardu
+
     remove, forget = [], []
     for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
+
         reason = None
-        if abs in modified and not opts['force']:
-            reason = _('is modified (use -f to force removal)')
+        if abs in removed or abs in unknown:
+            continue
+
+        # last column
+        elif abs in deleted:
+            remove.append(abs)
+
+        # rest of the third row
+        elif after and not force:
+            reason = _('still exists (use -f to force removal)')
+
+        # rest of the first column
         elif abs in added:
-            if opts['force']:
+            if not force:
+                reason = _('has been marked for add (use -f to force removal)')
+            else:
                 forget.append(abs)
-                continue
-            reason = _('has been marked for add (use -f to force removal)')
-            exact = 1 # force the message
-        elif abs not in repo.dirstate:
-            reason = _('is not managed')
-        elif opts['after'] and not exact and abs not in deleted:
-            continue
-        elif abs in removed:
-            continue
+
+        # rest of the third column
+        elif abs in modified:
+            if not force:
+                reason = _('is modified (use -f to force removal)')
+            else:
+                remove.append(abs)
+
+        # rest of the second column
+        elif not reason:
+            remove.append(abs)
+
         if reason:
-            if exact:
-                ui.warn(_('not removing %s: file %s\n') % (rel, reason))
-        else:
-            if ui.verbose or not exact:
-                ui.status(_('removing %s\n') % rel)
-            remove.append(abs)
+            ui.warn(_('not removing %s: file %s\n') % (rel, reason))
+        elif ui.verbose or not exact:
+            ui.status(_('removing %s\n') % rel)
+
     repo.forget(forget)
-    repo.remove(remove, unlink=opts['force'] or not opts['after'])
+    repo.remove(remove, unlink=not after)
 
 def rename(ui, repo, *pats, **opts):
     """rename files; equivalent of copy + remove
@@ -2375,8 +2422,19 @@
                     pass
                 repo.dirstate.remove(f)
 
+            normal = None
+            if node == parent:
+                # We're reverting to our parent. If possible, we'd like status
+                # to report the file as clean. We have to use normallookup for
+                # merges to avoid losing information about merged/dirty files.
+                if p2 != nullid:
+                    normal = repo.dirstate.normallookup
+                else:
+                    normal = repo.dirstate.normal
             for f in revert[0]:
                 checkout(f)
+                if normal:
+                    normal(f)
 
             for f in add[0]:
                 checkout(f)
@@ -2459,10 +2517,7 @@
     class service:
         def init(self):
             util.set_signal_handler()
-            try:
-                self.httpd = hgweb.server.create_server(parentui, repo)
-            except socket.error, inst:
-                raise util.Abort(_('cannot start server: ') + inst.args[1])
+            self.httpd = hgweb.server.create_server(parentui, repo)
 
             if not ui.verbose: return
 
@@ -2471,12 +2526,12 @@
             else:
                 prefix = ''
 
-            if self.httpd.port != 80:
-                ui.status(_('listening at http://%s:%d/%s\n') %
-                          (self.httpd.addr, self.httpd.port, prefix))
-            else:
-                ui.status(_('listening at http://%s/%s\n') %
-                          (self.httpd.addr, prefix))
+            port = ':%d' % self.httpd.port
+            if port == ':80':
+                port = ''
+
+            ui.status(_('listening at http://%s%s/%s (%s:%d)\n') %
+                      (self.httpd.fqaddr, port, prefix, self.httpd.addr, self.httpd.port))
 
         def run(self):
             self.httpd.serve_forever()
@@ -2544,6 +2599,22 @@
 
     explicit_changetypes = changetypes + (('clean', 'C', clean),)
 
+    copy = {}
+    showcopy = {}
+    if ((all or opts.get('copies')) and not opts.get('no_status')):
+        if opts.get('rev') == []:
+            # fast path, more correct with merge parents
+            showcopy = copy = repo.dirstate.copies().copy()
+        else:
+            ctxn = repo.changectx(nullid)
+            ctx1 = repo.changectx(node1)
+            ctx2 = repo.changectx(node2)
+            if node2 is None:
+                ctx2 = repo.workingctx()
+            copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
+            for k, v in copy.items():
+                copy[v] = k
+
     end = opts['print0'] and '\0' or '\n'
 
     for opt, char, changes in ([ct for ct in explicit_changetypes
@@ -2557,13 +2628,11 @@
 
         for f in changes:
             ui.write(format % repo.pathto(f, cwd))
-            if ((all or opts.get('copies')) and not opts.get('no_status')):
-                copied = repo.dirstate.copied(f)
-                if copied:
-                    ui.write('  %s%s' % (repo.pathto(copied, cwd), end))
-
-def tag(ui, repo, name, rev_=None, **opts):
-    """add a tag for the current or given revision
+            if f in copy and (f in added or f in showcopy):
+                ui.write('  %s%s' % (repo.pathto(copy[f], cwd), end))
+
+def tag(ui, repo, name1, *names, **opts):
+    """add one or more tags for the current or given revision
 
     Name a particular revision using <name>.
 
@@ -2582,43 +2651,49 @@
 
     See 'hg help dates' for a list of formats valid for -d/--date.
     """
-    if name in ['tip', '.', 'null']:
-        raise util.Abort(_("the name '%s' is reserved") % name)
-    if rev_ is not None:
-        ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
-                  "please use 'hg tag [-r REV] NAME' instead\n"))
-        if opts['rev']:
-            raise util.Abort(_("use only one form to specify the revision"))
+
+    rev_ = None
+    names = (name1,) + names
+    if len(names) != len(dict.fromkeys(names)):
+        raise util.Abort(_('tag names must be unique'))
+    for n in names:
+        if n in ['tip', '.', 'null']:
+            raise util.Abort(_('the name \'%s\' is reserved') % n)
     if opts['rev'] and opts['remove']:
         raise util.Abort(_("--rev and --remove are incompatible"))
     if opts['rev']:
         rev_ = opts['rev']
     message = opts['message']
     if opts['remove']:
-        tagtype = repo.tagtype(name)
-
-        if not tagtype:
-            raise util.Abort(_('tag %s does not exist') % name)
-        if opts['local'] and tagtype == 'global':
-           raise util.Abort(_('%s tag is global') % name)
-        if not opts['local'] and tagtype == 'local':
-           raise util.Abort(_('%s tag is local') % name)
-
+        expectedtype = opts['local'] and 'local' or 'global'
+        for n in names:
+            if not repo.tagtype(n):
+                raise util.Abort(_('tag \'%s\' does not exist') % n)
+            if repo.tagtype(n) != expectedtype:
+                raise util.Abort(_('tag \'%s\' is not a %s tag') %
+                                 (n, expectedtype))
         rev_ = nullid
         if not message:
-            message = _('Removed tag %s') % name
-    elif name in repo.tags() and not opts['force']:
-        raise util.Abort(_('a tag named %s already exists (use -f to force)')
-                         % name)
+            message = _('Removed tag %s') % ', '.join(names)
+    elif not opts['force']:
+        for n in names:
+            if n in repo.tags():
+                raise util.Abort(_('tag \'%s\' already exists '
+                                   '(use -f to force)') % n)
     if not rev_ and repo.dirstate.parents()[1] != nullid:
         raise util.Abort(_('uncommitted merge - please provide a '
                            'specific revision'))
     r = repo.changectx(rev_).node()
 
     if not message:
-        message = _('Added tag %s for changeset %s') % (name, short(r))
-
-    repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
+        message = (_('Added tag %s for changeset %s') %
+                   (', '.join(names), short(r)))
+
+    date = opts.get('date')
+    if date:
+        date = util.parsedate(date)
+
+    repo.tag(names, r, message, opts['local'], opts['user'], date)
 
 def tags(ui, repo):
     """list repository tags
@@ -2656,7 +2731,14 @@
 def tip(ui, repo, **opts):
     """show the tip revision
 
-    Show the tip revision.
+    The tip revision (usually just called the tip) is the most
+    recently added changeset in the repository, the most recently
+    changed head.
+
+    If you have just made a commit, that commit will be the tip. If
+    you have just pulled changes from another repository, the tip of
+    that repository becomes the current tip. The "tip" tag is special
+    and cannot be renamed or assigned to a different changeset.
     """
     cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
 
@@ -2688,17 +2770,17 @@
 
     Update the working directory to the specified revision, or the
     tip of the current branch if none is specified.
-    See 'hg help dates' for a list of formats valid for -d/--date.
-
-    If there are no outstanding changes in the working directory and
-    there is a linear relationship between the current version and the
-    requested version, the result is the requested version.
-
-    To merge the working directory with another revision, use the
-    merge command.
-
-    By default, update will refuse to run if doing so would require
-    discarding local changes.
+
+    If the requested revision is a descendant of the working
+    directory, any outstanding changes in the working directory will
+    be merged into the result. If it is not directly descended but is
+    on the same named branch, update aborts with a suggestion to use
+    merge or update -C instead.
+
+    If the requested revision is on a different named branch and the
+    working directory is clean, update quietly switches branches.
+
+    See 'hg help dates' for a list of formats valid for --date.
     """
     if rev and node:
         raise util.Abort(_("please specify just one revision"))
@@ -2856,7 +2938,7 @@
          [('f', 'force', None,
            _('run even when remote repository is unrelated')),
           ('r', 'rev', [],
-           _('a changeset you would like to bundle')),
+           _('a changeset up to which you would like to bundle')),
           ('', 'base', [],
            _('a base changeset to specify instead of a destination')),
           ('a', 'all', None,
@@ -2927,7 +3009,10 @@
         (debugsetparents,
          [],
          _('hg debugsetparents REV1 [REV2]')),
-    "debugstate": (debugstate, [], _('hg debugstate')),
+    "debugstate":
+        (debugstate,
+         [('', 'nodates', None, _('do not display the saved mtime'))],
+         _('hg debugstate [OPTS]')),
     "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
     "^diff":
         (diff,
@@ -2943,7 +3028,7 @@
            _('ignore changes in the amount of white space')),
           ('B', 'ignore-blank-lines', None,
            _('ignore changes whose lines are all blank')),
-          ('U', 'unified', 3,
+          ('U', 'unified', '',
            _('number of lines of context to show'))
          ] + walkopts,
          _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
@@ -3005,7 +3090,8 @@
            _('run even when remote repository is unrelated')),
           ('n', 'newest-first', None, _('show newest record first')),
           ('', 'bundle', '', _('file to store the bundles into')),
-          ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
+          ('r', 'rev', [],
+           _('a specific revision up to which you would like to pull')),
          ] + logopts + remoteopts,
          _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
            ' [--bundle FILENAME] [SOURCE]')),
@@ -3053,7 +3139,8 @@
         (outgoing,
          [('f', 'force', None,
            _('run even when remote repository is unrelated')),
-          ('r', 'rev', [], _('a specific revision you would like to push')),
+          ('r', 'rev', [],
+           _('a specific revision up to which you would like to push')),
           ('n', 'newest-first', None, _('show newest record first')),
          ] + logopts + remoteopts,
          _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
@@ -3076,14 +3163,16 @@
     "^push":
         (push,
          [('f', 'force', None, _('force push')),
-          ('r', 'rev', [], _('a specific revision you would like to push')),
+          ('r', 'rev', [],
+           _('a specific revision up to which you would like to push')),
          ] + remoteopts,
          _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
     "recover": (recover, [], _('hg recover')),
     "^remove|rm":
         (remove,
-         [('A', 'after', None, _('record remove without deleting')),
-          ('f', 'force', None, _('remove file even if modified')),
+         [('A', 'after', None, _('record delete for missing files')),
+          ('f', 'force', None,
+           _('remove (and delete) file even if added or modified')),
          ] + walkopts,
          _('hg remove [OPTION]... FILE...')),
     "rename|mv":
@@ -3109,8 +3198,8 @@
           ('d', 'daemon', None, _('run server in background')),
           ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
           ('E', 'errorlog', '', _('name of error log file to write to')),
-          ('p', 'port', 0, _('port to use (default: 8000)')),
-          ('a', 'address', '', _('address to use')),
+          ('p', 'port', 0, _('port to listen on (default: 8000)')),
+          ('a', 'address', '', _('address to listen on (default: all interfaces)')),
           ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
           ('n', 'name', '',
            _('name to show in web pages (default: working dir)')),
@@ -3153,7 +3242,7 @@
           # -l/--local is already there, commitopts cannot be used
           ('m', 'message', '', _('use <text> as commit message')),
          ] + commitopts2,
-         _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
+         _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
     "tags": (tags, [], _('hg tags')),
     "tip":
         (tip,
--- a/mercurial/context.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/context.py	Fri Apr 04 23:13:32 2008 +0200
@@ -190,6 +190,9 @@
         elif name == '_filerev':
             self._filerev = self._filelog.rev(self._filenode)
             return self._filerev
+        elif name == '_repopath':
+            self._repopath = self._path
+            return self._repopath
         else:
             raise AttributeError, name
 
@@ -404,7 +407,7 @@
                 pl = [(n.path(), n.filenode()) for n in c.parents()]
                 acache[(c._path, None)] = pl
 
-        flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
+        flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
         def parents(vertex):
             if vertex in acache:
                 return acache[vertex]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/copies.py	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,233 @@
+# copies.py - copy detection for Mercurial
+#
+# Copyright 2008 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+from node import nullid, nullrev
+from i18n import _
+import util, heapq
+
+def _nonoverlap(d1, d2, d3):
+    "Return list of elements in d1 not in d2 or d3"
+    l = [d for d in d1 if d not in d3 and d not in d2]
+    l.sort()
+    return l
+
+def _dirname(f):
+    s = f.rfind("/")
+    if s == -1:
+        return ""
+    return f[:s]
+
+def _dirs(files):
+    d = {}
+    for f in files:
+        f = _dirname(f)
+        while f not in d:
+            d[f] = True
+            f = _dirname(f)
+    return d
+
+def _findoldnames(fctx, limit):
+    "find files that path was copied from, back to linkrev limit"
+    old = {}
+    seen = {}
+    orig = fctx.path()
+    visit = [(fctx, 0)]
+    while visit:
+        fc, depth = visit.pop()
+        s = str(fc)
+        if s in seen:
+            continue
+        seen[s] = 1
+        if fc.path() != orig and fc.path() not in old:
+            old[fc.path()] = (depth, fc.path()) # remember depth
+        if fc.rev() < limit and fc.rev() is not None:
+            continue
+        visit += [(p, depth - 1) for p in fc.parents()]
+
+    # return old names sorted by depth
+    old = old.values()
+    old.sort()
+    return [o[1] for o in old]
+
+def _findlimit(repo, a, b):
+    "find the earliest revision that's an ancestor of a or b but not both"
+    # basic idea:
+    # - mark a and b with different sides
+    # - if a parent's children are all on the same side, the parent is
+    #   on that side, otherwise it is on no side
+    # - walk the graph in topological order with the help of a heap;
+    #   - add unseen parents to side map
+    #   - clear side of any parent that has children on different sides
+    #   - track number of interesting revs that might still be on a side
+    #   - track the lowest interesting rev seen
+    #   - quit when interesting revs is zero
+
+    cl = repo.changelog
+    working = cl.count() # pseudo rev for the working directory
+    if a is None:
+        a = working
+    if b is None:
+        b = working
+
+    side = {a: -1, b: 1}
+    visit = [-a, -b]
+    heapq.heapify(visit)
+    interesting = len(visit)
+    limit = working
+
+    while interesting:
+        r = -heapq.heappop(visit)
+        if r == working:
+            parents = [cl.rev(p) for p in repo.dirstate.parents()]
+        else:
+            parents = cl.parentrevs(r)
+        for p in parents:
+            if p not in side:
+                # first time we see p; add it to visit
+                side[p] = side[r]
+                if side[p]:
+                    interesting += 1
+                heapq.heappush(visit, -p)
+            elif side[p] and side[p] != side[r]:
+                # p was interesting but now we know better
+                side[p] = 0
+                interesting -= 1
+        if side[r]:
+            limit = r # lowest rev visited
+            interesting -= 1
+    return limit
+
+def copies(repo, c1, c2, ca, checkdirs=False):
+    """
+    Find moves and copies between context c1 and c2
+    """
+    # avoid silly behavior for update from empty dir
+    if not c1 or not c2 or c1 == c2:
+        return {}, {}
+
+    limit = _findlimit(repo, c1.rev(), c2.rev())
+    m1 = c1.manifest()
+    m2 = c2.manifest()
+    ma = ca.manifest()
+
+    def makectx(f, n):
+        if len(n) != 20: # in a working context?
+            if c1.rev() is None:
+                return c1.filectx(f)
+            return c2.filectx(f)
+        return repo.filectx(f, fileid=n)
+    ctx = util.cachefunc(makectx)
+
+    copy = {}
+    fullcopy = {}
+    diverge = {}
+
+    def checkcopies(f, m1, m2):
+        '''check possible copies of f from m1 to m2'''
+        c1 = ctx(f, m1[f])
+        for of in _findoldnames(c1, limit):
+            fullcopy[f] = of # remember for dir rename detection
+            if of in m2: # original file not in other manifest?
+                # if the original file is unchanged on the other branch,
+                # no merge needed
+                if m2[of] != ma.get(of):
+                    c2 = ctx(of, m2[of])
+                    ca = c1.ancestor(c2)
+                    # related and named changed on only one side?
+                    if ca and (ca.path() == f or ca.path() == c2.path()):
+                        if c1 != ca or c2 != ca: # merge needed?
+                            copy[f] = of
+            elif of in ma:
+                diverge.setdefault(of, []).append(f)
+
+    repo.ui.debug(_("  searching for copies back to rev %d\n") % limit)
+
+    u1 = _nonoverlap(m1, m2, ma)
+    u2 = _nonoverlap(m2, m1, ma)
+
+    if u1:
+        repo.ui.debug(_("  unmatched files in local:\n   %s\n")
+                      % "\n   ".join(u1))
+    if u2:
+        repo.ui.debug(_("  unmatched files in other:\n   %s\n")
+                      % "\n   ".join(u2))
+
+    for f in u1:
+        checkcopies(f, m1, m2)
+    for f in u2:
+        checkcopies(f, m2, m1)
+
+    diverge2 = {}
+    for of, fl in diverge.items():
+        if len(fl) == 1:
+            del diverge[of] # not actually divergent
+        else:
+            diverge2.update(dict.fromkeys(fl)) # reverse map for below
+
+    if fullcopy:
+        repo.ui.debug(_("  all copies found (* = to merge, ! = divergent):\n"))
+        for f in fullcopy:
+            note = ""
+            if f in copy: note += "*"
+            if f in diverge2: note += "!"
+            repo.ui.debug(_("   %s -> %s %s\n") % (f, fullcopy[f], note))
+    del diverge2
+
+    if not fullcopy or not checkdirs:
+        return copy, diverge
+
+    repo.ui.debug(_("  checking for directory renames\n"))
+
+    # generate a directory move map
+    d1, d2 = _dirs(m1), _dirs(m2)
+    invalid = {}
+    dirmove = {}
+
+    # examine each file copy for a potential directory move, which is
+    # when all the files in a directory are moved to a new directory
+    for dst, src in fullcopy.items():
+        dsrc, ddst = _dirname(src), _dirname(dst)
+        if dsrc in invalid:
+            # already seen to be uninteresting
+            continue
+        elif dsrc in d1 and ddst in d1:
+            # directory wasn't entirely moved locally
+            invalid[dsrc] = True
+        elif dsrc in d2 and ddst in d2:
+            # directory wasn't entirely moved remotely
+            invalid[dsrc] = True
+        elif dsrc in dirmove and dirmove[dsrc] != ddst:
+            # files from the same directory moved to two different places
+            invalid[dsrc] = True
+        else:
+            # looks good so far
+            dirmove[dsrc + "/"] = ddst + "/"
+
+    for i in invalid:
+        if i in dirmove:
+            del dirmove[i]
+    del d1, d2, invalid
+
+    if not dirmove:
+        return copy, diverge
+
+    for d in dirmove:
+        repo.ui.debug(_("  dir %s -> %s\n") % (d, dirmove[d]))
+
+    # check unaccounted nonoverlapping files against directory moves
+    for f in u1 + u2:
+        if f not in fullcopy:
+            for d in dirmove:
+                if f.startswith(d):
+                    # new file added in a directory that was moved, move it
+                    df = dirmove[d] + f[len(d):]
+                    if df not in copy:
+                        copy[f] = df
+                        repo.ui.debug(_("  file %s -> %s\n") % (f, copy[f]))
+                    break
+
+    return copy, diverge
--- a/mercurial/dirstate.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/dirstate.py	Fri Apr 04 23:13:32 2008 +0200
@@ -10,7 +10,7 @@
 from node import nullid
 from i18n import _
 import struct, os, bisect, stat, strutil, util, errno, ignore
-import cStringIO, osutil
+import cStringIO, osutil, sys
 
 _unknown = ('?', 0, 0, 0)
 _format = ">cllll"
@@ -63,6 +63,9 @@
         elif name == '_slash':
             self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
             return self._slash
+        elif name == '_checkexec':
+            self._checkexec = util.checkexec(self._root)
+            return self._checkexec
         else:
             raise AttributeError, name
 
@@ -241,6 +244,21 @@
 
     def normallookup(self, f):
         'mark a file normal, but possibly dirty'
+        if self._pl[1] != nullid and f in self._map:
+            # if there is a merge going on and the file was either
+            # in state 'm' or dirty before being removed, restore that state.
+            entry = self._map[f]
+            if entry[0] == 'r' and entry[2] in (-1, -2):
+                source = self._copymap.get(f)
+                if entry[2] == -1:
+                    self.merge(f)
+                elif entry[2] == -2:
+                    self.normaldirty(f)
+                if source:
+                    self.copy(source, f)
+                return
+            if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
+                return
         self._dirty = True
         self._changepath(f, 'n', True)
         self._map[f] = ('n', 0, -1, -1, 0)
@@ -267,8 +285,15 @@
         'mark a file removed'
         self._dirty = True
         self._changepath(f, 'r')
-        self._map[f] = ('r', 0, 0, 0, 0)
-        if f in self._copymap:
+        size = 0
+        if self._pl[1] != nullid and f in self._map:
+            entry = self._map[f]
+            if entry[0] == 'm':
+                size = -1
+            elif entry[0] == 'n' and entry[2] == -2:
+                size = -2
+        self._map[f] = ('r', 0, size, 0, 0)
+        if size == 0 and f in self._copymap:
             del self._copymap[f]
 
     def merge(self, f):
@@ -310,6 +335,16 @@
     def write(self):
         if not self._dirty:
             return
+        st = self._opener("dirstate", "w", atomictemp=True)
+
+        try:
+            gran = int(self._ui.config('dirstate', 'granularity', 1))
+        except ValueError:
+            gran = 1
+        limit = sys.maxint
+        if gran > 0:
+            limit = util.fstat(st).st_mtime - gran
+
         cs = cStringIO.StringIO()
         copymap = self._copymap
         pack = struct.pack
@@ -318,10 +353,11 @@
         for f, e in self._map.iteritems():
             if f in copymap:
                 f = "%s\0%s" % (f, copymap[f])
+            if e[3] > limit and e[0] == 'n':
+                e = (e[0], 0, -1, -1, 0)
             e = pack(_format, e[0], e[1], e[2], e[3], len(f))
             write(e)
             write(f)
-        st = self._opener("dirstate", "w", atomictemp=True)
         st.write(cs.getvalue())
         st.rename()
         self._dirty = self._dirtypl = False
@@ -578,8 +614,9 @@
             if type_ == 'n':
                 if not st:
                     st = lstat(_join(fn))
-                if (size >= 0 and (size != st.st_size
-                                   or (mode ^ st.st_mode) & 0100)
+                if (size >= 0 and
+                    (size != st.st_size
+                     or ((mode ^ st.st_mode) & 0100 and self._checkexec))
                     or size == -2
                     or fn in self._copymap):
                     madd(fn)
--- a/mercurial/hg.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/hg.py	Fri Apr 04 23:13:32 2008 +0200
@@ -148,7 +148,7 @@
 
         abspath = origsource
         copy = False
-        if src_repo.local() and islocal(dest):
+        if src_repo.cancopy() and islocal(dest):
             abspath = os.path.abspath(util.drop_scheme('file', origsource))
             copy = not pull and not rev
 
@@ -243,6 +243,7 @@
             fp.close()
 
             if update:
+                dest_repo.ui.status(_("updating working directory\n"))
                 if not checkout:
                     try:
                         checkout = dest_repo.lookup("default")
--- a/mercurial/hgweb/hgweb_mod.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/hgweb/hgweb_mod.py	Fri Apr 04 23:13:32 2008 +0200
@@ -6,7 +6,7 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-import os, mimetypes, re
+import os, mimetypes, re, mimetools, cStringIO
 from mercurial.node import hex, nullid, short
 from mercurial.repo import RepoError
 from mercurial import mdiff, ui, hg, util, archival, patch, hook
@@ -172,8 +172,8 @@
         if 'REPO_NAME' in req.env:
             req.url += req.env['REPO_NAME'] + '/'
 
-        if req.env.get('PATH_INFO'):
-            parts = req.env.get('PATH_INFO').strip('/').split('/')
+        if 'PATH_INFO' in req.env:
+            parts = req.env['PATH_INFO'].strip('/').split('/')
             repo_parts = req.env.get('REPO_NAME', '').split('/')
             if parts[:len(repo_parts)] == repo_parts:
                 parts = parts[len(repo_parts):]
@@ -226,15 +226,24 @@
         try:
 
             tmpl = self.templater(req)
-            ctype = tmpl('mimetype', encoding=self.encoding)
-            ctype = templater.stringify(ctype)
+            try:
+                ctype = tmpl('mimetype', encoding=self.encoding)
+                ctype = templater.stringify(ctype)
+            except KeyError:
+                # old templates with inline HTTP headers?
+                if 'mimetype' in tmpl:
+                    raise
+                header = tmpl('header', encoding=self.encoding)
+                header_file = cStringIO.StringIO(templater.stringify(header))
+                msg = mimetools.Message(header_file, 0)
+                ctype = msg['content-type']
 
             if cmd == '':
                 req.form['cmd'] = [tmpl.cache['default']]
                 cmd = req.form['cmd'][0]
 
             if cmd not in webcommands.__all__:
-                msg = 'No such method: %s' % cmd
+                msg = 'no such method: %s' % cmd
                 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
             elif cmd == 'file' and 'raw' in req.form.get('style', []):
                 self.ctype = ctype
@@ -248,7 +257,10 @@
 
         except revlog.LookupError, err:
             req.respond(HTTP_NOT_FOUND, ctype)
-            req.write(tmpl('error', error='revision not found: %s' % err.name))
+            msg = str(err)
+            if 'manifest' not in msg:
+                msg = 'revision not found: %s' % err.name
+            req.write(tmpl('error', error=msg))
         except (RepoError, revlog.RevlogError), inst:
             req.respond(HTTP_SERVER_ERROR, ctype)
             req.write(tmpl('error', error=str(inst)))
@@ -279,7 +291,13 @@
         # some functions for the templater
 
         def header(**map):
-            yield tmpl('header', encoding=self.encoding, **map)
+            header = tmpl('header', encoding=self.encoding, **map)
+            if 'mimetype' not in tmpl:
+                # old template with inline HTTP headers
+                header_file = cStringIO.StringIO(templater.stringify(header))
+                msg = mimetools.Message(header_file, 0)
+                header = header_file.read()
+            yield header
 
         def footer(**map):
             yield tmpl("footer", **map)
@@ -367,6 +385,20 @@
             branches.append({"name": branch})
         return branches
 
+    def nodeinbranch(self, ctx):
+        branches = []
+        branch = ctx.branch()
+        if branch != 'default' and self.repo.branchtags().get(branch) != ctx.node():
+            branches.append({"name": branch})
+        return branches
+
+    def nodebranchnodefault(self, ctx):
+        branches = []
+        branch = ctx.branch()
+        if branch != 'default':
+            branches.append({"name": branch})
+        return branches
+
     def showtag(self, tmpl, t1, node=nullid, **args):
         for t in self.repo.nodetags(node):
             yield tmpl(t1, tag=t, **args)
@@ -458,6 +490,7 @@
                              "rev": i,
                              "node": hex(n),
                              "tags": self.nodetagsdict(n),
+                             "inbranch": self.nodeinbranch(ctx),
                              "branches": self.nodebranchdict(ctx)})
 
             if limit > 0:
@@ -529,6 +562,7 @@
                            rev=ctx.rev(),
                            node=hex(n),
                            tags=self.nodetagsdict(n),
+                           inbranch=self.nodeinbranch(ctx),
                            branches=self.nodebranchdict(ctx))
 
                 if count >= self.maxchanges:
@@ -572,6 +606,8 @@
                     files=files,
                     archives=self.archivelist(hex(n)),
                     tags=self.nodetagsdict(n),
+                    branch=self.nodebranchnodefault(ctx),
+                    inbranch=self.nodeinbranch(ctx),
                     branches=self.nodebranchdict(ctx))
 
     def filelog(self, tmpl, fctx):
@@ -642,6 +678,7 @@
                     author=fctx.user(),
                     date=fctx.date(),
                     desc=fctx.description(),
+                    branch=self.nodebranchnodefault(fctx),
                     parent=self.siblings(fctx.parents()),
                     child=self.siblings(fctx.children()),
                     rename=self.renamelink(fl, n),
@@ -689,6 +726,7 @@
                     date=fctx.date(),
                     desc=fctx.description(),
                     rename=self.renamelink(fl, n),
+                    branch=self.nodebranchnodefault(fctx),
                     parent=self.siblings(fctx.parents()),
                     child=self.siblings(fctx.children()),
                     permissions=fctx.manifest().flags(f))
@@ -717,7 +755,7 @@
                 files[short] = (f, n)
 
         if not files:
-            raise ErrorResponse(HTTP_NOT_FOUND, 'Path not found: ' + path)
+            raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
 
         def filelist(**map):
             fl = files.keys()
@@ -757,6 +795,7 @@
                     dentries=dirlist,
                     archives=self.archivelist(hex(node)),
                     tags=self.nodetagsdict(node),
+                    inbranch=self.nodeinbranch(ctx),
                     branches=self.nodebranchdict(ctx))
 
     def tags(self, tmpl):
@@ -837,6 +876,7 @@
                     rev=i,
                     node=hn,
                     tags=self.nodetagsdict(n),
+                    inbranch=self.nodeinbranch(ctx),
                     branches=self.nodebranchdict(ctx)))
 
             yield l
@@ -869,6 +909,7 @@
                     file=path,
                     node=hex(n),
                     rev=fctx.rev(),
+                    branch=self.nodebranchnodefault(fctx),
                     parent=self.siblings(parents),
                     child=self.siblings(fctx.children()),
                     diff=diff)
--- a/mercurial/hgweb/hgwebdir_mod.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/hgweb/hgwebdir_mod.py	Fri Apr 04 23:13:32 2008 +0200
@@ -6,7 +6,7 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-import os
+import os, mimetools, cStringIO
 from mercurial.i18n import gettext as _
 from mercurial.repo import RepoError
 from mercurial import ui, hg, util, templater, templatefilters
@@ -55,7 +55,7 @@
                 self.repos.extend(cleannames(cp.items('paths')))
             if cp.has_section('collections'):
                 for prefix, root in cp.items('collections'):
-                    for path in util.walkrepos(root):
+                    for path in util.walkrepos(root, followsym=True):
                         repo = os.path.normpath(path)
                         name = repo
                         if name.startswith(prefix):
@@ -81,8 +81,17 @@
 
                 virtual = req.env.get("PATH_INFO", "").strip('/')
                 tmpl = self.templater(req)
-                ctype = tmpl('mimetype', encoding=util._encoding)
-                ctype = templater.stringify(ctype)
+                try:
+                    ctype = tmpl('mimetype', encoding=util._encoding)
+                    ctype = templater.stringify(ctype)
+                except KeyError:
+                    # old templates with inline HTTP headers?
+                    if 'mimetype' in tmpl:
+                        raise
+                    header = tmpl('header', encoding=util._encoding)
+                    header_file = cStringIO.StringIO(templater.stringify(header))
+                    msg = mimetools.Message(header_file, 0)
+                    ctype = msg['content-type']
 
                 # a static file
                 if virtual.startswith('static/') or 'static' in req.form:
@@ -181,7 +190,9 @@
                 if u.configbool("web", "hidden", untrusted=True):
                     continue
 
-                parts = [req.env['PATH_INFO'].rstrip('/'), name]
+                parts = [name]
+                if 'PATH_INFO' in req.env:
+                    parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
                 if req.env['SCRIPT_NAME']:
                     parts.insert(0, req.env['SCRIPT_NAME'])
                 url = ('/'.join(parts).replace("//", "/")) + '/'
@@ -246,7 +257,13 @@
     def templater(self, req):
 
         def header(**map):
-            yield tmpl('header', encoding=util._encoding, **map)
+            header = tmpl('header', encoding=util._encoding, **map)
+            if 'mimetype' not in tmpl:
+                # old template with inline HTTP headers
+                header_file = cStringIO.StringIO(templater.stringify(header))
+                msg = mimetools.Message(header_file, 0)
+                header = header_file.read()
+            yield header
 
         def footer(**map):
             yield tmpl("footer", **map)
--- a/mercurial/hgweb/protocol.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/hgweb/protocol.py	Fri Apr 04 23:13:32 2008 +0200
@@ -106,17 +106,26 @@
     req.write(resp)
 
 def unbundle(web, req):
+
     def bail(response, headers={}):
-        length = int(req.env['CONTENT_LENGTH'])
+        length = int(req.env.get('CONTENT_LENGTH', 0))
         for s in util.filechunkiter(req, limit=length):
             # drain incoming bundle, else client will not see
             # response when run outside cgi script
             pass
+
+        status = headers.pop('status', HTTP_OK)
         req.header(headers.items())
-        req.respond(HTTP_OK, HGTYPE)
+        req.respond(status, HGTYPE)
         req.write('0\n')
         req.write(response)
 
+    # enforce that you can only unbundle with POST requests
+    if req.env['REQUEST_METHOD'] != 'POST':
+        headers = {'status': '405 Method Not Allowed'}
+        bail('unbundle requires POST request\n', headers)
+        return
+
     # require ssl by default, auth info cannot be sniffed and
     # replayed
     ssl_req = web.configbool('web', 'push_ssl', True)
@@ -130,8 +139,7 @@
 
     # do not allow push unless explicitly allowed
     if not web.check_perm(req, 'push', False):
-        bail('push not authorized\n',
-             headers={'status': '401 Unauthorized'})
+        bail('push not authorized\n', headers={'status': '401 Unauthorized'})
         return
 
     their_heads = req.form['heads'][0].split(' ')
@@ -175,8 +183,8 @@
 
                 # send addchangegroup output to client
 
-                old_stdout = sys.stdout
-                sys.stdout = cStringIO.StringIO()
+                oldio = sys.stdout, sys.stderr
+                sys.stderr = sys.stdout = cStringIO.StringIO()
 
                 try:
                     url = 'remote:%s:%s' % (proto,
@@ -188,7 +196,7 @@
                         ret = 0
                 finally:
                     val = sys.stdout.getvalue()
-                    sys.stdout = old_stdout
+                    sys.stdout, sys.stderr = oldio
                 req.write('%d\n' % ret)
                 req.write(val)
             finally:
--- a/mercurial/hgweb/server.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/hgweb/server.py	Fri Apr 04 23:13:32 2008 +0200
@@ -253,13 +253,6 @@
                 return hgwebobj
             self.application = make_handler()
 
-            addr = address
-            if addr in ('', '::'):
-                addr = socket.gethostname()
-
-            self.addr, self.port = addr, port
-            self.prefix = prefix
-
             if ssl_cert:
                 try:
                     from OpenSSL import SSL
@@ -273,6 +266,15 @@
                 self.server_bind()
                 self.server_activate()
 
+            self.addr, self.port = self.socket.getsockname()[0:2]
+            self.prefix = prefix
+
+            self.fqaddr = socket.getfqdn(address)
+            try:
+                socket.getaddrbyhost(self.fqaddr)
+            except:
+                fqaddr = address
+
     class IPv6HTTPServer(MercurialHTTPServer):
         address_family = getattr(socket, 'AF_INET6', None)
 
@@ -292,4 +294,5 @@
         else:
             return MercurialHTTPServer((address, port), handler)
     except socket.error, inst:
-        raise util.Abort(_('cannot start server: %s') % inst.args[1])
+        raise util.Abort(_("cannot start server at '%s:%d': %s")
+                         % (address, port, inst.args[1]))
--- a/mercurial/hgweb/webcommands.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/hgweb/webcommands.py	Fri Apr 04 23:13:32 2008 +0200
@@ -34,10 +34,13 @@
 
     try:
         fctx = web.filectx(req)
-    except revlog.LookupError:
-        content = web.manifest(tmpl, web.changectx(req), path)
-        req.respond(HTTP_OK, web.ctype)
-        return content
+    except revlog.LookupError, inst:
+        try:
+            content = web.manifest(tmpl, web.changectx(req), path)
+            req.respond(HTTP_OK, web.ctype)
+            return content
+        except ErrorResponse:
+            raise inst
 
     path = fctx.path()
     text = fctx.data()
@@ -53,10 +56,13 @@
     if path:
         try:
             return web.filerevision(tmpl, web.filectx(req))
-        except revlog.LookupError:
+        except revlog.LookupError, inst:
             pass
 
-    return web.manifest(tmpl, web.changectx(req), path)
+    try:
+        return web.manifest(tmpl, web.changectx(req), path)
+    except ErrorResponse:
+        raise inst
 
 def changelog(web, req, tmpl, shortlog = False):
     if 'node' in req.form:
@@ -109,7 +115,7 @@
         web.configbool("web", "allow" + type_, False))):
         web.archive(tmpl, req, req.form['node'][0], type_)
         return []
-    raise ErrorResponse(HTTP_NOT_FOUND, 'Unsupported archive type: %s' % type_)
+    raise ErrorResponse(HTTP_NOT_FOUND, 'unsupported archive type: %s' % type_)
 
 def static(web, req, tmpl):
     fname = req.form['file'][0]
--- a/mercurial/hook.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/hook.py	Fri Apr 04 23:13:32 2008 +0200
@@ -85,6 +85,7 @@
 
 _redirect = False
 def redirect(state):
+    global _redirect
     _redirect = state
 
 def hook(ui, repo, name, throw=False, **args):
@@ -92,8 +93,8 @@
 
     if _redirect:
         # temporarily redirect stdout to stderr
-        oldstdout = os.dup(sys.stdout.fileno())
-        os.dup2(sys.stderr.fileno(), sys.stdout.fileno())
+        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]
@@ -106,8 +107,9 @@
                             args, throw) or r
         else:
             r = _exthook(ui, repo, hname, cmd, args, throw) or r
-    return r
 
     if _redirect:
-        os.dup2(oldstdout, sys.stdout.fileno())
+        os.dup2(oldstdout, sys.__stdout__.fileno())
         os.close(oldstdout)
+
+    return r
--- a/mercurial/httprepo.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/httprepo.py	Fri Apr 04 23:13:32 2008 +0200
@@ -7,7 +7,6 @@
 # of the GNU General Public License, incorporated herein by reference.
 
 from node import bin, hex
-from remoterepo import remoterepository
 from i18n import _
 import repo, os, urllib, urllib2, urlparse, zlib, util, httplib
 import errno, keepalive, socket, changegroup
@@ -181,7 +180,7 @@
             l[i] = '%%%02X' % ord(c)
     return ''.join(l)
 
-class httprepository(remoterepository):
+class httprepository(repo.repository):
     def __init__(self, ui, path):
         self.path = path
         self.caps = None
--- a/mercurial/localrepo.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/localrepo.py	Fri Apr 04 23:13:32 2008 +0200
@@ -124,21 +124,29 @@
 
     tag_disallowed = ':\r\n'
 
-    def _tag(self, name, node, message, local, user, date, parent=None,
+    def _tag(self, names, node, message, local, user, date, parent=None,
              extra={}):
         use_dirstate = parent is None
 
+        if isinstance(names, str):
+            allchars = names
+            names = (names,)
+        else:
+            allchars = ''.join(names)
         for c in self.tag_disallowed:
-            if c in name:
+            if c in allchars:
                 raise util.Abort(_('%r cannot be used in a tag name') % c)
 
-        self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
+        for name in names:
+            self.hook('pretag', throw=True, node=hex(node), tag=name,
+                      local=local)
 
-        def writetag(fp, name, munge, prevtags):
+        def writetags(fp, names, munge, prevtags):
             fp.seek(0, 2)
             if prevtags and prevtags[-1] != '\n':
                 fp.write('\n')
-            fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
+            for name in names:
+                fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
             fp.close()
 
         prevtags = ''
@@ -151,8 +159,9 @@
                 prevtags = fp.read()
 
             # local tags are stored in the current charset
-            writetag(fp, name, None, prevtags)
-            self.hook('tag', node=hex(node), tag=name, local=local)
+            writetags(fp, names, None, prevtags)
+            for name in names:
+                self.hook('tag', node=hex(node), tag=name, local=local)
             return
 
         if use_dirstate:
@@ -172,7 +181,7 @@
                 fp.write(prevtags)
 
         # committed tags are stored in UTF-8
-        writetag(fp, name, util.fromlocal, prevtags)
+        writetags(fp, names, util.fromlocal, prevtags)
 
         if use_dirstate and '.hgtags' not in self.dirstate:
             self.add(['.hgtags'])
@@ -180,20 +189,24 @@
         tagnode = self.commit(['.hgtags'], message, user, date, p1=parent,
                               extra=extra)
 
-        self.hook('tag', node=hex(node), tag=name, local=local)
+        for name in names:
+            self.hook('tag', node=hex(node), tag=name, local=local)
 
         return tagnode
 
-    def tag(self, name, node, message, local, user, date):
-        '''tag a revision with a symbolic name.
+    def tag(self, names, node, message, local, user, date):
+        '''tag a revision with one or more symbolic names.
 
-        if local is True, the tag is stored in a per-repository file.
-        otherwise, it is stored in the .hgtags file, and a new
+        names is a list of strings or, when adding a single tag, names may be a
+        string.
+
+        if local is True, the tags are stored in a per-repository file.
+        otherwise, they are stored in the .hgtags file, and a new
         changeset is committed with the change.
 
         keyword arguments:
 
-        local: whether to store tag in non-version-controlled file
+        local: whether to store tags in non-version-controlled file
         (default False)
 
         message: commit message to use if committing
@@ -202,14 +215,12 @@
 
         date: date tuple to use if committing'''
 
-        date = util.parsedate(date)
         for x in self.status()[:5]:
             if '.hgtags' in x:
                 raise util.Abort(_('working copy of .hgtags is changed '
                                    '(please commit .hgtags manually)'))
 
-
-        self._tag(name, node, message, local, user, date)
+        self._tag(names, node, message, local, user, date)
 
     def tags(self):
         '''return a mapping of tag to node'''
@@ -452,9 +463,6 @@
             pass
         raise repo.RepoError(_("unknown revision '%s'") % key)
 
-    def dev(self):
-        return os.lstat(self.path).st_dev
-
     def local(self):
         return True
 
@@ -625,8 +633,8 @@
 
     def invalidate(self):
         for a in "changelog manifest".split():
-            if hasattr(self, a):
-                self.__delattr__(a)
+            if a in self.__dict__:
+                delattr(self, a)
         self.tagscache = None
         self._tagstypecache = None
         self.nodetagscache = None
@@ -744,6 +752,8 @@
         if files:
             files = util.unique(files)
         try:
+            wlock = self.wlock()
+            lock = self.lock()
             commit = []
             remove = []
             changed = []
@@ -771,6 +781,11 @@
             if use_dirstate:
                 p1, p2 = self.dirstate.parents()
                 update_dirstate = True
+
+                if (not force and p2 != nullid and
+                    (files or match != util.always)):
+                    raise util.Abort(_('cannot partially commit a merge '
+                                       '(do not specify files or patterns)'))
             else:
                 p1, p2 = p1, p2 or nullid
                 update_dirstate = (self.dirstate.parents()[0] == p1)
@@ -802,8 +817,6 @@
 
             self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
 
-            wlock = self.wlock()
-            lock = self.lock()
             tr = self.transaction()
             trp = weakref.proxy(tr)
 
@@ -885,13 +898,12 @@
             if branchname:
                 extra["branch"] = branchname
 
-            if use_dirstate:
-                lines = [line.rstrip() for line in text.rstrip().splitlines()]
-                while lines and not lines[0]:
-                    del lines[0]
-                if not lines:
-                    raise util.Abort(_("empty commit message"))
-                text = '\n'.join(lines)
+            lines = [line.rstrip() for line in text.rstrip().splitlines()]
+            while lines and not lines[0]:
+                del lines[0]
+            if not lines and use_dirstate:
+                raise util.Abort(_("empty commit message"))
+            text = '\n'.join(lines)
 
             n = self.changelog.add(mn, changed + removed, text, trp, p1, p2,
                                    user, date, extra)
@@ -2063,7 +2075,7 @@
         l = fp.readline()
         try:
             total_files, total_bytes = map(int, l.split(' ', 1))
-        except ValueError, TypeError:
+        except (ValueError, TypeError):
             raise util.UnexpectedOutput(
                 _('Unexpected response from remote server:'), l)
         self.ui.status(_('%d files to transfer, %s of data\n') %
--- a/mercurial/mdiff.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/mdiff.py	Fri Apr 04 23:13:32 2008 +0200
@@ -5,6 +5,7 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
+from i18n import _
 import bdiff, mpatch, re, struct, util, md5
 
 def splitnewlines(text):
@@ -47,6 +48,12 @@
                 v = self.defaults[k]
             setattr(self, k, v)
 
+        try:
+            self.context = int(self.context)
+        except ValueError:
+            raise util.Abort(_('diff context lines count must be '
+                               'an integer, not %r') % self.context)
+
 defaultopts = diffopts()
 
 def wsclean(opts, text):
--- a/mercurial/merge.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/merge.py	Fri Apr 04 23:13:32 2008 +0200
@@ -7,29 +7,26 @@
 
 from node import nullid, nullrev
 from i18n import _
-import errno, util, os, heapq, filemerge
+import errno, util, os, filemerge, copies
 
-def checkunknown(wctx, mctx):
+def _checkunknown(wctx, mctx):
     "check for collisions between unknown files and files in mctx"
-    man = mctx.manifest()
     for f in wctx.unknown():
-        if f in man:
-            if mctx.filectx(f).cmp(wctx.filectx(f).data()):
-                raise util.Abort(_("untracked file in working directory differs"
-                                   " from file in requested revision: '%s'")
-                                 % f)
+        if f in mctx and mctx[f].cmp(wctx[f].data()):
+            raise util.Abort(_("untracked file in working directory differs"
+                               " from file in requested revision: '%s'") % f)
 
-def checkcollision(mctx):
+def _checkcollision(mctx):
     "check for case folding collisions in the destination context"
     folded = {}
-    for fn in mctx.manifest():
+    for fn in mctx:
         fold = fn.lower()
         if fold in folded:
             raise util.Abort(_("case-folding collision between %s and %s")
                              % (fn, folded[fold]))
         folded[fold] = fn
 
-def forgetremoved(wctx, mctx, branchmerge):
+def _forgetremoved(wctx, mctx, branchmerge):
     """
     Forget removed files
 
@@ -45,249 +42,18 @@
     """
 
     action = []
-    man = mctx.manifest()
     state = branchmerge and 'r' or 'f'
     for f in wctx.deleted():
-        if f not in man:
+        if f not in mctx:
             action.append((f, state))
 
     if not branchmerge:
         for f in wctx.removed():
-            if f not in man:
+            if f not in mctx:
                 action.append((f, "f"))
 
     return action
 
-def findcopies(repo, m1, m2, ma, limit):
-    """
-    Find moves and copies between m1 and m2 back to limit linkrev
-    """
-
-    def nonoverlap(d1, d2, d3):
-        "Return list of elements in d1 not in d2 or d3"
-        l = [d for d in d1 if d not in d3 and d not in d2]
-        l.sort()
-        return l
-
-    def dirname(f):
-        s = f.rfind("/")
-        if s == -1:
-            return ""
-        return f[:s]
-
-    def dirs(files):
-        d = {}
-        for f in files:
-            f = dirname(f)
-            while f not in d:
-                d[f] = True
-                f = dirname(f)
-        return d
-
-    wctx = repo.workingctx()
-
-    def makectx(f, n):
-        if len(n) == 20:
-            return repo.filectx(f, fileid=n)
-        return wctx.filectx(f)
-    ctx = util.cachefunc(makectx)
-
-    def findold(fctx):
-        "find files that path was copied from, back to linkrev limit"
-        old = {}
-        seen = {}
-        orig = fctx.path()
-        visit = [fctx]
-        while visit:
-            fc = visit.pop()
-            s = str(fc)
-            if s in seen:
-                continue
-            seen[s] = 1
-            if fc.path() != orig and fc.path() not in old:
-                old[fc.path()] = 1
-            if fc.rev() < limit:
-                continue
-            visit += fc.parents()
-
-        old = old.keys()
-        old.sort()
-        return old
-
-    copy = {}
-    fullcopy = {}
-    diverge = {}
-
-    def checkcopies(c, man, aman):
-        '''check possible copies for filectx c'''
-        for of in findold(c):
-            fullcopy[c.path()] = of # remember for dir rename detection
-            if of not in man: # original file not in other manifest?
-                if of in ma:
-                    diverge.setdefault(of, []).append(c.path())
-                continue
-            # if the original file is unchanged on the other branch,
-            # no merge needed
-            if man[of] == aman.get(of):
-                continue
-            c2 = ctx(of, man[of])
-            ca = c.ancestor(c2)
-            if not ca: # unrelated?
-                continue
-            # named changed on only one side?
-            if ca.path() == c.path() or ca.path() == c2.path():
-                if c == ca and c2 == ca: # no merge needed, ignore copy
-                    continue
-                copy[c.path()] = of
-
-    if not repo.ui.configbool("merge", "followcopies", True):
-        return {}, {}
-
-    # avoid silly behavior for update from empty dir
-    if not m1 or not m2 or not ma:
-        return {}, {}
-
-    repo.ui.debug(_("  searching for copies back to rev %d\n") % limit)
-
-    u1 = nonoverlap(m1, m2, ma)
-    u2 = nonoverlap(m2, m1, ma)
-
-    if u1:
-        repo.ui.debug(_("  unmatched files in local:\n   %s\n")
-                      % "\n   ".join(u1))
-    if u2:
-        repo.ui.debug(_("  unmatched files in other:\n   %s\n")
-                      % "\n   ".join(u2))
-
-    for f in u1:
-        checkcopies(ctx(f, m1[f]), m2, ma)
-
-    for f in u2:
-        checkcopies(ctx(f, m2[f]), m1, ma)
-
-    diverge2 = {}
-    for of, fl in diverge.items():
-        if len(fl) == 1:
-            del diverge[of] # not actually divergent
-        else:
-            diverge2.update(dict.fromkeys(fl)) # reverse map for below
-
-    if fullcopy:
-        repo.ui.debug(_("  all copies found (* = to merge, ! = divergent):\n"))
-        for f in fullcopy:
-            note = ""
-            if f in copy: note += "*"
-            if f in diverge2: note += "!"
-            repo.ui.debug(_("   %s -> %s %s\n") % (f, fullcopy[f], note))
-
-    del diverge2
-
-    if not fullcopy or not repo.ui.configbool("merge", "followdirs", True):
-        return copy, diverge
-
-    repo.ui.debug(_("  checking for directory renames\n"))
-
-    # generate a directory move map
-    d1, d2 = dirs(m1), dirs(m2)
-    invalid = {}
-    dirmove = {}
-
-    # examine each file copy for a potential directory move, which is
-    # when all the files in a directory are moved to a new directory
-    for dst, src in fullcopy.items():
-        dsrc, ddst = dirname(src), dirname(dst)
-        if dsrc in invalid:
-            # already seen to be uninteresting
-            continue
-        elif dsrc in d1 and ddst in d1:
-            # directory wasn't entirely moved locally
-            invalid[dsrc] = True
-        elif dsrc in d2 and ddst in d2:
-            # directory wasn't entirely moved remotely
-            invalid[dsrc] = True
-        elif dsrc in dirmove and dirmove[dsrc] != ddst:
-            # files from the same directory moved to two different places
-            invalid[dsrc] = True
-        else:
-            # looks good so far
-            dirmove[dsrc + "/"] = ddst + "/"
-
-    for i in invalid:
-        if i in dirmove:
-            del dirmove[i]
-
-    del d1, d2, invalid
-
-    if not dirmove:
-        return copy, diverge
-
-    for d in dirmove:
-        repo.ui.debug(_("  dir %s -> %s\n") % (d, dirmove[d]))
-
-    # check unaccounted nonoverlapping files against directory moves
-    for f in u1 + u2:
-        if f not in fullcopy:
-            for d in dirmove:
-                if f.startswith(d):
-                    # new file added in a directory that was moved, move it
-                    copy[f] = dirmove[d] + f[len(d):]
-                    repo.ui.debug(_("  file %s -> %s\n") % (f, copy[f]))
-                    break
-
-    return copy, diverge
-
-def symmetricdifference(repo, rev1, rev2):
-    """symmetric difference of the sets of ancestors of rev1 and rev2
-
-    I.e. revisions that are ancestors of rev1 or rev2, but not both.
-    """
-    # basic idea:
-    # - mark rev1 and rev2 with different colors
-    # - walk the graph in topological order with the help of a heap;
-    #   for each revision r:
-    #     - if r has only one color, we want to return it
-    #     - add colors[r] to its parents
-    #
-    # We keep track of the number of revisions in the heap that
-    # we may be interested in.  We stop walking the graph as soon
-    # as this number reaches 0.
-    WHITE = 1
-    BLACK = 2
-    ALLCOLORS = WHITE | BLACK
-    colors = {rev1: WHITE, rev2: BLACK}
-
-    cl = repo.changelog
-
-    visit = [-rev1, -rev2]
-    heapq.heapify(visit)
-    n_wanted = len(visit)
-    ret = []
-
-    while n_wanted:
-        r = -heapq.heappop(visit)
-        wanted = colors[r] != ALLCOLORS
-        n_wanted -= wanted
-        if wanted:
-            ret.append(r)
-
-        for p in cl.parentrevs(r):
-            if p == nullrev:
-                continue
-            if p not in colors:
-                # first time we see p; add it to visit
-                n_wanted += wanted
-                colors[p] = colors[r]
-                heapq.heappush(visit, -p)
-            elif colors[p] != ALLCOLORS and colors[p] != colors[r]:
-                # at first we thought we wanted p, but now
-                # we know we don't really want it
-                n_wanted -= 1
-                colors[p] |= colors[r]
-
-        del colors[r]
-
-    return ret
-
 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
     """
     Merge p1 and p2 with ancestor ma and generate merge action list
@@ -305,8 +71,7 @@
     ma = pa.manifest()
     backwards = (pa == p2)
     action = []
-    copy = {}
-    diverge = {}
+    copy, copied, diverge = {}, {}, {}
 
     def fmerge(f, f2=None, fa=None):
         """merge flags"""
@@ -335,18 +100,13 @@
         repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
         action.append((f, m) + args)
 
-    if not (backwards or overwrite):
-        rev1 = p1.rev()
-        if rev1 is None:
-            # p1 is a workingctx
-            rev1 = p1.parents()[0].rev()
-        limit = min(symmetricdifference(repo, rev1, p2.rev()))
-        copy, diverge = findcopies(repo, m1, m2, ma, limit)
-
-    for of, fl in diverge.items():
-        act("divergent renames", "dr", of, fl)
-
-    copied = dict.fromkeys(copy.values())
+    if pa and not (backwards or overwrite):
+        if repo.ui.configbool("merge", "followcopies", True):
+            dirs = repo.ui.configbool("merge", "followdirs", True)
+            copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
+        copied = dict.fromkeys(copy.values())
+        for of, fl in diverge.items():
+            act("divergent renames", "dr", of, fl)
 
     # Compare manifests
     for f, n in m1.iteritems():
@@ -588,7 +348,6 @@
                 else:
                     raise util.Abort(_("branch %s not found") % wc.branch())
         overwrite = force and not branchmerge
-        forcemerge = force and branchmerge
         pl = wc.parents()
         p1, p2 = pl[0], repo.changectx(node)
         pa = p1.ancestor(p2)
@@ -598,27 +357,40 @@
         ### check phase
         if not overwrite and len(pl) > 1:
             raise util.Abort(_("outstanding uncommitted merges"))
-        if pa == p1 or pa == p2: # is there a linear path from p1 to p2?
-            if branchmerge:
-                if p1.branch() != p2.branch() and pa != p2:
+        if branchmerge:
+            if pa == p2:
+                raise util.Abort(_("can't merge with ancestor"))
+            elif pa == p1:
+                if p1.branch() != p2.branch():
                     fastforward = True
                 else:
-                    raise util.Abort(_("there is nothing to merge, just use "
-                                       "'hg update' or look at 'hg heads'"))
-        elif not (overwrite or branchmerge):
-            raise util.Abort(_("update spans branches, use 'hg merge' "
-                               "or 'hg update -C' to lose changes"))
-        if branchmerge and not forcemerge:
-            if wc.files():
+                    raise util.Abort(_("nothing to merge (use 'hg update'"
+                                       " or check 'hg heads')"))
+            if not force and (wc.files() or wc.deleted()):
                 raise util.Abort(_("outstanding uncommitted changes"))
+        elif not overwrite:
+            if pa == p1 or pa == p2: # linear
+                pass # all good
+            elif p1.branch() == p2.branch():
+                if wc.files() or wc.deleted():
+                    raise util.Abort(_("crosses branches (use 'hg merge' or "
+                                       "'hg update -C' to discard changes)"))
+                raise util.Abort(_("crosses branches (use 'hg merge' "
+                                   "or 'hg update -C')"))
+            elif wc.files() or wc.deleted():
+                raise util.Abort(_("crosses named branches (use "
+                                   "'hg update -C' to discard changes)"))
+            else:
+                # Allow jumping branches if there are no changes
+                overwrite = True
 
         ### calculate phase
         action = []
         if not force:
-            checkunknown(wc, p2)
+            _checkunknown(wc, p2)
         if not util.checkfolding(repo.path):
-            checkcollision(p2)
-        action += forgetremoved(wc, p2, branchmerge)
+            _checkcollision(p2)
+        action += _forgetremoved(wc, p2, branchmerge)
         action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
 
         ### apply phase
--- a/mercurial/patch.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/patch.py	Fri Apr 04 23:13:32 2008 +0200
@@ -8,7 +8,7 @@
 
 from i18n import _
 from node import hex, nullid, short
-import base85, cmdutil, mdiff, util, context, revlog, diffhelpers
+import base85, cmdutil, mdiff, util, context, revlog, diffhelpers, copies
 import cStringIO, email.Parser, os, popen2, re, sha, errno
 import sys, tempfile, zlib
 
@@ -505,7 +505,7 @@
         return -1
 
 class hunk:
-    def __init__(self, desc, num, lr, context, gitpatch=None):
+    def __init__(self, desc, num, lr, context, create=False, remove=False):
         self.number = num
         self.desc = desc
         self.hunk = [ desc ]
@@ -515,7 +515,8 @@
             self.read_context_hunk(lr)
         else:
             self.read_unified_hunk(lr)
-        self.gitpatch = gitpatch
+        self.create = create
+        self.remove = remove and not create
 
     def read_unified_hunk(self, lr):
         m = unidesc.match(self.desc)
@@ -640,6 +641,7 @@
         self.hunk[0] = self.desc
 
     def reverse(self):
+        self.create, self.remove = self.remove, self.create
         origlena = self.lena
         origstarta = self.starta
         self.lena = self.lenb
@@ -670,12 +672,10 @@
         return len(self.a) == self.lena and len(self.b) == self.lenb
 
     def createfile(self):
-        create = self.gitpatch is None or self.gitpatch.op == 'ADD'
-        return self.starta == 0 and self.lena == 0 and create
+        return self.starta == 0 and self.lena == 0 and self.create
 
     def rmfile(self):
-        remove = self.gitpatch is None or self.gitpatch.op == 'DELETE'
-        return self.startb == 0 and self.lenb == 0 and remove
+        return self.startb == 0 and self.lenb == 0 and self.remove
 
     def fuzzit(self, l, fuzz, toponly):
         # this removes context lines from the top and bottom of list 'l'.  It
@@ -800,13 +800,13 @@
             while i < pathlen - 1 and path[i] == '/':
                 i += 1
             count -= 1
-        return path[i:].rstrip()
+        return path[:i].lstrip(), path[i:].rstrip()
 
     nulla = afile_orig == "/dev/null"
     nullb = bfile_orig == "/dev/null"
-    afile = pathstrip(afile_orig, strip)
+    abase, afile = pathstrip(afile_orig, strip)
     gooda = not nulla and os.path.exists(afile)
-    bfile = pathstrip(bfile_orig, strip)
+    bbase, bfile = pathstrip(bfile_orig, strip)
     if afile == bfile:
         goodb = gooda
     else:
@@ -815,16 +815,20 @@
     if reverse:
         createfunc = hunk.rmfile
     missing = not goodb and not gooda and not createfunc()
+    # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
+    # diff is between a file and its backup. In this case, the original
+    # file should be patched (see original mpatch code).
+    isbackup = (abase == bbase and bfile.startswith(afile))
     fname = None
     if not missing:
         if gooda and goodb:
-            fname = (afile in bfile) and afile or bfile
+            fname = isbackup and afile or bfile
         elif gooda:
             fname = afile
 
     if not fname:
         if not nullb:
-            fname = (afile in bfile) and afile or bfile
+            fname = isbackup and afile or bfile
         elif not nulla:
             fname = afile
         else:
@@ -912,7 +916,9 @@
                 if context == None and x.startswith('***************'):
                     context = True
                 gpatch = changed.get(bfile[2:], (None, None))[1]
-                current_hunk = hunk(x, hunknum + 1, lr, context, gpatch)
+                create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
+                remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
+                current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
             except PatchError, err:
                 ui.debug(err)
                 current_hunk = None
@@ -1049,9 +1055,9 @@
     return err
 
 def diffopts(ui, opts={}, untrusted=False):
-    def get(key, name=None):
+    def get(key, name=None, getter=ui.configbool):
         return (opts.get(key) or
-                ui.configbool('diff', name or key, None, untrusted=untrusted))
+                getter('diff', name or key, None, untrusted=untrusted))
     return mdiff.diffopts(
         text=opts.get('text'),
         git=get('git'),
@@ -1060,7 +1066,7 @@
         ignorews=get('ignore_all_space', 'ignorews'),
         ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
         ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
-        context=get('unified'))
+        context=get('unified', getter=ui.config))
 
 def updatedir(ui, repo, patches):
     '''Update dirstate after patch application according to metadata'''
@@ -1202,36 +1208,6 @@
             execf2 = mc.execf
             linkf2 = mc.linkf
 
-    # returns False if there was no rename between ctx1 and ctx2
-    # returns None if the file was created between ctx1 and ctx2
-    # returns the (file, node) present in ctx1 that was renamed to f in ctx2
-    # This will only really work if c1 is the Nth 1st parent of c2.
-    def renamed(c1, c2, man, f):
-        startrev = c1.rev()
-        c = c2
-        crev = c.rev()
-        if crev is None:
-            crev = repo.changelog.count()
-        orig = f
-        files = (f,)
-        while crev > startrev:
-            if f in files:
-                try:
-                    src = getfilectx(f, c).renamed()
-                except revlog.LookupError:
-                    return None
-                if src:
-                    f = src[0]
-            crev = c.parents()[0].rev()
-            # try to reuse
-            c = getctx(crev)
-            files = c.files()
-        if f not in man:
-            return None
-        if f == orig:
-            return False
-        return f
-
     if repo.ui.quiet:
         r = None
     else:
@@ -1239,28 +1215,9 @@
         r = [hexfunc(node) for node in [node1, node2] if node]
 
     if opts.git:
-        copied = {}
-        c1, c2 = ctx1, ctx2
-        files = added
-        man = man1
-        if node2 and ctx1.rev() >= ctx2.rev():
-            # renamed() starts at c2 and walks back in history until c1.
-            # Since ctx1.rev() >= ctx2.rev(), invert ctx2 and ctx1 to
-            # detect (inverted) copies.
-            c1, c2 = ctx2, ctx1
-            files = removed
-            man = ctx2.manifest()
-        for f in files:
-            src = renamed(c1, c2, man, f)
-            if src:
-                copied[f] = src
-        if ctx1 == c2:
-            # invert the copied dict
-            copied = dict([(v, k) for (k, v) in copied.iteritems()])
-        # If we've renamed file foo to bar (copied['bar'] = 'foo'),
-        # avoid showing a diff for foo if we're going to show
-        # the rename to bar.
-        srcs = [x[1] for x in copied.iteritems() if x[0] in added]
+        copy, diverge = copies.copies(repo, ctx1, ctx2, repo.changectx(nullid))
+        for k, v in copy.items():
+            copy[v] = k
 
     all = modified + added + removed
     all.sort()
@@ -1286,8 +1243,8 @@
 
             if f in added:
                 mode = gitmode(execf2(f), linkf2(f))
-                if f in copied:
-                    a = copied[f]
+                if f in copy:
+                    a = copy[f]
                     omode = gitmode(man1.execf(a), man1.linkf(a))
                     addmodehdr(header, omode, mode)
                     if a in removed and a not in gone:
@@ -1303,7 +1260,8 @@
                 if util.binary(tn):
                     dodiff = 'binary'
             elif f in removed:
-                if f in srcs:
+                # have we already reported a copy above?
+                if f in copy and copy[f] in added and copy[copy[f]] == f:
                     dodiff = False
                 else:
                     mode = gitmode(man1.execf(f), man1.linkf(f))
--- a/mercurial/remoterepo.py	Fri Apr 04 23:09:54 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-# remoterepo - remote repository proxy classes for mercurial
-#
-# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-
-import repo
-
-class remoterepository(repo.repository):
-    def dev(self):
-        return -1
-
-    def local(self):
-        return False
-
-class remotelock(object):
-    def __init__(self, repo):
-        self.repo = repo
-    def release(self):
-        self.repo.unlock()
-        self.repo = None
-    def __del__(self):
-        if self.repo:
-            self.release()
--- a/mercurial/repo.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/repo.py	Fri Apr 04 23:13:32 2008 +0200
@@ -34,3 +34,9 @@
             raise NoCapability(_('cannot %s; remote repository does not '
                                  'support the %r capability') %
                                (purpose, name))
+
+    def local(self):
+        return False
+
+    def cancopy(self):
+        return self.local()
--- a/mercurial/revlog.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/revlog.py	Fri Apr 04 23:13:32 2008 +0200
@@ -977,15 +977,18 @@
 
         tr.add(self.datafile, dataoff)
         df = self.opener(self.datafile, 'w')
-        calc = self._io.size
-        for r in xrange(self.count()):
-            start = self.start(r) + (r + 1) * calc
-            length = self.length(r)
-            fp.seek(start)
-            d = fp.read(length)
-            df.write(d)
+        try:
+            calc = self._io.size
+            for r in xrange(self.count()):
+                start = self.start(r) + (r + 1) * calc
+                length = self.length(r)
+                fp.seek(start)
+                d = fp.read(length)
+                df.write(d)
+        finally:
+            df.close()
+
         fp.close()
-        df.close()
         fp = self.opener(self.indexfile, 'w', atomictemp=True)
         self.version &= ~(REVLOGNGINLINEDATA)
         self._inline = False
@@ -1013,7 +1016,12 @@
         if not self._inline:
             dfh = self.opener(self.datafile, "a")
         ifh = self.opener(self.indexfile, "a+")
-        return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh)
+        try:
+            return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh)
+        finally:
+            if dfh:
+                dfh.close()
+            ifh.close()
 
     def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh):
         node = hash(text, p1, p2)
@@ -1154,86 +1162,91 @@
             transaction.add(self.datafile, end)
             dfh = self.opener(self.datafile, "a")
 
-        # loop through our set of deltas
-        chain = None
-        for chunk in revs:
-            node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
-            link = linkmapper(cs)
-            if node in self.nodemap:
-                # this can happen if two branches make the same change
-                # if unique:
-                #    raise RevlogError(_("already have %s") % hex(node[:4]))
-                chain = node
-                continue
-            delta = buffer(chunk, 80)
-            del chunk
+        try:
+            # loop through our set of deltas
+            chain = None
+            for chunk in revs:
+                node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
+                link = linkmapper(cs)
+                if node in self.nodemap:
+                    # this can happen if two branches make the same change
+                    # if unique:
+                    #    raise RevlogError(_("already have %s") % hex(node[:4]))
+                    chain = node
+                    continue
+                delta = buffer(chunk, 80)
+                del chunk
 
-            for p in (p1, p2):
-                if not p in self.nodemap:
-                    raise LookupError(p, self.indexfile, _('unknown parent'))
-
-            if not chain:
-                # retrieve the parent revision of the delta chain
-                chain = p1
-                if not chain in self.nodemap:
-                    raise LookupError(chain, self.indexfile, _('unknown base'))
+                for p in (p1, p2):
+                    if not p in self.nodemap:
+                        raise LookupError(p, self.indexfile, _('unknown parent'))
 
-            # full versions are inserted when the needed deltas become
-            # comparable to the uncompressed text or when the previous
-            # version is not the one we have a delta against. We use
-            # the size of the previous full rev as a proxy for the
-            # current size.
-
-            if chain == prev:
-                cdelta = compress(delta)
-                cdeltalen = len(cdelta[0]) + len(cdelta[1])
-                textlen = mdiff.patchedsize(textlen, delta)
+                if not chain:
+                    # retrieve the parent revision of the delta chain
+                    chain = p1
+                    if not chain in self.nodemap:
+                        raise LookupError(chain, self.indexfile, _('unknown base'))
 
-            if chain != prev or (end - start + cdeltalen) > textlen * 2:
-                # flush our writes here so we can read it in revision
-                if dfh:
-                    dfh.flush()
-                ifh.flush()
-                text = self.revision(chain)
-                if len(text) == 0:
-                    # skip over trivial delta header
-                    text = buffer(delta, 12)
-                else:
-                    text = mdiff.patches(text, [delta])
-                del delta
-                chk = self._addrevision(text, transaction, link, p1, p2, None,
-                                        ifh, dfh)
-                if not dfh and not self._inline:
-                    # addrevision switched from inline to conventional
-                    # reopen the index
-                    dfh = self.opener(self.datafile, "a")
-                    ifh = self.opener(self.indexfile, "a")
-                if chk != node:
-                    raise RevlogError(_("consistency error adding group"))
-                textlen = len(text)
-            else:
-                e = (offset_type(end, 0), cdeltalen, textlen, base,
-                     link, self.rev(p1), self.rev(p2), node)
-                self.index.insert(-1, e)
-                self.nodemap[node] = r
-                entry = self._io.packentry(e, self.node, self.version, r)
-                if self._inline:
-                    ifh.write(entry)
-                    ifh.write(cdelta[0])
-                    ifh.write(cdelta[1])
-                    self.checkinlinesize(transaction, ifh)
-                    if not self._inline:
+                # full versions are inserted when the needed deltas become
+                # comparable to the uncompressed text or when the previous
+                # version is not the one we have a delta against. We use
+                # the size of the previous full rev as a proxy for the
+                # current size.
+
+                if chain == prev:
+                    cdelta = compress(delta)
+                    cdeltalen = len(cdelta[0]) + len(cdelta[1])
+                    textlen = mdiff.patchedsize(textlen, delta)
+
+                if chain != prev or (end - start + cdeltalen) > textlen * 2:
+                    # flush our writes here so we can read it in revision
+                    if dfh:
+                        dfh.flush()
+                    ifh.flush()
+                    text = self.revision(chain)
+                    if len(text) == 0:
+                        # skip over trivial delta header
+                        text = buffer(delta, 12)
+                    else:
+                        text = mdiff.patches(text, [delta])
+                    del delta
+                    chk = self._addrevision(text, transaction, link, p1, p2, None,
+                                            ifh, dfh)
+                    if not dfh and not self._inline:
+                        # addrevision switched from inline to conventional
+                        # reopen the index
                         dfh = self.opener(self.datafile, "a")
                         ifh = self.opener(self.indexfile, "a")
+                    if chk != node:
+                        raise RevlogError(_("consistency error adding group"))
+                    textlen = len(text)
                 else:
-                    dfh.write(cdelta[0])
-                    dfh.write(cdelta[1])
-                    ifh.write(entry)
+                    e = (offset_type(end, 0), cdeltalen, textlen, base,
+                         link, self.rev(p1), self.rev(p2), node)
+                    self.index.insert(-1, e)
+                    self.nodemap[node] = r
+                    entry = self._io.packentry(e, self.node, self.version, r)
+                    if self._inline:
+                        ifh.write(entry)
+                        ifh.write(cdelta[0])
+                        ifh.write(cdelta[1])
+                        self.checkinlinesize(transaction, ifh)
+                        if not self._inline:
+                            dfh = self.opener(self.datafile, "a")
+                            ifh = self.opener(self.indexfile, "a")
+                    else:
+                        dfh.write(cdelta[0])
+                        dfh.write(cdelta[1])
+                        ifh.write(entry)
 
-            t, r, chain, prev = r, r + 1, node, node
-            base = self.base(t)
-            start = self.start(base)
-            end = self.end(t)
+                t, r, chain, prev = r, r + 1, node, node
+                base = self.base(t)
+                start = self.start(base)
+                end = self.end(t)
+        finally:
+            if dfh:
+                dfh.close()
+            ifh.close()
 
         return node
 
--- a/mercurial/sshrepo.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/sshrepo.py	Fri Apr 04 23:13:32 2008 +0200
@@ -6,11 +6,20 @@
 # of the GNU General Public License, incorporated herein by reference.
 
 from node import bin, hex
-from remoterepo import remotelock, remoterepository
 from i18n import _
 import repo, os, re, util
 
-class sshrepository(remoterepository):
+class remotelock(object):
+    def __init__(self, repo):
+        self.repo = repo
+    def release(self):
+        self.repo.unlock()
+        self.repo = None
+    def __del__(self):
+        if self.repo:
+            self.release()
+
+class sshrepository(repo.repository):
     def __init__(self, ui, path, create=0):
         self._url = path
         self.ui = ui
--- a/mercurial/statichttprepo.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/statichttprepo.py	Fri Apr 04 23:13:32 2008 +0200
@@ -74,9 +74,6 @@
     def url(self):
         return 'static-' + self._url
 
-    def dev(self):
-        return -1
-
     def local(self):
         return False
 
--- a/mercurial/templatefilters.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/templatefilters.py	Fri Apr 04 23:13:32 2008 +0200
@@ -136,6 +136,7 @@
     "tabindent": lambda x: indent(x, '\t'),
     "hgdate": lambda x: "%d %d" % x,
     "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
+    "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
     "obfuscate": obfuscate,
     "permissions": permissions,
     "person": person,
--- a/mercurial/templater.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/templater.py	Fri Apr 04 23:13:32 2008 +0200
@@ -7,6 +7,7 @@
 
 from i18n import _
 import re, sys, os
+from mercurial import util
 
 def parsestring(s, quoted=True):
     '''parse a string using simple c-like syntax.
@@ -55,6 +56,9 @@
 
         if not mapfile:
             return
+        if not os.path.exists(mapfile):
+            raise util.Abort(_('style not found: %s') % mapfile)
+
         i = 0
         for l in file(mapfile):
             l = l.strip()
--- a/mercurial/ui.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/ui.py	Fri Apr 04 23:13:32 2008 +0200
@@ -60,6 +60,11 @@
                 self.ucdata = dupconfig(self.parentui.ucdata)
             if self.parentui.overlay:
                 self.overlay = dupconfig(self.parentui.overlay)
+            if self.parentui is not parentui and parentui.overlay is not None:
+                if self.overlay is None:
+                    self.overlay = util.configparser()
+                updateconfig(parentui.overlay, self.overlay)
+            self.buffers = parentui.buffers
 
     def __getattr__(self, key):
         return getattr(self.parentui, key)
@@ -346,6 +351,8 @@
                 pass
         if not user:
             raise util.Abort(_("Please specify a username."))
+        if "\n" in user:
+            raise util.Abort(_("username %s contains a newline\n") % `user`)
         return user
 
     def shortuser(self, user):
@@ -477,4 +484,3 @@
                 self.config("ui", "editor") or
                 os.environ.get("VISUAL") or
                 os.environ.get("EDITOR", "vi"))
-
--- a/mercurial/util.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/mercurial/util.py	Fri Apr 04 23:13:32 2008 +0200
@@ -1000,7 +1000,10 @@
         pass
 
     def set_binary(fd):
-        msvcrt.setmode(fd.fileno(), os.O_BINARY)
+        # When run without console, pipes may expose invalid
+        # fileno(), usually set to -1.
+        if hasattr(fd, 'fileno') and fd.fileno() >= 0:
+            msvcrt.setmode(fd.fileno(), os.O_BINARY)
 
     def pconvert(path):
         return '/'.join(splitpath(path))
@@ -1702,19 +1705,47 @@
     else:
         return "%s..." % (text[:maxlength-3])
 
-def walkrepos(path):
+def walkrepos(path, followsym=False, seen_dirs=None):
     '''yield every hg repository under path, recursively.'''
     def errhandler(err):
         if err.filename == path:
             raise err
+    if followsym and hasattr(os.path, 'samestat'):
+        def _add_dir_if_not_there(dirlst, dirname):
+            match = False
+            samestat = os.path.samestat
+            dirstat = os.stat(dirname)
+            for lstdirstat in dirlst:
+                if samestat(dirstat, lstdirstat):
+                    match = True
+                    break
+            if not match:
+                dirlst.append(dirstat)
+            return not match
+    else:
+        followsym = False
 
-    for root, dirs, files in os.walk(path, onerror=errhandler):
+    if (seen_dirs is None) and followsym:
+        seen_dirs = []
+        _add_dir_if_not_there(seen_dirs, path)
+    for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
         if '.hg' in dirs:
             dirs[:] = [] # don't descend further
             yield root # found a repository
             qroot = os.path.join(root, '.hg', 'patches')
-            if os.path.exists(os.path.join(qroot, '.hg')):
+            if os.path.isdir(os.path.join(qroot, '.hg')):
                 yield qroot # we have a patch queue repo here
+        elif followsym:
+            newdirs = []
+            for d in dirs:
+                fname = os.path.join(root, d)
+                if _add_dir_if_not_there(seen_dirs, fname):
+                    if os.path.islink(fname):
+                        for hgname in walkrepos(fname, True, seen_dirs):
+                            yield hgname
+                    else:
+                        newdirs.append(d)
+            dirs[:] = newdirs
 
 _rcpath = None
 
--- a/setup.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/setup.py	Fri Apr 04 23:13:32 2008 +0200
@@ -10,13 +10,48 @@
     raise SystemExit, "Mercurial requires python 2.3 or later."
 
 import os
+import shutil
+import tempfile
 from distutils.core import setup, Extension
 from distutils.command.install_data import install_data
+from distutils.ccompiler import new_compiler
 
 import mercurial.version
 
 extra = {}
 
+# simplified version of distutils.ccompiler.CCompiler.has_function
+# that actually removes its temporary files.
+def has_function(cc, funcname):
+    tmpdir = tempfile.mkdtemp(prefix='hg-install-')
+    devnull = oldstderr = None
+    try:
+        try:
+            fname = os.path.join(tmpdir, 'funcname.c')
+            f = open(fname, 'w')
+            f.write('int main(void) {\n')
+            f.write('    %s();\n' % funcname)
+            f.write('}\n')
+            f.close()
+            # Redirect stderr to /dev/null to hide any error messages
+            # from the compiler.
+            # This will have to be changed if we ever have to check
+            # for a function on Windows.
+            devnull = open('/dev/null', 'w')
+            oldstderr = os.dup(sys.stderr.fileno())
+            os.dup2(devnull.fileno(), sys.stderr.fileno())
+            objects = cc.compile([fname])
+            cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
+        except:
+            return False
+        return True
+    finally:
+        if oldstderr is not None:
+            os.dup2(oldstderr, sys.stderr.fileno())
+        if devnull is not None:
+            devnull.close()
+        shutil.rmtree(tmpdir)
+
 # py2exe needs to be installed to work
 try:
     import py2exe
@@ -66,10 +101,13 @@
     ext_modules.append(Extension('mercurial.osutil', ['mercurial/osutil.c']))
 
     if sys.platform == 'linux2' and os.uname()[2] > '2.6':
-        # the inotify extension is only usable with Linux 2.6 kernels
-        ext_modules.append(Extension('hgext.inotify.linux._inotify',
-                                     ['hgext/inotify/linux/_inotify.c']))
-        packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
+        # The inotify extension is only usable with Linux 2.6 kernels.
+        # You also need a reasonably recent C library.
+        cc = new_compiler()
+        if has_function(cc, 'inotify_add_watch'):
+            ext_modules.append(Extension('hgext.inotify.linux._inotify',
+                                         ['hgext/inotify/linux/_inotify.c']))
+            packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
 except ImportError:
     pass
 
--- a/templates/gitweb/changelogentry.tmpl	Fri Apr 04 23:09:54 2008 +0200
+++ b/templates/gitweb/changelogentry.tmpl	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,5 @@
 <div>
-<a class="title" href="{url}rev/#node|short#{sessionvars%urlparameter}"><span class="age">#date|age# ago</span>#desc|strip|firstline|escape#<span class="logtags"> {branches%branchtag}{tags%tagtag}</span></a>
+<a class="title" href="{url}rev/#node|short#{sessionvars%urlparameter}"><span class="age">#date|age# ago</span>#desc|strip|firstline|escape#<span class="logtags"> {inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a>
 </div>
 <div class="title_text">
 <div class="log_link">
--- a/templates/gitweb/changeset.tmpl	Fri Apr 04 23:09:54 2008 +0200
+++ b/templates/gitweb/changeset.tmpl	Fri Apr 04 23:13:32 2008 +0200
@@ -16,12 +16,13 @@
 </div>
 
 <div>
-<a class="title" href="{url}raw-rev/#node|short#">#desc|strip|escape|firstline# <span class="logtags">{branches%branchtag}{tags%tagtag}</span></a>
+<a class="title" href="{url}raw-rev/#node|short#">#desc|strip|escape|firstline# <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a>
 </div>
 <div class="title_text">
 <table cellspacing="0">
 <tr><td>author</td><td>#author|obfuscate#</td></tr>
 <tr><td></td><td>#date|date# (#date|age# ago)</td></tr>
+#branch%changesetbranch#
 <tr><td>changeset {rev}</td><td style="font-family:monospace">{node|short}</td></tr>
 #parent%changesetparent#
 #child%changesetchild#
--- a/templates/gitweb/fileannotate.tmpl	Fri Apr 04 23:09:54 2008 +0200
+++ b/templates/gitweb/fileannotate.tmpl	Fri Apr 04 23:13:32 2008 +0200
@@ -35,6 +35,7 @@
 <tr>
  <td></td>
  <td>#date|date# (#date|age# ago)</td></tr>
+#branch%filerevbranch#
 <tr>
  <td>changeset {rev}</td>
  <td style="font-family:monospace"><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>
--- a/templates/gitweb/filediff.tmpl	Fri Apr 04 23:09:54 2008 +0200
+++ b/templates/gitweb/filediff.tmpl	Fri Apr 04 23:13:32 2008 +0200
@@ -28,6 +28,7 @@
 <div class="title">{file|escape}</div>
 
 <table>
+{branch%filerevbranch}
 <tr>
  <td>changeset {rev}</td>
  <td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
--- a/templates/gitweb/filerevision.tmpl	Fri Apr 04 23:09:54 2008 +0200
+++ b/templates/gitweb/filerevision.tmpl	Fri Apr 04 23:13:32 2008 +0200
@@ -35,6 +35,7 @@
 <tr>
  <td></td>
  <td>#date|date# (#date|age# ago)</td></tr>
+#branch%filerevbranch#
 <tr>
  <td>changeset {rev}</td>
  <td style="font-family:monospace"><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>
--- a/templates/gitweb/manifest.tmpl	Fri Apr 04 23:09:54 2008 +0200
+++ b/templates/gitweb/manifest.tmpl	Fri Apr 04 23:13:32 2008 +0200
@@ -20,7 +20,7 @@
 <a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> #archives%archiveentry#<br/>
 </div>
 
-<div class="title">#path|escape# <span class="logtags">{branches%branchtag}{tags%tagtag}</span></div>
+<div class="title">#path|escape# <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></div>
 <table cellspacing="0">
 <tr class="parity#upparity#">
 <td style="font-family:monospace">drwxr-xr-x</td>
--- a/templates/gitweb/map	Fri Apr 04 23:09:54 2008 +0200
+++ b/templates/gitweb/map	Fri Apr 04 23:13:32 2008 +0200
@@ -30,7 +30,9 @@
 difflineat = '<span style="color:#990099;"><a class="linenr" href="##lineid#" id="#lineid#">#linenumber#</a> #line|escape#</span>'
 diffline = '<span><a class="linenr" href="##lineid#" id="#lineid#">#linenumber#</a> #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>'
+changesetbranch = '<tr><td>branch</td><td>{name}</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>'
+filerevbranch = '<tr><td>branch</td><td>{name}</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>'
 filerename = '{file|escape}@'
 filelogrename = '| <a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">base</a>'
@@ -50,7 +52,8 @@
 shortlog = shortlog.tmpl
 tagtag = '<span class="tagtag" title="{name}">{name}</span> '
 branchtag = '<span class="branchtag" title="{name}">{name}</span> '
-shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author|person#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b> <span class="logtags">{branches%branchtag}{tags%tagtag}</span></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a></td></tr>'
+inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
+shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author|person#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b> <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}</span></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">files</a></td></tr>'
 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a>&nbsp;|&nbsp;<a href="{url}diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<a href="{url}annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a> #rename%filelogrename#</td></tr>'
 archiveentry = ' | <a href="{url}archive/{node|short}{extension}">#type|escape#</a> '
 indexentry = '<tr class="parity{parity}"><td><a class="list" href="{url}{sessionvars%urlparameter}"><b>{name|escape}</b></a></td><td>{description}</td><td>{contact|obfuscate}</td><td class="age">{lastchange|age} ago</td><td class="indexlinks">{archives%indexarchiveentry}</td><td><div class="rss_logo"><a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a></div></td></tr>\n'
--- a/templates/static/style-gitweb.css	Fri Apr 04 23:09:54 2008 +0200
+++ b/templates/static/style-gitweb.css	Fri Apr 04 23:13:32 2008 +0200
@@ -75,3 +75,7 @@
 	background-color: #aaffaa;
 	border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
 }
+span.logtags span.inbranchtag {
+	background-color: #d5dde6;
+	border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
+}
--- a/tests/coverage.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/coverage.py	Fri Apr 04 23:13:32 2008 +0200
@@ -412,6 +412,9 @@
         else:
             omit = []
 
+        omit = [os.path.normcase(os.path.abspath(os.path.realpath(p)))
+                for p in omit]
+
         if settings.get('report'):
             self.report(args, show_missing, ignore_errors, omit_prefixes=omit)
         if settings.get('annotate'):
@@ -537,7 +540,7 @@
                     if os.path.exists(g):
                         f = g
                         break
-            cf = os.path.normcase(os.path.abspath(f))
+            cf = os.path.normcase(os.path.abspath(os.path.realpath(f)))
             self.canonical_filename_cache[filename] = cf
         return self.canonical_filename_cache[filename]
 
--- a/tests/hghave	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/hghave	Fri Apr 04 23:13:32 2008 +0200
@@ -31,7 +31,11 @@
     return matchoutput('cvsps -h -q 2>&1', r'cvsps version', True)
 
 def has_darcs():
-    return matchoutput('darcs', 'darcs version', True)
+    return matchoutput('darcs', r'darcs version', True)
+
+def has_mtn():
+    return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
+        'mtn --version', r'monotone 0\.(\d|[12]\d|3[01])[^\d]', True)
 
 def has_eol_in_paths():
     try:
@@ -107,6 +111,13 @@
     finally:
         os.rmdir(d)
 
+def has_pygments():
+    try:
+        import pygments
+        return True
+    except ImportError:
+        return False
+
 checks = {
     "baz": (has_baz, "GNU Arch baz client"),
     "cvs": (has_cvs, "cvs client"),
@@ -118,11 +129,13 @@
     "git": (has_git, "git command line client"),
     "hotshot": (has_hotshot, "python hotshot module"),
     "lsprof": (has_lsprof, "python lsprof module"),
+    "mtn": (has_mtn, "monotone client (> 0.31)"),
     "svn": (has_svn, "subversion client and admin tools"),
     "svn-bindings": (has_svn_bindings, "subversion python bindings"),
     "symlink": (has_symlink, "symbolic links"),
     "tla": (has_tla, "GNU Arch tla client"),
     "unix-permissions": (has_unix_permissions, "unix-style permissions"),
+    "pygments": (has_pygments, "Pygments source highlighting library"),
 }
 
 def list_features():
--- a/tests/run-tests.py	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/run-tests.py	Fri Apr 04 23:13:32 2008 +0200
@@ -24,6 +24,12 @@
 
 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
 
+defaults = {
+    'jobs': ('HGTEST_JOBS', 1),
+    'timeout': ('HGTEST_TIMEOUT', 180),
+    'port': ('HGTEST_PORT', 20059),
+}
+
 parser = optparse.OptionParser("%prog [options] [tests]")
 parser.add_option("-C", "--annotate", action="store_true",
     help="output files annotated with coverage")
@@ -36,19 +42,23 @@
 parser.add_option("-i", "--interactive", action="store_true",
     help="prompt to accept changed output")
 parser.add_option("-j", "--jobs", type="int",
-    help="number of jobs to run in parallel")
+    help="number of jobs to run in parallel"
+         " (default: $%s or %d)" % defaults['jobs'])
 parser.add_option("--keep-tmpdir", action="store_true",
-    help="keep temporary directory after running tests (best used with --tmpdir)")
+    help="keep temporary directory after running tests"
+         " (best used with --tmpdir)")
 parser.add_option("-R", "--restart", action="store_true",
     help="restart at last error")
 parser.add_option("-p", "--port", type="int",
-    help="port on which servers should listen")
+    help="port on which servers should listen"
+         " (default: $%s or %d)" % defaults['port'])
 parser.add_option("-r", "--retest", action="store_true",
     help="retest failed tests")
 parser.add_option("-s", "--cover_stdlib", action="store_true",
     help="print a test coverage report inc. standard libraries")
 parser.add_option("-t", "--timeout", type="int",
-    help="kill errant tests after TIMEOUT seconds")
+    help="kill errant tests after TIMEOUT seconds"
+         " (default: $%s or %d)" % defaults['timeout'])
 parser.add_option("--tmpdir", type="string",
     help="run tests in the given temporary directory")
 parser.add_option("-v", "--verbose", action="store_true",
@@ -56,7 +66,9 @@
 parser.add_option("--with-hg", type="string",
     help="test existing install at given location")
 
-parser.set_defaults(jobs=1, port=20059, timeout=180)
+for option, default in defaults.items():
+    defaults[option] = os.environ.get(*default)
+parser.set_defaults(**defaults)
 (options, args) = parser.parse_args()
 verbose = options.verbose
 coverage = options.cover or options.cover_stdlib or options.annotate
@@ -267,7 +279,7 @@
                        % options.timeout)
     return ret, splitnewlines(output)
 
-def run_one(test, skips):
+def run_one(test, skips, fails):
     '''tristate output:
     None -> skipped
     True -> passed
@@ -280,6 +292,11 @@
             print "\nSkipping %s: %s" % (test, msg)
         return None
 
+    def fail(msg):
+        fails.append((test, msg))
+        print "\nERROR: %s %s" % (test, msg)
+        return None
+
     vlog("# Test", test)
 
     # create a fresh hgrc
@@ -342,7 +359,6 @@
         signal.alarm(0)
 
     skipped = (ret == SKIPPED_STATUS)
-    diffret = 0
     # If reference output file exists, check test output against it
     if os.path.exists(ref):
         f = open(ref, "r")
@@ -350,19 +366,20 @@
         f.close()
     else:
         ref_out = []
-    if not skipped and out != ref_out:
-        diffret = 1
-        print "\nERROR: %s output changed" % (test)
-        show_diff(ref_out, out)
     if skipped:
         missing = extract_missing_features(out)
         if not missing:
             missing = ['irrelevant']
         skip(missing[-1])
+    elif out != ref_out:
+        if ret:
+            fail("output changed and returned error code %d" % ret)
+        else:
+            fail("output changed")
+        show_diff(ref_out, out)
+        ret = 1
     elif ret:
-        print "\nERROR: %s failed with error code %d" % (test, ret)
-    elif diffret:
-        ret = diffret
+        fail("returned error code %d" % ret)
 
     if not verbose:
         sys.stdout.write(skipped and 's' or '.')
@@ -474,13 +491,17 @@
     failures = 0
     tested, skipped, failed = 0, 0, 0
     skips = []
+    fails = []
     while fps:
         pid, status = os.wait()
         fp = fps.pop(pid)
         l = fp.read().splitlines()
         test, skip, fail = map(int, l[:3])
-        for s in l[3:]:
+        split = -fail or len(l)
+        for s in l[3:split]:
             skips.append(s.split(" ", 1))
+        for s in l[split:]:
+            fails.append(s.split(" ", 1))
         tested += test
         skipped += skip
         failed += fail
@@ -489,6 +510,8 @@
     print
     for s in skips:
         print "Skipped %s: %s" % (s[0], s[1])
+    for s in fails:
+        print "Failed %s: %s" % (s[0], s[1])
     print "# Ran %d tests, %d skipped, %d failed." % (
         tested, skipped, failed)
     sys.exit(failures != 0)
@@ -526,11 +549,12 @@
                 tests = orig
 
         skips = []
+        fails = []
         for test in tests:
             if options.retest and not os.path.exists(test + ".err"):
                 skipped += 1
                 continue
-            ret = run_one(test, skips)
+            ret = run_one(test, skips, fails)
             if ret is None:
                 skipped += 1
             elif not ret:
@@ -540,6 +564,7 @@
                     if answer.lower() in "y yes".split():
                         rename(test + ".err", test + ".out")
                         tested += 1
+                        fails.pop()
                         continue
                 failed += 1
                 if options.first:
@@ -551,11 +576,15 @@
             fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
             for s in skips:
                 fp.write("%s %s\n" % s)
+            for s in fails:
+                fp.write("%s %s\n" % s)
             fp.close()
         else:
             print
             for s in skips:
                 print "Skipped %s: %s" % s
+            for s in fails:
+                print "Failed %s: %s" % s
             print "# Ran %d tests, %d skipped, %d failed." % (
                 tested, skipped, failed)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/svn-safe-append.py	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+__doc__ = """Same as `echo a >> b`, but ensures a changed mtime of b.
+Without this svn will not detect workspace changes."""
+
+import sys, os
+
+text = sys.argv[1]
+fname = sys.argv[2]
+
+f = open(fname, "ab")
+try:
+    before = os.fstat(f.fileno()).st_mtime
+    f.write(text)
+    f.write("\n")
+finally:
+    f.close()
+inc = 1
+now = os.stat(fname).st_mtime
+while now == before:
+    t = now + inc
+    inc += 1
+    os.utime(fname, (t, t))
+    now = os.stat(fname).st_mtime
+
--- a/tests/test-acl.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-acl.out	Fri Apr 04 23:13:32 2008 +0200
@@ -4,6 +4,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 3 changes to 3 files
+updating working directory
 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Extension disabled for lack of a hook
--- a/tests/test-add.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-add.out	Fri Apr 04 23:13:32 2008 +0200
@@ -13,6 +13,7 @@
 % should fail
 a already tracked!
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 merging a
 warning: conflicts during merge.
 merging a failed!
--- a/tests/test-annotate.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-annotate.out	Fri Apr 04 23:13:32 2008 +0200
@@ -46,6 +46,7 @@
 3 b:5: b5
 3 b:6: b6
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 merging b
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
@@ -64,6 +65,7 @@
 4 b:5: c
 3 b:5: b5
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 merging b
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
--- a/tests/test-backout.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-backout.out	Fri Apr 04 23:13:32 2008 +0200
@@ -21,10 +21,12 @@
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 abort: cannot back out change on a different branch
 adding c
+created new head
 abort: cannot back out change on a different branch
 # backout with merge
 adding a
 reverting a
+created new head
 changeset 3:26b8ccb9ad91 backs out changeset 1:5a50a024c182
 merging with changeset 3:26b8ccb9ad91
 merging a
@@ -37,6 +39,7 @@
 adding a
 adding b
 reverting a
+created new head
 changeset 3:3202beb76721 backs out changeset 1:22bca4c721e5
 the backout changeset is a new head - do not forget to merge
 (use "backout --merge" if you want to auto-merge)
@@ -47,6 +50,7 @@
 adding c
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 adding d
+created new head
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 # backout of merge should fail
@@ -69,7 +73,8 @@
 marked working directory as branch branch2
 adding file2
 removing file1
-changeset 3:f1c642b1d8e5 backs out changeset 1:bf1602f437f3
+created new head
+changeset 3:d4e8f6db59fb backs out changeset 1:bf1602f437f3
 the backout changeset is a new head - do not forget to merge
 (use "backout --merge" if you want to auto-merge)
 % on branch2 with branch1 not merged, so file1 should still exist:
@@ -80,10 +85,11 @@
 % on branch2 with branch1 merged, so file1 should be gone:
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
-21d4dc6f9a41 (branch2) tip
+22149cdde76d (branch2) tip
 C default
 C file2
 % on branch1, so no file1 and file2:
-0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-f1c642b1d8e5 (branch1)
+1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+bf1602f437f3 (branch1)
 C default
+C file1
--- a/tests/test-bheads.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-bheads.out	Fri Apr 04 23:13:32 2008 +0200
@@ -9,6 +9,7 @@
 =======
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 marked working directory as branch b
+created new head
 2: Adding b branch
 1: Adding a branch
 -------
@@ -20,6 +21,7 @@
 3: Adding b branch head 1
 =======
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 4: Adding b branch head 2
 3: Adding b branch head 1
 1: Adding a branch
@@ -28,6 +30,7 @@
 3: Adding b branch head 1
 =======
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 5: Adding b branch head 3
 4: Adding b branch head 2
 3: Adding b branch head 1
--- a/tests/test-branches.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-branches.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,7 +1,9 @@
 marked working directory as branch a
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 marked working directory as branch b
+created new head
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 marked working directory as branch c
 c                              5:5ca481e59b8c
 a                              1:dd6b440dd85a
--- a/tests/test-bundle	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-bundle	Fri Apr 04 23:13:32 2008 +0200
@@ -97,6 +97,11 @@
 hg -R bundle://../does-not-exist.hg outgoing ../partial2
 cd ..
 
+echo "====== Direct clone from bundle (all-history)"
+hg clone full.hg full-clone
+hg -R full-clone heads
+rm -r full-clone
+
 # test for http://www.selenic.com/mercurial/bts/issue216
 echo "====== Unbundle incremental bundles into fresh empty in one go"
 rm -r empty
--- a/tests/test-bundle-r.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-bundle-r.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
    rev    offset  length   base linkrev nodeid       p1           p2
      0         0       3      0       0 362fef284ce2 000000000000 000000000000
@@ -162,6 +163,7 @@
 6 changesets found
 1 changesets found
 1 changesets found
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % 2
 2:d62976ca1e50
@@ -217,6 +219,7 @@
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 7 changesets found
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding changesets
 adding manifests
--- a/tests/test-bundle.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-bundle.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,6 @@
 ====== Setting up test
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -145,7 +146,9 @@
 adding manifests
 adding file changes
 added 4 changesets with 4 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 ====== Log -R full.hg in partial
 changeset:   8:836ac62537ab
@@ -260,6 +263,26 @@
 
 ====== Outgoing -R does-not-exist.hg vs partial2 in partial
 abort: No such file or directory: ../does-not-exist.hg
+====== Direct clone from bundle (all-history)
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 9 changesets with 7 changes to 4 files (+1 heads)
+updating working directory
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+changeset:   8:836ac62537ab
+tag:         tip
+parent:      3:ac69c658229d
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     0.3m
+
+changeset:   7:80fe151401c2
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     1.3m
+
 ====== Unbundle incremental bundles into fresh empty in one go
 1 changesets found
 1 changesets found
@@ -273,6 +296,7 @@
 added 1 changesets with 1 changes to 1 files
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 ====== test for 540d1059c802
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 searching for changes
 1 changesets found
--- a/tests/test-cat	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-cat	Fri Apr 04 23:13:32 2008 +0200
@@ -9,7 +9,6 @@
 hg rm a
 hg cat a
 hg cat --decode a # more tests in test-encode
-sleep 1 # make sure mtime is changed
 echo 1 > b
 hg ci -m m -d "1000000 0"
 echo 2 > b
--- a/tests/test-changelog-exec.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-changelog-exec.out	Fri Apr 04 23:13:32 2008 +0200
@@ -2,6 +2,7 @@
 bar
 foo
 
+created new head
 % manifest of p1:
 foo
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-churn	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+echo "[extensions]" >> $HGRCPATH
+echo "churn=" >> $HGRCPATH
+
+echo % create test repository
+hg init repo
+cd repo
+echo a > a
+hg ci -Am adda -u user1
+echo b >> a
+echo b > b
+hg ci -Am addb -u user2
+echo c >> a
+echo c >> b
+echo c > c
+hg ci -Am addc -u user3
+
+echo % churn all
+hg churn
+echo % churn up to rev 1
+hg churn -r :1
+echo % churn with aliases
+cat > ../aliases <<EOF
+user1 alias1
+user3 alias3
+EOF
+hg churn --aliases ../aliases
+echo % churn with column specifier
+COLUMNS=40 hg churn
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-churn.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,19 @@
+% create test repository
+adding a
+adding b
+adding c
+% churn all
+user3      3 ***************************************************************
+user2      2 ******************************************
+user1      1 *********************
+% churn up to rev 1
+user2      2 ***************************************************************
+user1      1 *******************************
+% churn with aliases
+alias3      3 **************************************************************
+user2       2 *****************************************
+alias1      1 ********************
+% churn with column specifier
+user3      3 ***********************
+user2      2 ***************
+user1      1 *******
--- a/tests/test-clone-pull-corruption.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-clone-pull-corruption.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pulling from ../source
 transaction abort!
--- a/tests/test-clone-r.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-clone-r.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
    rev    offset  length   base linkrev nodeid       p1           p2
      0         0       3      0       0 362fef284ce2 000000000000 000000000000
@@ -29,6 +30,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -40,6 +42,7 @@
 adding manifests
 adding file changes
 added 2 changesets with 2 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -51,6 +54,7 @@
 adding manifests
 adding file changes
 added 3 changesets with 3 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -62,6 +66,7 @@
 adding manifests
 adding file changes
 added 4 changesets with 4 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -73,6 +78,7 @@
 adding manifests
 adding file changes
 added 2 changesets with 2 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -84,6 +90,7 @@
 adding manifests
 adding file changes
 added 3 changesets with 3 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -95,6 +102,7 @@
 adding manifests
 adding file changes
 added 4 changesets with 5 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -106,6 +114,7 @@
 adding manifests
 adding file changes
 added 5 changesets with 6 changes to 3 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -117,6 +126,7 @@
 adding manifests
 adding file changes
 added 5 changesets with 5 changes to 2 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
--- a/tests/test-clone.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-clone.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 a
 checking changesets
@@ -12,7 +13,9 @@
 checking files
 1 files, 1 changesets, 1 total revisions
 destination directory: a
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 a
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 a#0
--- a/tests/test-command-template	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-command-template	Fri Apr 04 23:13:32 2008 +0200
@@ -103,6 +103,7 @@
 hg log --template '{date|age}\n' > /dev/null || exit 1
 hg log --template '{date|date}\n'
 hg log --template '{date|isodate}\n'
+hg log --template '{date|isodatesec}\n'
 hg log --template '{date|rfc822date}\n'
 hg log --template '{desc|firstline}\n'
 hg log --template '{node|short}\n'
--- a/tests/test-command-template.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-command-template.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,6 @@
+created new head
 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+created new head
 # default style is like normal output
 #  normal
 #  verbose
@@ -81,7 +83,7 @@
 # error if style not readable
 abort: Permission denied: ./q
 # error if no style
-abort: No such file or directory: notexist
+abort: style not found: notexist
 # error if style missing key
 abort: ./t: no key named 'changeset'
 # error if include fails
@@ -517,6 +519,14 @@
 1970-01-14 21:20 +0000
 1970-01-13 17:33 +0000
 1970-01-12 13:46 +0000
+1970-01-12 13:46:40 +0000
+1970-01-18 08:40:01 +0000
+1970-01-18 08:40:00 +0000
+1970-01-17 04:53:20 +0000
+1970-01-16 01:06:40 +0000
+1970-01-14 21:20:00 +0000
+1970-01-13 17:33:20 +0000
+1970-01-12 13:46:40 +0000
 Mon, 12 Jan 1970 13:46:40 +0000
 Sun, 18 Jan 1970 08:40:01 +0000
 Sun, 18 Jan 1970 08:40:00 +0000
--- a/tests/test-commit	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-commit	Fri Apr 04 23:13:32 2008 +0200
@@ -85,4 +85,22 @@
 cd ..
 cd ..
 
+cd ..
+hg init issue1049
+cd issue1049
+echo a > a
+hg ci -Ama
+echo a >> a
+hg ci -mb
+hg up 0
+echo b >> a
+hg ci -mc
+HGMERGE=true hg merge
+echo % should fail because we are specifying a file name
+hg ci -mmerge a
+echo % should fail because we are specifying a pattern
+hg ci -mmerge -I a
+echo % should succeed
+hg ci -mmerge
+
 exit 0
--- a/tests/test-commit.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-commit.out	Fri Apr 04 23:13:32 2008 +0200
@@ -92,3 +92,14 @@
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     commit-foo-subdir
 
+adding a
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
+merging a
+0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+% should fail because we are specifying a file name
+abort: cannot partially commit a merge (do not specify files or patterns)
+% should fail because we are specifying a pattern
+abort: cannot partially commit a merge (do not specify files or patterns)
+% should succeed
--- a/tests/test-conflict.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-conflict.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 merging a
 warning: conflicts during merge.
 merging a failed!
--- a/tests/test-confused-revert.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-confused-revert.out	Fri Apr 04 23:13:32 2008 +0200
@@ -8,6 +8,7 @@
 ? b
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 merging a
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
@@ -22,6 +23,7 @@
 undeleting a
 forgetting b
 %%% should show b unknown and a marked modified (merged)
+M a
 ? b
 %%% should show foo-b
 foo-b
--- a/tests/test-convert-cvs	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-cvs	Fri Apr 04 23:13:32 2008 +0200
@@ -37,6 +37,7 @@
 
 echo % commit a new revision changing b/c
 cd src
+sleep 1
 echo c >> b/c
 cvscall -q commit -mci0 . | grep '<--' |\
     sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
--- a/tests/test-convert-datesort.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-datesort.out	Fri Apr 04 23:13:32 2008 +0200
@@ -3,6 +3,7 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 marked working directory as branch branchb
 adding b
+created new head
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % convert with datesort
--- a/tests/test-convert-filemap.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-filemap.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,5 @@
+created new head
+created new head
 @  8 "8: change foo" files: foo
 |
 o    7 "7: second merge; change bar" files: bar baz
--- a/tests/test-convert-git.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-git.out	Fri Apr 04 23:13:32 2008 +0200
@@ -10,10 +10,10 @@
 2 t4.1
 1 t4.2
 0 Merge branch other
-changeset:   5:c6d72c98aa00
+changeset:   5:4ab1af49a271
 tag:         tip
-parent:      3:a18bdfccf429
-parent:      4:48cb5b72ce56
+parent:      3:0222ab0998d7
+parent:      4:5333c870e3c2
 user:        test <test@example.org>
 date:        Mon Jan 01 00:00:15 2007 +0000
 files:       a
--- a/tests/test-convert-hg-source.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-hg-source.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+created new head
 merging baz and foo
 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
@@ -5,6 +6,7 @@
 merging foo and baz
 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
+created new head
 initializing destination new repository
 scanning source...
 sorting...
--- a/tests/test-convert-hg-svn.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-hg-svn.out	Fri Apr 04 23:13:32 2008 +0200
@@ -19,6 +19,7 @@
 sorting...
 converting...
 % new hg rev
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % echo hg to svn
 scanning source...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-mtn	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" mtn || exit 80
+
+# Monotone directory is called .monotone on *nix and monotone
+# on Windows. Having a variable here ease test patching.
+mtndir=.monotone
+echo "[extensions]" >> $HGRCPATH
+echo "convert=" >> $HGRCPATH
+echo 'hgext.graphlog =' >> $HGRCPATH
+
+HOME=`pwd`/do_not_use_HOME_mtn; export HOME
+# Windows version of monotone home
+APPDATA=$HOME; export APPDATA
+
+echo % tedious monotone keys configuration
+# The /dev/null redirection is necessary under Windows, or
+# it complains about home directory permissions
+mtn --quiet genkey test@selenic.com 1>/dev/null 2>&1 <<EOF
+passphrase
+passphrase
+EOF
+cat >> $HOME/$mtndir/monotonerc <<EOF
+function get_passphrase(keypair_id)
+    return "passphrase"
+end
+EOF
+
+echo % create monotone repository
+mtn db init --db=repo.mtn
+mtn --db=repo.mtn --branch=com.selenic.test setup workingdir
+cd workingdir
+echo a > a
+mkdir dir
+echo b > dir/b
+python -c 'file("bin", "wb").write("a\\x00b")'
+echo c > c
+mtn add a dir/b c bin
+mtn ci -m initialize
+echo % update monotone working directory
+mtn mv a dir/a
+echo a >> dir/a
+echo b >> dir/b
+mtn drop c
+python -c 'file("bin", "wb").write("b\\x00c")'
+mtn ci -m update1
+cd ..
+
+echo % convert once
+hg convert -s mtn repo.mtn
+
+cd workingdir
+echo e > e
+mtn add e
+mtn drop dir/b
+mtn mv bin bin2
+mtn ci -m update2
+# Test directory move
+mtn mv dir dir2
+mtn ci -m movedir
+# Test directory removal with empty directory
+mkdir dir2/dir
+mkdir dir2/dir/subdir
+echo f > dir2/dir/subdir/f
+mkdir dir2/dir/emptydir
+mtn add -R dir2/dir
+mtn ci -m emptydir
+mtn drop -R dir2/dir
+mtn ci -m dropdirectory
+cd ..
+
+echo % convert incrementally
+hg convert -s mtn repo.mtn
+
+glog()
+{
+    hg glog --template '#rev# "#desc|firstline#" files: #files#\n' "$@"
+}
+
+cd repo.mtn-hg
+hg up -C
+glog
+echo % manifest
+hg manifest
+echo % contents
+cat dir2/a
+test -d dir2/dir && echo 'removed dir2/dir is still there!'
+exit 0
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-mtn.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,72 @@
+% tedious monotone keys configuration
+% create monotone repository
+mtn: adding a to workspace manifest
+mtn: adding bin to workspace manifest
+mtn: adding c to workspace manifest
+mtn: adding dir to workspace manifest
+mtn: adding dir/b to workspace manifest
+mtn: beginning commit on branch 'com.selenic.test'
+mtn: committed revision 803ef0bf815e35b951dbd4310acd1e45e675016e
+% update monotone working directory
+mtn: skipping dir, already accounted for in workspace
+mtn: renaming a to dir/a in workspace manifest
+mtn: dropping c from workspace manifest
+mtn: beginning commit on branch 'com.selenic.test'
+mtn: committed revision 4daf60753d6fe21a06ce5f716303fe55fd6d3a56
+% convert once
+assuming destination repo.mtn-hg
+initializing destination repo.mtn-hg repository
+scanning source...
+sorting...
+converting...
+1 initialize
+0 update1
+mtn: adding e to workspace manifest
+mtn: dropping dir/b from workspace manifest
+mtn: renaming bin to bin2 in workspace manifest
+mtn: beginning commit on branch 'com.selenic.test'
+mtn: committed revision 6c6977a6ef609ec80e40779f89dbd2772c96de62
+mtn: renaming dir to dir2 in workspace manifest
+mtn: beginning commit on branch 'com.selenic.test'
+mtn: committed revision 5de5abe7c15eae70cf3acdda23c9c319ea50c1af
+mtn: adding dir2/dir to workspace manifest
+mtn: adding dir2/dir/emptydir to workspace manifest
+mtn: adding dir2/dir/subdir to workspace manifest
+mtn: adding dir2/dir/subdir/f to workspace manifest
+mtn: beginning commit on branch 'com.selenic.test'
+mtn: committed revision 27a423be1e406595cc57f50f42a8790fa0a93d8e
+mtn: dropping dir2/dir/subdir/f from workspace manifest
+mtn: dropping dir2/dir/subdir from workspace manifest
+mtn: dropping dir2/dir/emptydir from workspace manifest
+mtn: dropping dir2/dir from workspace manifest
+mtn: beginning commit on branch 'com.selenic.test'
+mtn: committed revision ba57ba5ac63178529d37fa8a2a1a012fc0e42047
+% convert incrementally
+assuming destination repo.mtn-hg
+scanning source...
+sorting...
+converting...
+3 update2
+2 movedir
+1 emptydir
+0 dropdirectory
+3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+@  5 "dropdirectory" files: dir2/dir/subdir/f
+|
+o  4 "emptydir" files: dir2/dir/subdir/f
+|
+o  3 "movedir" files: dir/a dir2/a
+|
+o  2 "update2" files: bin bin2 dir/b e
+|
+o  1 "update1" files: a bin c dir/a dir/b
+|
+o  0 "initialize" files: a bin c dir/b
+
+% manifest
+bin2
+dir2/a
+e
+% contents
+a
+a
--- a/tests/test-convert-svn-branches	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-svn-branches	Fri Apr 04 23:13:32 2008 +0200
@@ -48,25 +48,25 @@
 svn up
 
 echo % update trunk
-echo "what can I say ?" >> trunk/letter.txt
+"$TESTDIR/svn-safe-append.py" "what can I say ?" trunk/letter.txt
 svn ci -m "change letter"
 
 echo % update old branch
-echo "what's up ?" >> branches/old/letter2.txt
+"$TESTDIR/svn-safe-append.py" "what's up ?" branches/old/letter2.txt
 svn ci -m "change letter2"
 
 echo % create a cross-branch revision
 svn move -m "move letter2" trunk/letter2.txt \
     branches/old/letter3.txt
-echo "I am fine" >> branches/old/letter3.txt
+"$TESTDIR/svn-safe-append.py" "I am fine" branches/old/letter3.txt
 svn ci -m "move and update letter3.txt"
 
 echo % update old branch again
-echo "bye" >> branches/old/letter2.txt
+"$TESTDIR/svn-safe-append.py" "bye" branches/old/letter2.txt
 svn ci -m "change letter2 again"
 
 echo % update trunk again
-echo "how are you ?" >> trunk/letter.txt
+"$TESTDIR/svn-safe-append.py" "how are you ?" trunk/letter.txt
 svn ci -m "last change to letter"
 cd ..
 
--- a/tests/test-convert-svn-move	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-svn-move	Fri Apr 04 23:13:32 2008 +0200
@@ -38,8 +38,8 @@
 echo % update svn repository
 svn co $svnurl A | fix_path
 cd A
-echo a >> trunk/a
-echo c >> trunk/d1/c
+"$TESTDIR/svn-safe-append.py" a trunk/a
+"$TESTDIR/svn-safe-append.py" c trunk/d1/c
 svn ci -m commitbeforemove
 svn mv $svnurl/trunk $svnurl/subproject -m movedtrunk
 svn up
@@ -51,7 +51,7 @@
 svn ci -m createbranches
 svn mv $svnurl/subproject/d1 $svnurl/subproject/trunk/d1 -m moved1
 svn up
-echo b >> subproject/trunk/d1/b
+"$TESTDIR/svn-safe-append.py" b subproject/trunk/d1/b
 svn ci -m changeb
 svn mv $svnurl/subproject/trunk/d1 $svnurl/subproject/branches/d1 -m moved1again
 cd ..
--- a/tests/test-convert-svn-sink	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-svn-sink	Fri Apr 04 23:13:32 2008 +0200
@@ -32,7 +32,7 @@
 echo % add
 hg --cwd a ci -d '0 0' -A -m 'add a file'
 
-echo a >> a/a
+"$TESTDIR/svn-safe-append.py" a a/a
 echo % modify
 hg --cwd a ci -d '1 0' -m 'modify a file'
 hg --cwd a tip -q
@@ -107,21 +107,21 @@
 echo base > b/b
 hg --cwd b ci -d '0 0' -Ambase
 
-echo left-1 >> b/b
+"$TESTDIR/svn-safe-append.py" left-1 b/b
 echo left-1 > b/left-1
 hg --cwd b ci -d '1 0' -Amleft-1
 
-echo left-2 >> b/b
+"$TESTDIR/svn-safe-append.py" left-2 b/b
 echo left-2 > b/left-2
 hg --cwd b ci -d '2 0' -Amleft-2
 
 hg --cwd b up 0
 
-echo right-1 >> b/b
+"$TESTDIR/svn-safe-append.py" right-1 b/b
 echo right-1 > b/right-1
 hg --cwd b ci -d '3 0' -Amright-1
 
-echo right-2 >> b/b
+"$TESTDIR/svn-safe-append.py" right-2 b/b
 echo right-2 > b/right-2
 hg --cwd b ci -d '4 0' -Amright-2
 
--- a/tests/test-convert-svn-sink.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-svn-sink.out	Fri Apr 04 23:13:32 2008 +0200
@@ -258,6 +258,7 @@
 adding left-2
 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
 adding right-1
+created new head
 adding right-2
 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
 merging b
--- a/tests/test-convert-svn-source	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-svn-source	Fri Apr 04 23:13:32 2008 +0200
@@ -9,15 +9,10 @@
 
 echo "[extensions]" >> $HGRCPATH
 echo "convert = " >> $HGRCPATH
+echo 'hgext.graphlog =' >> $HGRCPATH
 
 svnadmin create svn-repo
 
-echo % initial svn import
-mkdir t
-cd t
-echo a > a
-cd ..
-
 svnpath=`pwd | fix_path`
 # SVN wants all paths to start with a slash. Unfortunately,
 # Windows ones don't. Handle that.
@@ -26,106 +21,6 @@
     svnpath='/'$svnpath
 fi
 
-svnurl=file://$svnpath/svn-repo/trunk/test
-svn import -m init t $svnurl | fix_path
-
-echo % update svn repository
-svn co $svnurl t2 | fix_path
-cd t2
-echo b >> a
-echo b > b
-svn add b
-svn ci -m changea
-cd ..
-
-echo % convert to hg once
-hg convert $svnurl
-
-echo % update svn repository again
-cd t2
-echo c >> a
-echo c >> b
-svn ci -m changeb
-cd ..
-
-echo % test incremental conversion
-hg convert -v $svnurl | sed 's/source:.*/source:/'
-
-echo % test filemap
-echo 'include b' > filemap
-hg convert --filemap filemap $svnurl fmap
-echo '[extensions]' >> $HGRCPATH
-echo 'hgext.graphlog =' >> $HGRCPATH
-hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n'
-
-echo % test stop revision
-hg convert --rev 1 $svnurl stoprev
-# Check convert_revision extra-records.
-# This is also the only place testing more than one extra field
-# in a revision.
-hg --cwd stoprev tip --debug | grep extra | sed 's/=.*/=/'
-
-########################################
-
-echo "# now tests that it works with trunk/branches/tags layout"
-echo
-echo % initial svn import
-mkdir projA
-cd projA
-mkdir trunk
-mkdir branches
-mkdir tags
-cd ..
-
-svnurl=file://$svnpath/svn-repo/projA
-svn import -m "init projA" projA $svnurl | fix_path
-
-
-echo % update svn repository
-svn co $svnurl/trunk A | fix_path
-cd A
-echo hello > letter.txt
-svn add letter.txt
-svn ci -m hello
-
-echo world >> letter.txt
-svn ci -m world
-
-svn copy -m "tag v0.1" $svnurl/trunk $svnurl/tags/v0.1
-
-echo 'nice day today!' >> letter.txt
-svn ci -m "nice day"
-cd ..
-
-echo % convert to hg once
-hg convert $svnurl A-hg
-
-echo % update svn repository again
-cd A
-echo "see second letter" >> letter.txt
-# Put it in a subdirectory to test duplicate file records
-# from svn source (issue 714)
-mkdir todo
-echo "nice to meet you" > todo/letter2.txt
-svn add todo
-svn ci -m "second letter"
-
-svn copy -m "tag v0.2" $svnurl/trunk $svnurl/tags/v0.2
-
-echo "blah-blah-blah" >> todo/letter2.txt
-svn ci -m "work in progress"
-cd ..
-
-echo % test incremental conversion
-hg convert $svnurl A-hg
-
-cd A-hg
-hg glog --template '#rev# #desc|firstline# files: #files#\n'
-hg tags -q
-cd ..
-
-########################################
-
 echo "# now tests that it works with trunk/tags layout, but no branches yet"
 echo
 echo % initial svn import
@@ -146,12 +41,12 @@
 svn add letter.txt
 svn ci -m hello
 
-echo world >> letter.txt
+"$TESTDIR/svn-safe-append.py" world letter.txt
 svn ci -m world
 
 svn copy -m "tag v0.1" $svnurl/trunk $svnurl/tags/v0.1
 
-echo 'nice day today!' >> letter.txt
+"$TESTDIR/svn-safe-append.py" 'nice day today!' letter.txt
 svn ci -m "nice day"
 cd ..
 
@@ -160,17 +55,19 @@
 
 echo % update svn repository again
 cd B
-echo "see second letter" >> letter.txt
+"$TESTDIR/svn-safe-append.py" "see second letter" letter.txt
 echo "nice to meet you" > letter2.txt
 svn add letter2.txt
 svn ci -m "second letter"
 
 svn copy -m "tag v0.2" $svnurl/trunk $svnurl/tags/v0.2
 
-echo "blah-blah-blah" >> letter2.txt
+"$TESTDIR/svn-safe-append.py" "blah-blah-blah" letter2.txt
 svn ci -m "work in progress"
 cd ..
 
+########################################
+
 echo % test incremental conversion
 hg convert $svnurl B-hg
 
@@ -178,3 +75,15 @@
 hg glog --template '#rev# #desc|firstline# files: #files#\n'
 hg tags -q
 cd ..
+
+echo % test filemap
+echo 'include letter2.txt' > filemap
+hg convert --filemap filemap $svnurl/trunk fmap
+hg glog -R fmap --template '#rev# #desc|firstline# files: #files#\n'
+
+echo % test stop revision
+hg convert --rev 1 $svnurl/trunk stoprev
+# Check convert_revision extra-records.
+# This is also the only place testing more than one extra field
+# in a revision.
+hg --cwd stoprev tip --debug | grep extra | sed 's/=.*/=/'
--- a/tests/test-convert-svn-source.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-svn-source.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,151 +1,24 @@
-% initial svn import
-Adding         t/a
-
-Committed revision 1.
-% update svn repository
-A    t2/a
-Checked out revision 1.
-A         b
-Sending        a
-Adding         b
-Transmitting file data ..
-Committed revision 2.
-% convert to hg once
-assuming destination test-hg
-initializing destination test-hg repository
-scanning source...
-sorting...
-converting...
-1 init
-0 changea
-% update svn repository again
-Sending        a
-Sending        b
-Transmitting file data ..
-Committed revision 3.
-% test incremental conversion
-assuming destination test-hg
-scanning source...
-fetching revision log for "/trunk/test" from 3 to 2
-sorting...
-converting...
-0 changeb
-source:
-a
-b
-no tags found at revision 3
-% test filemap
-initializing destination fmap repository
-scanning source...
-sorting...
-converting...
-2 init
-1 changea
-0 changeb
-o  1 changeb files: b
-|
-o  0 changea files: b
-
-% test stop revision
-initializing destination stoprev repository
-scanning source...
-sorting...
-converting...
-0 init
-extra:       branch=
-extra:       convert_revision=
-# now tests that it works with trunk/branches/tags layout
-
-% initial svn import
-Adding         projA/trunk
-Adding         projA/branches
-Adding         projA/tags
-
-Committed revision 4.
-% update svn repository
-Checked out revision 4.
-A         letter.txt
-Adding         letter.txt
-Transmitting file data .
-Committed revision 5.
-Sending        letter.txt
-Transmitting file data .
-Committed revision 6.
-
-Committed revision 7.
-Sending        letter.txt
-Transmitting file data .
-Committed revision 8.
-% convert to hg once
-initializing destination A-hg repository
-scanning source...
-sorting...
-converting...
-3 init projA
-2 hello
-1 world
-0 nice day
-updating tags
-% update svn repository again
-A         todo
-A         todo/letter2.txt
-Sending        letter.txt
-Adding         todo
-Adding         todo/letter2.txt
-Transmitting file data ..
-Committed revision 9.
-
-Committed revision 10.
-Sending        todo/letter2.txt
-Transmitting file data .
-Committed revision 11.
-% test incremental conversion
-scanning source...
-sorting...
-converting...
-1 second letter
-0 work in progress
-updating tags
-o  7 update tags files: .hgtags
-|
-o  6 work in progress files: todo/letter2.txt
-|
-o  5 second letter files: letter.txt todo/letter2.txt
-|
-o  4 update tags files: .hgtags
-|
-o  3 nice day files: letter.txt
-|
-o  2 world files: letter.txt
-|
-o  1 hello files: letter.txt
-|
-o  0 init projA files:
-
-tip
-v0.2
-v0.1
 # now tests that it works with trunk/tags layout, but no branches yet
 
 % initial svn import
 Adding         projB/trunk
 Adding         projB/tags
 
-Committed revision 12.
+Committed revision 1.
 % update svn repository
-Checked out revision 12.
+Checked out revision 1.
 A         letter.txt
 Adding         letter.txt
 Transmitting file data .
-Committed revision 13.
+Committed revision 2.
 Sending        letter.txt
 Transmitting file data .
-Committed revision 14.
+Committed revision 3.
 
-Committed revision 15.
+Committed revision 4.
 Sending        letter.txt
 Transmitting file data .
-Committed revision 16.
+Committed revision 5.
 % convert to hg once
 initializing destination B-hg repository
 scanning source...
@@ -161,12 +34,12 @@
 Sending        letter.txt
 Adding         letter2.txt
 Transmitting file data ..
-Committed revision 17.
+Committed revision 6.
 
-Committed revision 18.
+Committed revision 7.
 Sending        letter2.txt
 Transmitting file data .
-Committed revision 19.
+Committed revision 8.
 % test incremental conversion
 scanning source...
 sorting...
@@ -193,3 +66,26 @@
 tip
 v0.2
 v0.1
+% test filemap
+initializing destination fmap repository
+scanning source...
+sorting...
+converting...
+5 init projB
+4 hello
+3 world
+2 nice day
+1 second letter
+0 work in progress
+o  1 work in progress files: letter2.txt
+|
+o  0 second letter files: letter2.txt
+
+% test stop revision
+initializing destination stoprev repository
+scanning source...
+sorting...
+converting...
+0 init projB
+extra:       branch=
+extra:       convert_revision=
--- a/tests/test-convert-svn-startrev	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert-svn-startrev	Fri Apr 04 23:13:32 2008 +0200
@@ -42,16 +42,16 @@
 svn rm trunk/b
 svn ci -m removeb
 svn up
-echo a >> trunk/a
+"$TESTDIR/svn-safe-append.py" a trunk/a
 svn ci -m changeaa
 
 echo % branch
 svn up
 svn copy trunk branches/branch1
-echo a >> branches/branch1/a
+"$TESTDIR/svn-safe-append.py" a branches/branch1/a
 svn ci -m "branch, changeaaa"
 
-echo a >> branches/branch1/a
+"$TESTDIR/svn-safe-append.py" a branches/branch1/a
 echo c > branches/branch1/c
 svn add branches/branch1/c
 svn ci -m "addc,changeaaaa"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-tags	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" svn svn-bindings || exit 80
+
+fix_path()
+{
+    tr '\\' /
+}
+
+echo "[extensions]" >> $HGRCPATH
+echo "convert = " >> $HGRCPATH
+echo "hgext.graphlog =" >> $HGRCPATH
+
+svnadmin create svn-repo
+
+svnpath=`pwd | fix_path`
+# SVN wants all paths to start with a slash. Unfortunately,
+# Windows ones don't. Handle that.
+expr $svnpath : "\/" > /dev/null
+if [ $? -ne 0 ]; then
+    svnpath='/'$svnpath
+fi
+
+echo % initial svn import
+mkdir projA
+cd projA
+mkdir trunk
+mkdir branches
+mkdir tags
+mkdir unrelated
+cd ..
+
+svnurl=file://$svnpath/svn-repo/projA
+svn import -m "init projA" projA $svnurl | fix_path
+
+echo % update svn repository
+svn co $svnurl A | fix_path
+cd A
+echo a > trunk/a
+svn add trunk/a
+svn ci -m adda
+"$TESTDIR/svn-safe-append.py" a trunk/a
+svn ci -m changea
+"$TESTDIR/svn-safe-append.py" a trunk/a
+svn ci -m changea2
+# Add an unrelated commit to test that tags are bound to the
+# correct "from" revision and not a dummy one
+"$TESTDIR/svn-safe-append.py" a unrelated/dummy
+svn add unrelated/dummy
+svn ci -m unrelatedchange
+echo % tag current revision
+svn up
+svn copy trunk tags/trunk.v1
+svn copy trunk tags/trunk.badtag
+svn ci -m "tagging trunk.v1 trunk.badtag"
+"$TESTDIR/svn-safe-append.py" a trunk/a
+svn ci -m changea3
+echo % fix the bad tag
+# trunk.badtag should not show in converted tags
+svn up
+svn mv tags/trunk.badtag tags/trunk.goodtag
+svn ci -m "fix trunk.badtag"
+cd ..
+
+echo % convert
+hg convert --datesort $svnurl A-hg
+
+cd A-hg
+hg glog --template '#rev# #desc|firstline# tags: #tags#\n'
+hg tags -q
+cd ..
+
+echo % convert without tags
+hg convert --datesort --config convert.svn.tags= $svnurl A-notags-hg
+hg -R A-notags-hg tags -q
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-svn-tags.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,84 @@
+% initial svn import
+Adding         projA/trunk
+Adding         projA/unrelated
+Adding         projA/branches
+Adding         projA/tags
+
+Committed revision 1.
+% update svn repository
+A    A/trunk
+A    A/unrelated
+A    A/branches
+A    A/tags
+Checked out revision 1.
+A         trunk/a
+Adding         trunk/a
+Transmitting file data .
+Committed revision 2.
+Sending        trunk/a
+Transmitting file data .
+Committed revision 3.
+Sending        trunk/a
+Transmitting file data .
+Committed revision 4.
+A         unrelated/dummy
+Adding         unrelated/dummy
+Transmitting file data .
+Committed revision 5.
+% tag current revision
+At revision 5.
+A         tags/trunk.v1
+A         tags/trunk.badtag
+Adding         tags/trunk.badtag
+Adding         tags/trunk.v1
+
+Committed revision 6.
+Sending        trunk/a
+Transmitting file data .
+Committed revision 7.
+% fix the bad tag
+At revision 7.
+A         tags/trunk.goodtag
+D         tags/trunk.badtag/a
+D         tags/trunk.badtag
+Deleting       tags/trunk.badtag
+Adding         tags/trunk.goodtag
+
+Committed revision 8.
+% convert
+initializing destination A-hg repository
+scanning source...
+sorting...
+converting...
+4 init projA
+3 adda
+2 changea
+1 changea2
+0 changea3
+updating tags
+o  5 update tags tags: tip
+|
+o  4 changea3 tags:
+|
+o  3 changea2 tags: trunk.v1 trunk.goodtag
+|
+o  2 changea tags:
+|
+o  1 adda tags:
+|
+o  0 init projA tags:
+
+tip
+trunk.v1
+trunk.goodtag
+% convert without tags
+initializing destination A-notags-hg repository
+scanning source...
+sorting...
+converting...
+4 init projA
+3 adda
+2 changea
+1 changea2
+0 changea3
+tip
--- a/tests/test-convert.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-convert.out	Fri Apr 04 23:13:32 2008 +0200
@@ -8,6 +8,7 @@
     - Darcs
     - git
     - Subversion
+    - Monotone
     - GNU Arch
 
     Accepted destination formats:
--- a/tests/test-debugindexdot.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-debugindexdot.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+created new head
 digraph G {
 	-1 -> 0
 	0 -> 1
--- a/tests/test-default-push.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-default-push.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,7 @@
 adding a
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % push should push to default when default-push not set
 pushing
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diff-copy-depth	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+for i in aaa zzz; do
+    hg init t
+    cd t
+
+    echo "-- With $i"
+
+    touch file
+    hg add file
+    hg ci -m "Add"
+
+    hg cp file $i
+    hg ci -m "a -> $i"
+
+    hg cp $i other-file
+    echo "different" >> $i
+    hg ci -m "$i -> other-file"
+
+    hg cp other-file somename
+
+    echo "Status":
+    hg st -C
+    echo
+    echo "Diff:"
+    hg diff -g
+    echo
+
+    cd ..
+    rm -rf t
+done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diff-copy-depth.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,20 @@
+-- With aaa
+Status:
+A somename
+  other-file
+
+Diff:
+diff --git a/other-file b/somename
+copy from other-file
+copy to somename
+
+-- With zzz
+Status:
+A somename
+  other-file
+
+Diff:
+diff --git a/other-file b/somename
+copy from other-file
+copy to somename
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diff-unified	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+hg init repo
+cd repo
+cat > a <<EOF
+c
+c
+a
+a
+b
+a
+a
+c
+c
+EOF
+hg ci -Am adda
+cat > a <<EOF
+c
+c
+a
+a
+dd
+a
+a
+c
+c
+EOF
+
+echo '% default context'
+hg diff --nodates
+
+echo '% invalid --unified'
+hg diff --nodates -U foo
+
+echo '% --unified=2'
+hg diff --nodates -U 2
+
+echo '% diff.unified=2'
+hg --config diff.unified=2 diff --nodates
+
+echo '% diff.unified=2 --unified=1'
+hg diff --nodates -U 1
+
+echo '% invalid diff.unified'
+hg --config diff.unified=foo diff --nodates
+
+exit 0
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diff-unified.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,49 @@
+adding a
+% default context
+diff -r cf9f4ba66af2 a
+--- a/a
++++ b/a
+@@ -2,7 +2,7 @@
+ c
+ a
+ a
+-b
++dd
+ a
+ a
+ c
+% invalid --unified
+abort: diff context lines count must be an integer, not 'foo'
+% --unified=2
+diff -r cf9f4ba66af2 a
+--- a/a
++++ b/a
+@@ -3,5 +3,5 @@
+ a
+ a
+-b
++dd
+ a
+ a
+% diff.unified=2
+diff -r cf9f4ba66af2 a
+--- a/a
++++ b/a
+@@ -3,5 +3,5 @@
+ a
+ a
+-b
++dd
+ a
+ a
+% diff.unified=2 --unified=1
+diff -r cf9f4ba66af2 a
+--- a/a
++++ b/a
+@@ -4,3 +4,3 @@
+ a
+-b
++dd
+ a
+% invalid diff.unified
+abort: diff context lines count must be an integer, not 'foo'
--- a/tests/test-double-merge.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-double-merge.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+created new head
 resolving manifests
  overwrite None partial False
  ancestor 310fd17130da local 2092631ce82b+ remote 7731dad1c2b9
--- a/tests/test-empty-file.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-empty-file.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 changeset:   2:62ec0e86d1e5
 tag:         tip
 parent:      0:567dde5e6e98
--- a/tests/test-empty-group.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-empty-group.out	Fri Apr 04 23:13:32 2008 +0200
@@ -4,22 +4,26 @@
 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
 adding x
 adding y
+created new head
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
+created new head
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
 added 4 changesets with 3 changes to 3 files
+updating working directory
 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
 added 4 changesets with 3 changes to 3 files
+updating working directory
 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 comparing with b
 searching for changes
--- a/tests/test-excessive-merge.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-excessive-merge.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,9 +1,11 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
+created new head
 changeset:   4:f6c172c6198c
 tag:         tip
 parent:      1:448a8c5e42f1
--- a/tests/test-extdiff.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-extdiff.out	Fri Apr 04 23:13:32 2008 +0200
@@ -26,6 +26,7 @@
 use "hg -v help falabala" to show global options
 diffing a.8a5febb7f867/a a.34eed99112ab/a
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
--- a/tests/test-extension.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-extension.out	Fri Apr 04 23:13:32 2008 +0200
@@ -9,6 +9,7 @@
 ui == repo.ui
 reposetup called for b
 ui == repo.ui
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 uisetup called
 ui.parentui is None
--- a/tests/test-fetch	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-fetch	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,9 @@
 #!/bin/sh
 
+# adjust to non-default HGPORT, e.g. with run-tests.py -j
+hideport() { sed "s/localhost:$HGPORT/localhost:20059/"; }
+hidehash() { sed "s/changeset 3:............ merges/changeset 3:... merges/"; }
+
 echo "[extensions]" >> $HGRCPATH
 echo "fetch=" >> $HGRCPATH
 
@@ -36,12 +40,12 @@
 cat a/hg.pid >> "$DAEMON_PIDS"
 
 echo '% fetch over http, no auth'
-hg --cwd d fetch -d '5 0' http://localhost:$HGPORT/
-hg --cwd d tip --template '{desc}\n'
+hg --cwd d fetch -d '5 0' http://localhost:$HGPORT/ | hideport | hidehash
+hg --cwd d tip --template '{desc}\n' | hideport
 
 echo '% fetch over http with auth (should be hidden in desc)'
-hg --cwd e fetch -d '5 0' http://user:password@localhost:$HGPORT/
-hg --cwd e tip --template '{desc}\n'
+hg --cwd e fetch -d '5 0' http://user:password@localhost:$HGPORT/ | hideport | hidehash
+hg --cwd e tip --template '{desc}\n' | hideport
 
 hg clone a f
 hg clone a g
--- a/tests/test-fetch.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-fetch.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,7 @@
 adding a
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b
 1:97d72e5f12c7
@@ -13,7 +15,9 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1:97d72e5f12c7
 adding c
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % should merge c into a
 pulling from ../a
@@ -41,7 +45,7 @@
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 merging with 1:5e056962225c
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-new changeset 3:0b6439e938f9 merges remote changes with local
+new changeset 3:... merges remote changes with local
 Automated merge with http://localhost:20059/
 % fetch over http with auth (should be hidden in desc)
 pulling from http://user:***@localhost:20059/
@@ -54,9 +58,11 @@
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 merging with 1:5e056962225c
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-new changeset 3:0b6439e938f9 merges remote changes with local
+new changeset 3:... merges remote changes with local
 Automated merge with http://localhost:20059/
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding f
 adding g
--- a/tests/test-filebranch	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-filebranch	Fri Apr 04 23:13:32 2008 +0200
@@ -37,7 +37,7 @@
 hg commit -m "branch b" -d "1000000 0"
 
 echo "we shouldn't have anything but n state here"
-hg debugstate | cut -b 1-16,37-
+hg debugstate --nodates | grep -v "^n"
 
 echo merging
 hg pull ../a
@@ -48,7 +48,7 @@
 echo new > quux
 
 echo "we shouldn't have anything but foo in merge state here"
-hg debugstate | cut -b 1-16,37- | grep "^m"
+hg debugstate --nodates | grep "^m"
 
 hg ci -m "merge" -d "1000000 0"
 
--- a/tests/test-filebranch.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-filebranch.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,12 +1,9 @@
 creating base
+updating working directory
 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
 creating branch a
 creating branch b
 we shouldn't have anything but n state here
-n 644          2 bar
-n 644          3 baz
-n 644          3 foo
-n 644          2 quux
 merging
 pulling from ../a
 searching for changes
--- a/tests/test-flags.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-flags.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pulling from ../test1
 requesting all changes
--- a/tests/test-globalopts.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-globalopts.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,6 @@
 adding a
 adding b
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pulling from ../b
 searching for changes
@@ -188,7 +189,7 @@
  serve        export the repository via HTTP
  showconfig   show combined config settings from all hgrc files
  status       show changed files in the working directory
- tag          add a tag for the current or given revision
+ tag          add one or more tags for the current or given revision
  tags         list repository tags
  tip          show the tip revision
  unbundle     apply one or more changegroup files
@@ -241,7 +242,7 @@
  serve        export the repository via HTTP
  showconfig   show combined config settings from all hgrc files
  status       show changed files in the working directory
- tag          add a tag for the current or given revision
+ tag          add one or more tags for the current or given revision
  tags         list repository tags
  tip          show the tip revision
  unbundle     apply one or more changegroup files
--- a/tests/test-glog	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-glog	Fri Apr 04 23:13:32 2008 +0200
@@ -82,7 +82,7 @@
     fi
     echo $rev > $rev
     hg add $rev
-    hg ci -d "$rev 0" -m "($rev) $msg" $rev
+    hg rawcommit -q -d "$rev 0" -m "($rev) $msg" $rev
 }
 
 echo "[extensions]" >> $HGRCPATH
--- a/tests/test-glog.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-glog.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,6 +1,41 @@
 % init
 % empty repo
 % building tree
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
+(the rawcommit command is deprecated)
 % glog -q
 @  34:0eed7cd895e0
 |
--- a/tests/test-help.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-help.out	Fri Apr 04 23:13:32 2008 +0200
@@ -80,7 +80,7 @@
  serve        export the repository via HTTP
  showconfig   show combined config settings from all hgrc files
  status       show changed files in the working directory
- tag          add a tag for the current or given revision
+ tag          add one or more tags for the current or given revision
  tags         list repository tags
  tip          show the tip revision
  unbundle     apply one or more changegroup files
@@ -129,7 +129,7 @@
  serve        export the repository via HTTP
  showconfig   show combined config settings from all hgrc files
  status       show changed files in the working directory
- tag          add a tag for the current or given revision
+ tag          add one or more tags for the current or given revision
  tags         list repository tags
  tip          show the tip revision
  unbundle     apply one or more changegroup files
@@ -205,7 +205,7 @@
  -w --ignore-all-space     ignore white space when comparing lines
  -b --ignore-space-change  ignore changes in the amount of white space
  -B --ignore-blank-lines   ignore changes whose lines are all blank
- -U --unified              number of lines of context to show (default: 3)
+ -U --unified              number of lines of context to show
  -I --include              include names matching the given patterns
  -X --exclude              exclude names matching the given patterns
 
--- a/tests/test-hgweb	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-hgweb	Fri Apr 04 23:13:32 2008 +0200
@@ -27,6 +27,8 @@
 
 echo % should give a 404 - file does not exist
 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/bork?style=raw'
 
 echo % stop and restart
 kill `cat hg.pid`
--- a/tests/test-hgweb-commands	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-hgweb-commands	Fri Apr 04 23:13:32 2008 +0200
@@ -47,6 +47,8 @@
 "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/changegroup'
 echo % stream_out
 "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/stream_out'
+echo % failing unbundle, requires POST request
+"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/unbundle'
 
 echo % Static files
 "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css'
Binary file tests/test-hgweb-commands.out has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgweb-no-path-info	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,59 @@
+#!/bin/sh
+# This tests if hgweb and hgwebdir still work if the REQUEST_URI variable is
+# no longer passed with the request. Instead, SCRIPT_NAME and PATH_INFO
+# should be used from d74fc8dec2b4 onward to route the request.
+
+mkdir repo
+cd repo
+hg init
+echo foo > bar
+hg add bar
+hg commit -m "test" -d "0 0" -u "Testing"
+hg tip
+
+cat > request.py <<EOF
+from mercurial.hgweb import hgweb, hgwebdir
+from StringIO import StringIO
+import os, sys
+
+errors = StringIO()
+input = StringIO()
+
+def startrsp(headers, data):
+	print '---- HEADERS'
+	print headers
+	print '---- DATA'
+	print data
+	return output.write
+
+env = {
+	'wsgi.version': (1, 0),
+	'wsgi.url_scheme': 'http',
+	'wsgi.errors': errors,
+	'wsgi.input': input,
+	'wsgi.multithread': False,
+	'wsgi.multiprocess': False,
+	'wsgi.run_once': False,
+	'REQUEST_METHOD': 'GET',
+	'SCRIPT_NAME': '',
+	'SERVER_NAME': '127.0.0.1',
+	'SERVER_PORT': os.environ['HGPORT'],
+	'SERVER_PROTOCOL': 'HTTP/1.0'
+}
+
+output = StringIO()
+env['QUERY_STRING'] = 'style=atom'
+hgweb('.', name = 'repo')(env, startrsp)
+print output.getvalue()
+print '---- ERRORS'
+print errors.getvalue()
+
+output = StringIO()
+env['QUERY_STRING'] = 'style=raw'
+hgwebdir({'repo': '.'})(env, startrsp)
+print output.getvalue()
+print '---- ERRORS'
+print errors.getvalue()
+EOF
+
+python request.py | sed "s/http:\/\/127\.0\.0\.1:[0-9]*\//http:\/\/127.0.0.1\//"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgweb-no-path-info.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,50 @@
+changeset:   0:4cbec7e6f8c4
+tag:         tip
+user:        Testing
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     test
+
+---- HEADERS
+200 Script output follows
+---- DATA
+[('Content-Type', 'application/atom+xml; charset=ascii')]
+<?xml version="1.0" encoding="ascii"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <!-- Changelog -->
+ <id>http://127.0.0.1/</id>
+ <link rel="self" href="http://127.0.0.1/atom-log"/>
+ <link rel="alternate" href="http://127.0.0.1/"/>
+ <title>repo Changelog</title>
+ <updated>1970-01-01T00:00:00+00:00</updated>
+
+ <entry>
+  <title>test</title>
+  <id>http://www.selenic.com/mercurial/#changeset-4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e</id>
+  <link href="http://127.0.0.1/rev/4cbec7e6f8c42eb52b6b52670e1f7560ae9a101e"/>
+  <author>
+   <name>Testing</name>
+   <email>&#84;&#101;&#115;&#116;&#105;&#110;&#103;</email>
+  </author>
+  <updated>1970-01-01T00:00:00+00:00</updated>
+  <published>1970-01-01T00:00:00+00:00</published>
+  <content type="xhtml">
+   <div xmlns="http://www.w3.org/1999/xhtml">
+    <pre xml:space="preserve">test</pre>
+   </div>
+  </content>
+ </entry>
+
+</feed>
+
+---- ERRORS
+
+---- HEADERS
+200 Script output follows
+---- DATA
+[('Content-Type', 'text/plain; charset=ascii')]
+
+repo/
+
+
+---- ERRORS
+
--- a/tests/test-hgweb.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-hgweb.out	Fri Apr 04 23:13:32 2008 +0200
@@ -59,14 +59,49 @@
 400
 
 
-error: No such method: spam
+error: no such method: spam
 % should give a 404 - file does not exist
 404 Not Found
 
 
-error: Path not found: bork/
+error: bork@2ef0ac749a14: not found in manifest
+404 Not Found
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<link rel="icon" href="/static/hgicon.png" type="image/png">
+<meta name="robots" content="index, nofollow" />
+<link rel="stylesheet" href="/static/style.css" type="text/css" />
+
+<title>Mercurial Error</title>
+</head>
+<body>
+
+<h2>Mercurial Error</h2>
+
+<p>
+An error occurred while processing your request:
+</p>
+<p>
+bork@2ef0ac749a14: not found in manifest
+</p>
+
+
+<div class="logo">
+<a href="http://www.selenic.com/mercurial/">
+<img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+</div>
+
+</body>
+</html>
+
+404 Not Found
+
+
+error: bork@2ef0ac749a14: not found in manifest
 % stop and restart
-7 log lines written
+9 log lines written
 % static file
 200 Script output follows
 
@@ -147,4 +182,8 @@
 	background-color: #aaffaa;
 	border-color: #ccffcc #00cc33 #00cc33 #ccffcc;
 }
+span.logtags span.inbranchtag {
+	background-color: #d5dde6;
+	border-color: #e3ecf4 #9398f4 #9398f4 #e3ecf4;
+}
 % errors
--- a/tests/test-hgwebdir.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-hgwebdir.out	Fri Apr 04 23:13:32 2008 +0200
@@ -5,7 +5,7 @@
 404 Not Found
 
 
-error: Path not found: bork/
+error: bork@8580ff50825a: not found in manifest
 % should succeed
 200 Script output follows
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgwebdirsym	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,49 @@
+#!/bin/sh
+# Tests whether or not hgwebdir properly handles various symlink topologies.
+
+"$TESTDIR/hghave" symlink || exit 80
+
+hg init a
+echo a > a/a
+hg --cwd a ci -Ama -d'1 0'
+
+mkdir webdir
+cd webdir
+
+hg init b
+echo b > b/b
+hg --cwd b ci -Amb -d'2 0'
+
+hg init c
+echo c > c/c
+hg --cwd c ci -Amc -d'3 0'
+
+ln -s ../a al
+ln -s ../webdir circle
+
+root=`pwd`
+
+cd ..
+
+cat > collections.conf <<EOF
+[collections]
+$root=$root
+EOF
+
+hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf collections.conf \
+    -A access-collections.log -E error-collections.log
+cat hg.pid >> $DAEMON_PIDS
+
+echo % should succeed
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/al/file/tip/a?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/b/file/tip/b?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw'
+
+echo % should fail
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/circle/al/file/tip/a?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/circle/b/file/tip/a?style=raw'
+"$TESTDIR/get-with-headers.py" localhost:$HGPORT '/circle/c/file/tip/a?style=raw'
+
+echo % collections errors
+cat error-collections.log
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-hgwebdirsym.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,34 @@
+adding a
+adding b
+adding c
+% should succeed
+200 Script output follows
+
+
+/al/
+/b/
+/c/
+
+200 Script output follows
+
+a
+200 Script output follows
+
+b
+200 Script output follows
+
+c
+% should fail
+404 Not Found
+
+
+error: repository circle not found
+404 Not Found
+
+
+error: repository circle not found
+404 Not Found
+
+
+error: repository circle not found
+% collections errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-highlight	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+"$TESTDIR/hghave" pygments || exit 80
+
+cat <<EOF >> $HGRCPATH
+[extensions]
+hgext.highlight =
+EOF
+
+hg init test
+cd test
+cp $TESTDIR/get-with-headers.py ./
+hg ci -Ama
+
+echo % hg serve
+hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
+cat hg.pid >> $DAEMON_PIDS
+
+echo % hgweb filerevision
+("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/get-with-headers.py') \
+    | sed "s/[0-9]* years ago/long ago/g"
+
+echo % hgweb fileannotate
+("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/annotate/tip/get-with-headers.py') \
+    | sed "s/[0-9]* years ago/long ago/g"
+
+echo % errors encountered
+cat errors.log
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-highlight.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,129 @@
+adding get-with-headers.py
+% hg serve
+% hgweb filerevision
+200 Script output follows
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<link rel="icon" href="/static/hgicon.png" type="image/png">
+<meta name="robots" content="index, nofollow" />
+<link rel="stylesheet" href="/static/style.css" type="text/css" />
+
+<link rel="stylesheet" href="/static/highlight.css" type="text/css" />
+<title>test:get-with-headers.py</title>
+</head>
+<body>
+
+<div class="buttons">
+<a href="/log/0">changelog</a>
+<a href="/shortlog/0">shortlog</a>
+<a href="/tags">tags</a>
+<a href="/rev/79ee608ca36d">changeset</a>
+<a href="/file/79ee608ca36d/">files</a>
+<a href="/log/79ee608ca36d/get-with-headers.py">revisions</a>
+<a href="/annotate/79ee608ca36d/get-with-headers.py">annotate</a>
+<a href="/raw-file/79ee608ca36d/get-with-headers.py">raw</a>
+</div>
+
+<h2>get-with-headers.py</h2>
+
+<table>
+<tr>
+ <td class="metatag">changeset 0:</td>
+ <td><a href="/rev/79ee608ca36d">79ee608ca36d</a></td></tr>
+
+
+<tr>
+ <td class="metatag">author:</td>
+ <td>&#116;&#101;&#115;&#116;</td></tr>
+<tr>
+ <td class="metatag">date:</td>
+ <td>Thu Jan 01 00:00:00 1970 +0000 (long ago)</td></tr>
+<tr>
+ <td class="metatag">permissions:</td>
+ <td>-rwxr-xr-x</td></tr>
+<tr>
+  <td class="metatag">description:</td>
+  <td>a</td>
+</tr>
+</table>
+
+<pre>
+<div class="parity0"><a class="lineno" href="#l1" id="l1">     1</a><span class="c">#!/usr/bin/env python</span></div><div class="parity1"><a class="lineno" href="#l2" id="l2">     2</a></div><div class="parity0"><a class="lineno" href="#l3" id="l3">     3</a><span class="n">__doc__</span> <span class="o">=</span> <span class="s">&quot;&quot;&quot;This does HTTP get requests given a host:port and path and returns</span></div><div class="parity1"><a class="lineno" href="#l4" id="l4">     4</a><span class="s">a subset of the headers plus the body of the result.&quot;&quot;&quot;</span></div><div class="parity0"><a class="lineno" href="#l5" id="l5">     5</a></div><div class="parity1"><a class="lineno" href="#l6" id="l6">     6</a><span class="k">import</span> <span class="nn">httplib</span><span class="o">,</span> <span class="nn">sys</span></div><div class="parity0"><a class="lineno" href="#l7" id="l7">     7</a><span class="n">headers</span> <span class="o">=</span> <span class="p">[</span><span class="n">h</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">3</span><span class="p">:]]</span></div><div class="parity1"><a class="lineno" href="#l8" id="l8">     8</a><span class="n">conn</span> <span class="o">=</span> <span class="n">httplib</span><span class="o">.</span><span class="n">HTTPConnection</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">1</span><span class="p">])</span></div><div class="parity0"><a class="lineno" href="#l9" id="l9">     9</a><span class="n">conn</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s">&quot;GET&quot;</span><span class="p">,</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">2</span><span class="p">])</span></div><div class="parity1"><a class="lineno" href="#l10" id="l10">    10</a><span class="n">response</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">getresponse</span><span class="p">()</span></div><div class="parity0"><a class="lineno" href="#l11" id="l11">    11</a><span class="k">print</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">reason</span></div><div class="parity1"><a class="lineno" href="#l12" id="l12">    12</a><span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">headers</span><span class="p">:</span></div><div class="parity0"><a class="lineno" href="#l13" id="l13">    13</a>    <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span></div><div class="parity1"><a class="lineno" href="#l14" id="l14">    14</a>        <span class="k">print</span> <span class="s">&quot;</span><span class="si">%s</span><span class="s">: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="n">h</span><span class="p">))</span></div><div class="parity0"><a class="lineno" href="#l15" id="l15">    15</a><span class="k">print</span></div><div class="parity1"><a class="lineno" href="#l16" id="l16">    16</a><span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">())</span></div><div class="parity0"><a class="lineno" href="#l17" id="l17">    17</a></div><div class="parity1"><a class="lineno" href="#l18" id="l18">    18</a><span class="k">if</span> <span class="mf">200</span> <span class="o">&lt;=</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span> <span class="o">&lt;=</span> <span class="mf">299</span><span class="p">:</span></div><div class="parity0"><a class="lineno" href="#l19" id="l19">    19</a>    <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mf">0</span><span class="p">)</span></div><div class="parity1"><a class="lineno" href="#l20" id="l20">    20</a><span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span></div>
+</pre>
+
+
+<div class="logo">
+<a href="http://www.selenic.com/mercurial/">
+<img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+</div>
+
+</body>
+</html>
+
+% hgweb fileannotate
+200 Script output follows
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<link rel="icon" href="/static/hgicon.png" type="image/png">
+<meta name="robots" content="index, nofollow" />
+<link rel="stylesheet" href="/static/style.css" type="text/css" />
+
+<link rel="stylesheet" href="/static/highlight.css" type="text/css" />
+<title>test: get-with-headers.py annotate</title>
+</head>
+<body>
+
+<div class="buttons">
+<a href="/log/0">changelog</a>
+<a href="/shortlog/0">shortlog</a>
+<a href="/tags">tags</a>
+<a href="/rev/79ee608ca36d">changeset</a>
+<a href="/file/79ee608ca36d/">files</a>
+<a href="/file/79ee608ca36d/get-with-headers.py">file</a>
+<a href="/log/79ee608ca36d/get-with-headers.py">revisions</a>
+<a href="/raw-annotate/79ee608ca36d/get-with-headers.py">raw</a>
+</div>
+
+<h2>Annotate get-with-headers.py</h2>
+
+<table>
+<tr>
+ <td class="metatag">changeset 0:</td>
+ <td><a href="/rev/79ee608ca36d">79ee608ca36d</a></td></tr>
+
+
+<tr>
+ <td class="metatag">author:</td>
+ <td>&#116;&#101;&#115;&#116;</td></tr>
+<tr>
+ <td class="metatag">date:</td>
+ <td>Thu Jan 01 00:00:00 1970 +0000 (long ago)</td></tr>
+<tr>
+ <td class="metatag">permissions:</td>
+ <td>-rwxr-xr-x</td></tr>
+<tr>
+  <td class="metatag">description:</td>
+  <td>a</td>
+</tr>
+</table>
+
+<br/>
+
+<table cellspacing="0" cellpadding="0">
+<tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l1">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l1" id="l1">     1</a></td><td><pre><span class="c">#!/usr/bin/env python</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l2">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l2" id="l2">     2</a></td><td><pre></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l3">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l3" id="l3">     3</a></td><td><pre><span class="n">__doc__</span> <span class="o">=</span> <span class="s">&quot;&quot;&quot;This does HTTP get requests given a host:port and path and returns</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l4">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l4" id="l4">     4</a></td><td><pre><span class="s">a subset of the headers plus the body of the result.&quot;&quot;&quot;</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l5">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l5" id="l5">     5</a></td><td><pre></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l6">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l6" id="l6">     6</a></td><td><pre><span class="k">import</span> <span class="nn">httplib</span><span class="o">,</span> <span class="nn">sys</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l7">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l7" id="l7">     7</a></td><td><pre><span class="n">headers</span> <span class="o">=</span> <span class="p">[</span><span class="n">h</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">3</span><span class="p">:]]</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l8">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l8" id="l8">     8</a></td><td><pre><span class="n">conn</span> <span class="o">=</span> <span class="n">httplib</span><span class="o">.</span><span class="n">HTTPConnection</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">1</span><span class="p">])</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l9">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l9" id="l9">     9</a></td><td><pre><span class="n">conn</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s">&quot;GET&quot;</span><span class="p">,</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mf">2</span><span class="p">])</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l10">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l10" id="l10">    10</a></td><td><pre><span class="n">response</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">getresponse</span><span class="p">()</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l11">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l11" id="l11">    11</a></td><td><pre><span class="k">print</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">reason</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l12">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l12" id="l12">    12</a></td><td><pre><span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">headers</span><span class="p">:</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l13">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l13" id="l13">    13</a></td><td><pre>    <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l14">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l14" id="l14">    14</a></td><td><pre>        <span class="k">print</span> <span class="s">&quot;</span><span class="si">%s</span><span class="s">: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="n">h</span><span class="p">))</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l15">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l15" id="l15">    15</a></td><td><pre><span class="k">print</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l16">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l16" id="l16">    16</a></td><td><pre><span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">())</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l17">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l17" id="l17">    17</a></td><td><pre></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l18">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l18" id="l18">    18</a></td><td><pre><span class="k">if</span> <span class="mf">200</span> <span class="o">&lt;=</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span> <span class="o">&lt;=</span> <span class="mf">299</span><span class="p">:</span></pre></td></tr><tr class="parity0"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l19">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l19" id="l19">    19</a></td><td><pre>    <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mf">0</span><span class="p">)</span></pre></td></tr><tr class="parity1"><td class="annotate"><a href="/annotate/79ee608ca36d/get-with-headers.py#l20">&#116;&#101;&#115;&#116;@0</a></td><td><a class="lineno" href="#l20" id="l20">    20</a></td><td><pre><span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span></pre></td></tr>
+</table>
+
+
+<div class="logo">
+<a href="http://www.selenic.com/mercurial/">
+<img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+</div>
+
+</body>
+</html>
+
+% errors encountered
--- a/tests/test-hook.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-hook.out	Fri Apr 04 23:13:32 2008 +0200
@@ -3,6 +3,7 @@
 0:29b62aeb769f
 commit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000 
 commit.b hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000 
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b 
 pretxncommit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b 
@@ -15,6 +16,7 @@
 2:1324a5531bac
 commit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b 
 commit.b hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b 
+created new head
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 precommit hook: HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2 
--- a/tests/test-http	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-http	Fri Apr 04 23:13:32 2008 +0200
@@ -9,7 +9,7 @@
 hg --config server.uncompressed=True serve -p $HGPORT -d --pid-file=../hg1.pid
 hg serve -p $HGPORT1 -d --pid-file=../hg2.pid
 # Test server address cannot be reused
-hg serve -p $HGPORT1 2>&1 | sed -e 's/abort: cannot start server:.*/abort: cannot start server:/'
+hg serve -p $HGPORT1 2>&1 | sed -e "s/abort: cannot start server at ':$HGPORT1':.*/abort: cannot start server at ':20060':/"
 cd ..
 cat hg1.pid hg2.pid >> $DAEMON_PIDS
 
--- a/tests/test-http-clone-r.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-http-clone-r.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,6 @@
 # creating 'remote'
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
    rev    offset  length   base linkrev nodeid       p1           p2
      0         0       3      0       0 362fef284ce2 000000000000 000000000000
@@ -32,6 +33,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -43,6 +45,7 @@
 adding manifests
 adding file changes
 added 2 changesets with 2 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -54,6 +57,7 @@
 adding manifests
 adding file changes
 added 3 changesets with 3 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -65,6 +69,7 @@
 adding manifests
 adding file changes
 added 4 changesets with 4 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -76,6 +81,7 @@
 adding manifests
 adding file changes
 added 2 changesets with 2 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -87,6 +93,7 @@
 adding manifests
 adding file changes
 added 3 changesets with 3 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -98,6 +105,7 @@
 adding manifests
 adding file changes
 added 4 changesets with 5 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -109,6 +117,7 @@
 adding manifests
 adding file changes
 added 5 changesets with 6 changes to 3 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -120,6 +129,7 @@
 adding manifests
 adding file changes
 added 5 changesets with 5 changes to 2 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
--- a/tests/test-http-proxy.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-http-proxy.out	Fri Apr 04 23:13:32 2008 +0200
@@ -3,6 +3,7 @@
 streaming all changes
 XXX files to transfer, XXX bytes of data
 transferred XXX bytes in XXX seconds (XXX XB/sec)
+updating working directory
 XXX files updated, XXX files merged, XXX files removed, XXX files unresolved
 checking changesets
 checking manifests
@@ -15,6 +16,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -27,6 +29,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 %% proxy url with user name and password
 requesting all changes
@@ -34,6 +37,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 %% url with user name and password
 requesting all changes
@@ -41,6 +45,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 %% bad host:port for proxy
 abort: error: Connection refused
--- a/tests/test-http.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-http.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,9 +1,10 @@
 adding foo
-abort: cannot start server:
+abort: cannot start server at ':20060':
 % clone via stream
 streaming all changes
 XXX files to transfer, XXX bytes of data
 transferred XXX bytes in XXX seconds (XXX XB/sec)
+updating working directory
 XXX files updated, XXX files merged, XXX files removed, XXX files unresolved
 checking changesets
 checking manifests
@@ -16,6 +17,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % clone via pull
 requesting all changes
@@ -23,6 +25,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
--- a/tests/test-imerge	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-imerge	Fri Apr 04 23:13:32 2008 +0200
@@ -33,6 +33,12 @@
 echo % next
 hg imerge next
 
+echo % resolve and unresolve
+hg imerge resolve foo
+hg -v imerge st
+hg imerge unresolve foo
+hg -v imerge st
+
 echo % merge next
 hg --traceback imerge
 
@@ -46,6 +52,11 @@
 echo foo > foo2
 hg imerge save ../savedmerge
 
+echo % merge auto
+hg up -C 1
+hg --traceback imerge --auto
+cat foo2
+
 echo % load
 hg up -C 0
 hg imerge --traceback load ../savedmerge
--- a/tests/test-imerge.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-imerge.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,6 +1,7 @@
 adding bar
 adding foo
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 % start imerge
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
@@ -14,6 +15,12 @@
 U foo (foo2)
 % next
 foo
+% resolve and unresolve
+merging e6da46716401 and 30d266f502e7
+R foo (foo2)
+all conflicts resolved
+merging e6da46716401 and 30d266f502e7
+U foo (foo2)
 % merge next
 merging foo and foo2
 all conflicts resolved
@@ -22,6 +29,20 @@
 merging foo and foo2
 all conflicts resolved
 % save
+% merge auto
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+merging foo2 and foo
+warning: conflicts during merge.
+merging foo2 failed!
+U foo2
+foo
+<<<<<<< local
+foo
+=======
+bar
+>>>>>>> other
 % load
 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-import	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-import	Fri Apr 04 23:13:32 2008 +0200
@@ -225,3 +225,22 @@
 hg manifest
 cd ..
 
+echo % 'test update+rename with common name (issue 927)'
+hg init t
+cd t
+touch a
+hg ci -Am t
+echo a > a
+# Here, bfile.startswith(afile)
+hg copy a a2
+hg ci -m copya
+hg export --git tip > copy.diff
+hg up -C 0
+hg import copy.diff
+echo % view a
+# a should contain an 'a'
+cat a
+echo % view a2
+# and a2 should have duplicated it
+cat a2
+cd ..
--- a/tests/test-import.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-import.out	Fri Apr 04 23:13:32 2008 +0200
@@ -6,6 +6,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../tip.patch
 % message should be same
@@ -18,6 +19,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../tip.patch
 transaction abort!
@@ -29,6 +31,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../tip.patch
 % import of plain diff with specific date and user
@@ -37,6 +40,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../tip.patch
 changeset:   1:ca68f19f3a40
@@ -61,6 +65,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../tip.patch
 diff -r 80971e65b431 a
@@ -75,6 +80,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying tip.patch
 % import from stdin
@@ -83,6 +89,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
 % override commit message
@@ -91,6 +98,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
 summary:     override
@@ -100,6 +108,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../msg.patch
 user:        email patcher
@@ -110,6 +119,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
 % plain diff in email, subject, no message body
@@ -118,6 +128,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
 % plain diff in email, no subject, no message body, should fail
@@ -126,6 +137,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
 transaction abort!
@@ -137,6 +149,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
 summary:     second change
@@ -146,6 +159,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
 email patch
@@ -164,6 +178,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../../../tip.patch
 % message should be 'subdir change'
@@ -174,6 +189,7 @@
 % test fuzziness
 adding a
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 applying tip.patch
 patching file a
 Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines).
@@ -232,3 +248,11 @@
 diff --git a/b b/b
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying remove.diff
+% test update+rename with common name (issue 927)
+adding a
+1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+applying copy.diff
+% view a
+a
+% view a2
+a
--- a/tests/test-incoming-outgoing.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-incoming-outgoing.out	Fri Apr 04 23:13:32 2008 +0200
@@ -279,6 +279,7 @@
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     8
 
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
--- a/tests/test-inherit-mode	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-inherit-mode	Fri Apr 04 23:13:32 2008 +0200
@@ -78,6 +78,7 @@
 cd ..
 hg init setgid
 cd setgid
+chmod g+rwx .hg/store
 chmod g+s .hg/store 2> /dev/null
 mkdir dir
 touch dir/file
--- a/tests/test-issue612.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-issue612.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,6 +1,7 @@
 adding src/a.c
 moving src/a.c to source/a.c
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 ? src/a.o
 merging src/a.c and source/a.c
 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-issue619.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-issue619.out	Fri Apr 04 23:13:32 2008 +0200
@@ -6,5 +6,5 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 bogus fast-forward should fail
-abort: there is nothing to merge, just use 'hg update' or look at 'hg heads'
+abort: can't merge with ancestor
 done
--- a/tests/test-issue672.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-issue672.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,6 +1,7 @@
 adding 1
 adding 2
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 resolving manifests
  overwrite None partial False
  ancestor 81f4b099af3d local c64f439569a9+ remote 2f8037f47a5c
@@ -17,6 +18,7 @@
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 resolving manifests
  overwrite None partial False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-issue842	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,20 @@
+hg init test
+cd test
+echo foo > a
+hg ci -Ama
+
+hg up -r0000
+echo bar > a
+echo % should issue warning
+hg ci -Amb
+
+hg up -r0000
+echo stuffy > a
+echo % should not issue warning
+hg ci -q -Amc
+
+hg up -r0000
+echo crap > a
+hg branch testing
+echo % should not issue warning
+hg ci -q -Amd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-issue842.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,10 @@
+adding a
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+% should issue warning
+adding a
+created new head
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+% should not issue warning
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+marked working directory as branch testing
+% should not issue warning
--- a/tests/test-keyword	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-keyword	Fri Apr 04 23:13:32 2008 +0200
@@ -158,8 +158,7 @@
 hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
 echo % cat a c
 cat a c
-echo % touch copied c after 1 second
-sleep 1
+echo % touch copied c
 touch c
 echo % status
 hg status
@@ -297,3 +296,13 @@
 echo % hg cat
 hg cat sym a b
 echo
+
+echo % hg serve
+hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
+cat hg.pid >> $DAEMON_PIDS
+echo % hgweb changeset
+("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/tip/?style=raw')
+echo % hgweb filediff
+("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/bb948857c743/a?style=raw')
+echo % errors encountered
+cat errors.log
--- a/tests/test-keyword.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-keyword.out	Fri Apr 04 23:13:32 2008 +0200
@@ -230,7 +230,7 @@
 expand $Id: c,v e22d299ac0c2 1970/01/01 00:00:01 user $
 do not process $Id:
 xxx $
-% touch copied c after 1 second
+% touch copied c
 % status
 % kwfiles
 a
@@ -312,6 +312,7 @@
 adding manifests
 adding file changes
 added 2 changesets with 3 changes to 3 files
+updating working directory
 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % incoming
 comparing with test-keyword/Test
@@ -392,3 +393,39 @@
 $Xinfo$
 ignore $Id$
 a
+% hg serve
+% hgweb changeset
+200 Script output follows
+
+
+# HG changeset patch
+# User User Name <user@example.com>
+# Date 3 0
+# Node ID cfa68229c1167443337266ebac453c73b1d5d16e
+# Parent bb948857c743469b22bbf51f7ec8112279ca5d83
+xa
+
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/x/a	Thu Jan 01 00:00:03 1970 +0000
+@@ -0,0 +1,4 @@
++expand $Id$
++do not process $Id:
++xxx $
++$Xinfo$
+
+% hgweb filediff
+200 Script output follows
+
+
+--- a/a	Thu Jan 01 00:00:00 1970 +0000
++++ b/a	Thu Jan 01 00:00:02 1970 +0000
+@@ -1,3 +1,4 @@
+ expand $Id$
+ do not process $Id:
+ xxx $
++$Xinfo$
+
+
+
+
+% errors encountered
--- a/tests/test-lock-badness.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-lock-badness.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 adding a
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b
 pushing to ../a
--- a/tests/test-log.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-log.out	Fri Apr 04 23:13:32 2008 +0200
@@ -85,6 +85,7 @@
 % log copies, non-linear manifest
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 adding foo
+created new head
 5 e (dir/b)
 % log copies, execute bit set
 6 
@@ -106,6 +107,7 @@
 adding base
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b1
+created new head
 % log -f
 changeset:   3:e62f78d544b4
 tag:         tip
@@ -126,6 +128,7 @@
 
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 adding b2
+created new head
 % log -f -r 1:tip
 changeset:   1:3d5bf5654eda
 user:        test
--- a/tests/test-manifest-merging.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-manifest-merging.out	Fri Apr 04 23:13:32 2008 +0200
@@ -2,6 +2,7 @@
 % create alpha in first repo
 adding alpha
 % clone foo-base to foo-work
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % create beta in second repo
 adding beta
--- a/tests/test-merge-commit.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge-commit.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 merging bar and foo
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
@@ -14,6 +15,7 @@
      0         0       7      0       0 690b295714ae 000000000000 000000000000
      1         7      13      1       1 9e25c27b8757 690b295714ae 000000000000
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 4:2d2f9a22c82b 2:0a3ab4856510 
 3:7d3b554bfdf1 2:0a3ab4856510 1:5cd961e4045d 
 2:0a3ab4856510 0:2665aaee66e9 
@@ -62,6 +64,7 @@
      0         0       7      0       0 690b295714ae 000000000000 000000000000
      1         7      13      1       1 9e25c27b8757 690b295714ae 000000000000
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 4:2d2f9a22c82b 2:0a3ab4856510 
 3:96ab80c60897 1:5cd961e4045d 2:0a3ab4856510 
 2:0a3ab4856510 0:2665aaee66e9 
--- a/tests/test-merge-default.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge-default.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,6 +1,8 @@
 adding a
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % should fail because not at a head
 abort: repo has 3 heads - please merge with an explicit rev
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-merge-force	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+hg init repo
+cd repo
+
+echo a > a
+hg ci -qAm 'add a'
+
+echo b > b
+hg ci -qAm 'add b'
+
+hg up -qC 0
+hg rm a
+hg ci -m 'rm a'
+
+hg up -qC 1
+rm a
+
+echo '% local deleted a file, remote removed'
+hg merge # should fail, since there are deleted files
+hg -v merge --force
+echo % should show a as removed
+hg st
+
+hg ci -m merge
+echo % manifest. should not have a:
+hg manifest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-merge-force.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,11 @@
+created new head
+% local deleted a file, remote removed
+abort: outstanding uncommitted changes
+resolving manifests
+removing a
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+% should show a as removed
+R a
+% manifest. should not have a:
+b
--- a/tests/test-merge-prompt.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge-prompt.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,6 +1,7 @@
 adding file1
 adding file2
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 
 # non-interactive merge
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-merge-remove	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+hg init repo
+cd repo
+
+echo foo > foo
+echo bar > bar
+hg ci -qAm 'add foo bar'
+
+echo foo2 >> foo
+echo bleh > bar
+hg ci -m 'change foo bar'
+
+hg up -qC 0
+hg mv foo foo1
+echo foo1 > foo1
+hg cat foo >> foo1
+hg ci -m 'mv foo foo1'
+
+hg merge
+hg debugstate --nodates
+hg st -q
+
+echo '% removing foo1 and bar'
+cp foo1 F
+cp bar B
+hg rm -f foo1 bar
+hg debugstate --nodates
+hg st -qC
+
+echo '% readding foo1 and bar'
+cp F foo1
+cp B bar
+hg add -v foo1 bar
+hg debugstate --nodates
+hg st -qC
+
+echo '% reverting foo1 and bar'
+hg revert -vr . foo1 bar
+hg debugstate --nodates
+hg st -qC
+hg diff
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-merge-remove.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,37 @@
+created new head
+merging foo1 and foo
+1 files updated, 1 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+n   0         -2 bar
+m 644         14 foo1
+copy: foo -> foo1
+M bar
+M foo1
+% removing foo1 and bar
+r   0         -2 bar
+r   0         -1 foo1
+copy: foo -> foo1
+R bar
+R foo1
+  foo
+% readding foo1 and bar
+adding bar
+adding foo1
+n   0         -2 bar
+m 644         14 foo1
+copy: foo -> foo1
+M bar
+M foo1
+  foo
+% reverting foo1 and bar
+warning: working directory has two parents, tag '.' uses the first
+saving current version of bar as bar.orig
+reverting bar
+saving current version of foo1 as foo1.orig
+reverting foo1
+n   0         -2 bar
+m 644         14 foo1
+copy: foo -> foo1
+M bar
+M foo1
+  foo
--- a/tests/test-merge-types.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge-types.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,6 @@
 adding a
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 resolving manifests
  overwrite None partial False
  ancestor c334dc3be0da local 521a1e40188f+ remote 3574f3e69b1c
--- a/tests/test-merge1.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge1.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 %% no merges expected
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
@@ -9,6 +10,7 @@
 +This is file b1
 M b
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 %% merge should fail
 abort: untracked file in working directory differs from file in requested revision: 'b'
 %% merge of b expected
@@ -24,6 +26,7 @@
 M b
 %%
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 Contents of b should be "this is file b1"
 This is file b1
 %% merge fails
@@ -39,6 +42,7 @@
 +This is file b22
 M b
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 %% merge of b should fail
 abort: outstanding uncommitted changes
 %% merge of b expected
--- a/tests/test-merge10.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge10.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pulling from ../a
 searching for changes
--- a/tests/test-merge2.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge2.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,8 @@
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b
+created new head
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b
+created new head
--- a/tests/test-merge4.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge4.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
--- a/tests/test-merge5	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge5	Fri Apr 04 23:13:32 2008 +0200
@@ -12,9 +12,14 @@
 hg update 0
 rm b
 hg commit -A -m"comment #2" -d "1000000 0"
+mv a c
 # in theory, we shouldn't need the "-y" below, but it prevents
 # this test from hanging when "hg update" erroneously prompts the
 # user for "keep or delete"
+echo % should abort
+hg update -y 1
+mv c a
+echo % should succeed
 hg update -y 1
 
 exit 0
--- a/tests/test-merge5.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge5.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,7 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 removing b
-abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
+created new head
+% should abort
+abort: crosses branches (use 'hg merge' or 'hg update -C' to discard changes)
+% should succeed
+abort: crosses branches (use 'hg merge' or 'hg update -C')
--- a/tests/test-merge6.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge6.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,8 @@
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pulling from ../B1
 searching for changes
--- a/tests/test-merge7.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge7.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pulling from ../test-a
 searching for changes
--- a/tests/test-merge8.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge8.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pulling from ../a
 searching for changes
--- a/tests/test-merge9.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-merge9.out	Fri Apr 04 23:13:32 2008 +0200
@@ -2,6 +2,7 @@
 adding foo
 adding quux1
 adding quux2
+created new head
 merging bar
 merging bar failed!
 merging foo and baz
--- a/tests/test-mq	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-mq	Fri Apr 04 23:13:32 2008 +0200
@@ -487,3 +487,13 @@
 qlog
 cd ..
 
+echo % 'test applying on an empty file (issue 1033)'
+hg init empty
+cd empty
+touch a
+hg ci -Am addempty
+echo a > a
+hg qnew -f -e changea
+hg qpop
+hg qpush
+cd ..
--- a/tests/test-mq-merge.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-mq-merge.out	Fri Apr 04 23:13:32 2008 +0200
@@ -3,6 +3,7 @@
 copy .hg/patches to .hg/patches.1
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 M b
+created new head
 a
 b
 merging with queue at: .hg/patches.1
--- a/tests/test-mq-pull-from-bundle.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-mq-pull-from-bundle.out	Fri Apr 04 23:13:32 2008 +0200
@@ -17,6 +17,7 @@
 ====== Bundle queue
 1 changesets found
 ====== Clone base
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 ====== Incoming queue bundle
 >> hg -R .hg/patches incoming ../queue.hgq
@@ -39,6 +40,7 @@
 >> hg qseries
 two.patch
 ====== Clone base again
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 ====== Unbundle queue bundle
 >> hg -R .hg/patches unbundle --update ../queue.hgq
--- a/tests/test-mq-qclone-http.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-mq-qclone-http.out	Fri Apr 04 23:13:32 2008 +0200
@@ -19,6 +19,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 3 changes to 3 files
+updating working directory
 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 a
--- a/tests/test-mq-symlinks	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-mq-symlinks	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,7 @@
 #!/bin/sh
 
+"$TESTDIR/hghave" symlink || exit 80
+
 echo "[extensions]" >> $HGRCPATH
 echo "mq=" >> $HGRCPATH
 
--- a/tests/test-mq.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-mq.out	Fri Apr 04 23:13:32 2008 +0200
@@ -52,6 +52,7 @@
 
 use "hg -v help mq" to show aliases and global options
 adding a
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b/z
 % qinit
@@ -323,6 +324,7 @@
 copy from new
 copy to copy
 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+created new head
 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
 adding branch
 adding changesets
@@ -330,6 +332,7 @@
 adding file changes
 added 1 changesets with 1 changes to 1 files
 Patch queue now empty
+(working directory not at tip)
 applying bar
 Now at: bar
 diff --git a/bar b/bar
@@ -363,6 +366,7 @@
 adding file changes
 added 1 changesets with 1 changes to 1 files
 Patch queue now empty
+(working directory not at tip)
 applying bar
 Now at: bar
 diff --git a/foo b/bleh
@@ -401,6 +405,7 @@
 8ba2a2f3e77b55d03051ff9c24ad65e7  bucephalus
 % strip again
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 merging foo
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
@@ -455,6 +460,7 @@
     rev 0: add foo
 patch repo:
     rev 0: checkpoint
+updating working directory
 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 main repo:
@@ -466,9 +472,15 @@
     rev 0: add foo
 patch repo:
     rev 0: checkpoint
+updating working directory
 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 main repo:
     rev 0: add foo
 patch repo:
     rev 0: checkpoint
+% test applying on an empty file (issue 1033)
+adding a
+Patch queue now empty
+applying changea
+Now at: changea
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mv-cp-st-diff	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+add()
+{
+    echo $2 >> $1
+}
+
+hg init t
+cd t
+
+# set up a boring main branch
+add a a
+hg add a
+mkdir x
+add x/x x
+hg add x/x
+hg ci -m0
+
+add a m1
+hg ci -m1
+
+add a m2
+add x/y y1
+hg add x/y
+hg ci -m2
+
+show()
+{
+    echo "- $2: $1"
+    hg st -C $1
+    echo
+    hg diff --git $1
+    echo
+}
+
+count=0
+# make a new branch and get diff/status output
+# $1 - first commit
+# $2 - second commit
+# $3 - working dir action
+# $4 - test description
+tb()
+{
+    hg co -q -C 0
+
+    add a $count
+    count=`expr $count + 1`
+    hg ci -m "t0"
+    $1
+    hg ci -m "t1"
+    $2
+    hg ci -m "t2"
+    $3
+
+    echo "** $4 **"
+    echo "** $1 / $2 / $3"
+    show "" "working to parent"
+    show "--rev 0" "working to root"
+    show "--rev 2" "working to branch"
+    show "--rev 0 --rev ." "root to parent"
+    show "--rev . --rev 0" "parent to root"
+    show "--rev 2 --rev ." "branch to parent"
+    show "--rev . --rev 2" "parent to branch"
+    echo
+}
+
+
+tb "add a a1" "add a a2" "hg mv a b" "rename in working dir"
+tb "add a a1" "add a a2" "hg cp a b" "copy in working dir" 
+tb "hg mv a b" "add b b1" "add b w" "single rename"
+tb "hg cp a b" "add b b1" "add a w" "single copy"
+tb "hg mv a b" "hg mv b c" "hg mv c d" "rename chain"
+tb "hg cp a b" "hg cp b c" "hg cp c d" "copy chain"
+tb "add a a1" "hg mv a b" "hg mv b a" "circular rename"
+
+tb "hg mv x y" "add y/x x1" "add y/x x2" "directory move"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mv-cp-st-diff.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,1220 @@
+created new head
+** rename in working dir **
+** add a a1 / add a a2 / hg mv a b
+- working to parent: 
+A b
+  a
+R a
+
+diff --git a/a b/b
+rename from a
+rename to b
+
+- working to root: --rev 0
+A b
+  a
+R a
+
+diff --git a/a b/b
+rename from a
+rename to b
+--- a/a
++++ b/b
+@@ -1,1 +1,4 @@
+ a
++0
++a1
++a2
+
+- working to branch: --rev 2
+A b
+  a
+R a
+R x/y
+
+diff --git a/a b/b
+rename from a
+rename to b
+--- a/a
++++ b/b
+@@ -1,3 +1,4 @@
+ a
+-m1
+-m2
++0
++a1
++a2
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- root to parent: --rev 0 --rev .
+M a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,4 @@
+ a
++0
++a1
++a2
+
+- parent to root: --rev . --rev 0
+M a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,4 +1,1 @@
+ a
+-0
+-a1
+-a2
+
+- branch to parent: --rev 2 --rev .
+M a
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,4 @@
+ a
+-m1
+-m2
++0
++a1
++a2
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- parent to branch: --rev . --rev 2
+M a
+A x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,4 +1,3 @@
+ a
+-0
+-a1
+-a2
++m1
++m2
+diff --git a/x/y b/x/y
+new file mode 100644
+--- /dev/null
++++ b/x/y
+@@ -0,0 +1,1 @@
++y1
+
+
+created new head
+** copy in working dir **
+** add a a1 / add a a2 / hg cp a b
+- working to parent: 
+A b
+  a
+
+diff --git a/a b/b
+copy from a
+copy to b
+
+- working to root: --rev 0
+M a
+A b
+  a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,4 @@
+ a
++1
++a1
++a2
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,1 +1,4 @@
+ a
++1
++a1
++a2
+
+- working to branch: --rev 2
+M a
+A b
+  a
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,4 @@
+ a
+-m1
+-m2
++1
++a1
++a2
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,3 +1,4 @@
+ a
+-m1
+-m2
++1
++a1
++a2
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- root to parent: --rev 0 --rev .
+M a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,4 @@
+ a
++1
++a1
++a2
+
+- parent to root: --rev . --rev 0
+M a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,4 +1,1 @@
+ a
+-1
+-a1
+-a2
+
+- branch to parent: --rev 2 --rev .
+M a
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,4 @@
+ a
+-m1
+-m2
++1
++a1
++a2
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- parent to branch: --rev . --rev 2
+M a
+A x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,4 +1,3 @@
+ a
+-1
+-a1
+-a2
++m1
++m2
+diff --git a/x/y b/x/y
+new file mode 100644
+--- /dev/null
++++ b/x/y
+@@ -0,0 +1,1 @@
++y1
+
+
+created new head
+** single rename **
+** hg mv a b / add b b1 / add b w
+- working to parent: 
+M b
+
+diff --git a/b b/b
+--- a/b
++++ b/b
+@@ -1,3 +1,4 @@
+ a
+ 2
+ b1
++w
+
+- working to root: --rev 0
+A b
+  a
+R a
+
+diff --git a/a b/b
+rename from a
+rename to b
+--- a/a
++++ b/b
+@@ -1,1 +1,4 @@
+ a
++2
++b1
++w
+
+- working to branch: --rev 2
+A b
+  a
+R a
+R x/y
+
+diff --git a/a b/b
+rename from a
+rename to b
+--- a/a
++++ b/b
+@@ -1,3 +1,4 @@
+ a
+-m1
+-m2
++2
++b1
++w
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- root to parent: --rev 0 --rev .
+A b
+  a
+R a
+
+diff --git a/a b/b
+rename from a
+rename to b
+--- a/a
++++ b/b
+@@ -1,1 +1,3 @@
+ a
++2
++b1
+
+- parent to root: --rev . --rev 0
+A a
+  b
+R b
+
+diff --git a/b b/a
+rename from b
+rename to a
+--- a/b
++++ b/a
+@@ -1,3 +1,1 @@
+ a
+-2
+-b1
+
+- branch to parent: --rev 2 --rev .
+A b
+  a
+R a
+R x/y
+
+diff --git a/a b/b
+rename from a
+rename to b
+--- a/a
++++ b/b
+@@ -1,3 +1,3 @@
+ a
+-m1
+-m2
++2
++b1
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- parent to branch: --rev . --rev 2
+A a
+  b
+A x/y
+R b
+
+diff --git a/b b/a
+rename from b
+rename to a
+--- a/b
++++ b/a
+@@ -1,3 +1,3 @@
+ a
+-2
+-b1
++m1
++m2
+diff --git a/x/y b/x/y
+new file mode 100644
+--- /dev/null
++++ b/x/y
+@@ -0,0 +1,1 @@
++y1
+
+
+created new head
+** single copy **
+** hg cp a b / add b b1 / add a w
+- working to parent: 
+M a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,2 +1,3 @@
+ a
+ 3
++w
+
+- working to root: --rev 0
+M a
+A b
+  a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,3 @@
+ a
++3
++w
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,1 +1,3 @@
+ a
++3
++b1
+
+- working to branch: --rev 2
+M a
+A b
+  a
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,3 @@
+ a
+-m1
+-m2
++3
++w
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,3 +1,3 @@
+ a
+-m1
+-m2
++3
++b1
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- root to parent: --rev 0 --rev .
+M a
+A b
+  a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,2 @@
+ a
++3
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,1 +1,3 @@
+ a
++3
++b1
+
+- parent to root: --rev . --rev 0
+M a
+R b
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,2 +1,1 @@
+ a
+-3
+diff --git a/b b/b
+deleted file mode 100644
+--- a/b
++++ /dev/null
+@@ -1,3 +0,0 @@
+-a
+-3
+-b1
+
+- branch to parent: --rev 2 --rev .
+M a
+A b
+  a
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++3
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,3 +1,3 @@
+ a
+-m1
+-m2
++3
++b1
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- parent to branch: --rev . --rev 2
+M a
+A x/y
+R b
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,2 +1,3 @@
+ a
+-3
++m1
++m2
+diff --git a/b b/b
+deleted file mode 100644
+--- a/b
++++ /dev/null
+@@ -1,3 +0,0 @@
+-a
+-3
+-b1
+diff --git a/x/y b/x/y
+new file mode 100644
+--- /dev/null
++++ b/x/y
+@@ -0,0 +1,1 @@
++y1
+
+
+created new head
+** rename chain **
+** hg mv a b / hg mv b c / hg mv c d
+- working to parent: 
+A d
+  c
+R c
+
+diff --git a/c b/d
+rename from c
+rename to d
+
+- working to root: --rev 0
+A d
+  a
+R a
+
+diff --git a/a b/d
+rename from a
+rename to d
+--- a/a
++++ b/d
+@@ -1,1 +1,2 @@
+ a
++4
+
+- working to branch: --rev 2
+A d
+  a
+R a
+R x/y
+
+diff --git a/a b/d
+rename from a
+rename to d
+--- a/a
++++ b/d
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++4
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- root to parent: --rev 0 --rev .
+A c
+  a
+R a
+
+diff --git a/a b/c
+rename from a
+rename to c
+--- a/a
++++ b/c
+@@ -1,1 +1,2 @@
+ a
++4
+
+- parent to root: --rev . --rev 0
+A a
+  c
+R c
+
+diff --git a/c b/a
+rename from c
+rename to a
+--- a/c
++++ b/a
+@@ -1,2 +1,1 @@
+ a
+-4
+
+- branch to parent: --rev 2 --rev .
+A c
+  a
+R a
+R x/y
+
+diff --git a/a b/c
+rename from a
+rename to c
+--- a/a
++++ b/c
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++4
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- parent to branch: --rev . --rev 2
+A a
+  c
+A x/y
+R c
+
+diff --git a/c b/a
+rename from c
+rename to a
+--- a/c
++++ b/a
+@@ -1,2 +1,3 @@
+ a
+-4
++m1
++m2
+diff --git a/x/y b/x/y
+new file mode 100644
+--- /dev/null
++++ b/x/y
+@@ -0,0 +1,1 @@
++y1
+
+
+created new head
+** copy chain **
+** hg cp a b / hg cp b c / hg cp c d
+- working to parent: 
+A d
+  c
+
+diff --git a/c b/d
+copy from c
+copy to d
+
+- working to root: --rev 0
+M a
+A b
+  a
+A c
+  a
+A d
+  a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,2 @@
+ a
++5
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,1 +1,2 @@
+ a
++5
+diff --git a/a b/c
+copy from a
+copy to c
+--- a/a
++++ b/c
+@@ -1,1 +1,2 @@
+ a
++5
+diff --git a/a b/d
+copy from a
+copy to d
+--- a/a
++++ b/d
+@@ -1,1 +1,2 @@
+ a
++5
+
+- working to branch: --rev 2
+M a
+A b
+  a
+A c
+  a
+A d
+  a
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++5
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++5
+diff --git a/a b/c
+copy from a
+copy to c
+--- a/a
++++ b/c
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++5
+diff --git a/a b/d
+copy from a
+copy to d
+--- a/a
++++ b/d
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++5
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- root to parent: --rev 0 --rev .
+M a
+A b
+  a
+A c
+  a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,2 @@
+ a
++5
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,1 +1,2 @@
+ a
++5
+diff --git a/a b/c
+copy from a
+copy to c
+--- a/a
++++ b/c
+@@ -1,1 +1,2 @@
+ a
++5
+
+- parent to root: --rev . --rev 0
+M a
+R b
+R c
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,2 +1,1 @@
+ a
+-5
+diff --git a/b b/b
+deleted file mode 100644
+--- a/b
++++ /dev/null
+@@ -1,2 +0,0 @@
+-a
+-5
+diff --git a/c b/c
+deleted file mode 100644
+--- a/c
++++ /dev/null
+@@ -1,2 +0,0 @@
+-a
+-5
+
+- branch to parent: --rev 2 --rev .
+M a
+A b
+  a
+A c
+  a
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++5
+diff --git a/a b/b
+copy from a
+copy to b
+--- a/a
++++ b/b
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++5
+diff --git a/a b/c
+copy from a
+copy to c
+--- a/a
++++ b/c
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++5
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- parent to branch: --rev . --rev 2
+M a
+A x/y
+R b
+R c
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,2 +1,3 @@
+ a
+-5
++m1
++m2
+diff --git a/b b/b
+deleted file mode 100644
+--- a/b
++++ /dev/null
+@@ -1,2 +0,0 @@
+-a
+-5
+diff --git a/c b/c
+deleted file mode 100644
+--- a/c
++++ /dev/null
+@@ -1,2 +0,0 @@
+-a
+-5
+diff --git a/x/y b/x/y
+new file mode 100644
+--- /dev/null
++++ b/x/y
+@@ -0,0 +1,1 @@
++y1
+
+
+created new head
+** circular rename **
+** add a a1 / hg mv a b / hg mv b a
+- working to parent: 
+A a
+  b
+R b
+
+diff --git a/b b/a
+rename from b
+rename to a
+
+- working to root: --rev 0
+M a
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,3 @@
+ a
++6
++a1
+
+- working to branch: --rev 2
+M a
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,3 @@
+ a
+-m1
+-m2
++6
++a1
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- root to parent: --rev 0 --rev .
+A b
+  a
+R a
+
+diff --git a/a b/b
+rename from a
+rename to b
+--- a/a
++++ b/b
+@@ -1,1 +1,3 @@
+ a
++6
++a1
+
+- parent to root: --rev . --rev 0
+A a
+  b
+R b
+
+diff --git a/b b/a
+rename from b
+rename to a
+--- a/b
++++ b/a
+@@ -1,3 +1,1 @@
+ a
+-6
+-a1
+
+- branch to parent: --rev 2 --rev .
+A b
+  a
+R a
+R x/y
+
+diff --git a/a b/b
+rename from a
+rename to b
+--- a/a
++++ b/b
+@@ -1,3 +1,3 @@
+ a
+-m1
+-m2
++6
++a1
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+
+- parent to branch: --rev . --rev 2
+A a
+  b
+A x/y
+R b
+
+diff --git a/b b/a
+rename from b
+rename to a
+--- a/b
++++ b/a
+@@ -1,3 +1,3 @@
+ a
+-6
+-a1
++m1
++m2
+diff --git a/x/y b/x/y
+new file mode 100644
+--- /dev/null
++++ b/x/y
+@@ -0,0 +1,1 @@
++y1
+
+
+created new head
+moving x/x to y/x
+** directory move **
+** hg mv x y / add y/x x1 / add y/x x2
+- working to parent: 
+M y/x
+
+diff --git a/y/x b/y/x
+--- a/y/x
++++ b/y/x
+@@ -1,2 +1,3 @@
+ x
+ x1
++x2
+
+- working to root: --rev 0
+M a
+A y/x
+  x/x
+R x/x
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,2 @@
+ a
++7
+diff --git a/x/x b/y/x
+rename from x/x
+rename to y/x
+--- a/x/x
++++ b/y/x
+@@ -1,1 +1,3 @@
+ x
++x1
++x2
+
+- working to branch: --rev 2
+M a
+A y/x
+  x/x
+R x/x
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++7
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+diff --git a/x/x b/y/x
+rename from x/x
+rename to y/x
+--- a/x/x
++++ b/y/x
+@@ -1,1 +1,3 @@
+ x
++x1
++x2
+
+- root to parent: --rev 0 --rev .
+M a
+A y/x
+  x/x
+R x/x
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,2 @@
+ a
++7
+diff --git a/x/x b/y/x
+rename from x/x
+rename to y/x
+--- a/x/x
++++ b/y/x
+@@ -1,1 +1,2 @@
+ x
++x1
+
+- parent to root: --rev . --rev 0
+M a
+A x/x
+  y/x
+R y/x
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,2 +1,1 @@
+ a
+-7
+diff --git a/y/x b/x/x
+rename from y/x
+rename to x/x
+--- a/y/x
++++ b/x/x
+@@ -1,2 +1,1 @@
+ x
+-x1
+
+- branch to parent: --rev 2 --rev .
+M a
+A y/x
+  x/x
+R x/x
+R x/y
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,3 +1,2 @@
+ a
+-m1
+-m2
++7
+diff --git a/x/y b/x/y
+deleted file mode 100644
+--- a/x/y
++++ /dev/null
+@@ -1,1 +0,0 @@
+-y1
+diff --git a/x/x b/y/x
+rename from x/x
+rename to y/x
+--- a/x/x
++++ b/y/x
+@@ -1,1 +1,2 @@
+ x
++x1
+
+- parent to branch: --rev . --rev 2
+M a
+A x/x
+  y/x
+A x/y
+R y/x
+
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,2 +1,3 @@
+ a
+-7
++m1
++m2
+diff --git a/y/x b/x/x
+rename from y/x
+rename to x/x
+--- a/y/x
++++ b/x/x
+@@ -1,2 +1,1 @@
+ x
+-x1
+diff --git a/x/y b/x/y
+new file mode 100644
+--- /dev/null
++++ b/x/y
+@@ -0,0 +1,1 @@
++y1
+
+
--- a/tests/test-newbranch.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-newbranch.out	Fri Apr 04 23:13:32 2008 +0200
@@ -6,6 +6,7 @@
 marked working directory as branch default
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 foo
+created new head
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 foo
--- a/tests/test-notify	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-notify	Fri Apr 04 23:13:32 2008 +0200
@@ -31,8 +31,18 @@
 echo % commit
 hg --traceback --cwd a commit -Amb -d '1 0'
 
+# on Mac OS X 10.5 the tmp path is very long so would get stripped in the subject line
+cat <<EOF >> $HGRCPATH
+[notify]
+maxsubject = 200
+EOF
+
+# the python call below wraps continuation lines, which appear on Mac OS X 10.5 because
+# of the very long subject line
 echo '% pull (minimal config)'
-hg --traceback --cwd b pull ../a 2>&1 | sed -e 's/\(Message-Id:\).*/\1/' \
+hg --traceback --cwd b pull ../a 2>&1 |
+  python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),' |
+  sed -e 's/\(Message-Id:\).*/\1/' \
   -e 's/changeset \([0-9a-f]* *\)in .*test-notif/changeset \1in test-notif/' \
   -e 's/^details: .*test-notify/details: test-notify/' \
   -e 's/^Date:.*/Date:/'
--- a/tests/test-notify.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-notify.out	Fri Apr 04 23:13:32 2008 +0200
@@ -4,6 +4,7 @@
 % commit
 adding a
 % clone
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % commit
 % pull (minimal config)
@@ -22,8 +23,7 @@
 
 changeset 0647d048b600 in test-notify/b
 details: test-notify/b?cmd=changeset;node=0647d048b600
-description:
-	b
+description: b
 
 diffs (6 lines):
 
--- a/tests/test-parentrevspec	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-parentrevspec	Fri Apr 04 23:13:32 2008 +0200
@@ -16,7 +16,7 @@
 
     echo >> foo
 
-    hg commit -d '0 0' -qAm "$msg" foo
+    hg commit -d '0 0' -qAm "$msg"
 }
 
 hg init repo
--- a/tests/test-parents.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-parents.out	Fri Apr 04 23:13:32 2008 +0200
@@ -4,6 +4,7 @@
 adding c
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 adding c
+created new head
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % hg parents
 changeset:   3:02d851b7e549
--- a/tests/test-parse-date.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-parse-date.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 reverting a
+created new head
 changeset 3:107ce1ee2b43 backs out changeset 1:25a1420a55f8
 merging with changeset 3:107ce1ee2b43
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-patch.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-patch.out	Fri Apr 04 23:13:32 2008 +0200
@@ -5,6 +5,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../a.diff
 Using custom patch
--- a/tests/test-paths.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-paths.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,1 +1,2 @@
+updating working directory
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-pull-permission.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-pull-permission.out	Fri Apr 04 23:13:32 2008 +0200
@@ -3,6 +3,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
--- a/tests/test-pull-pull-corruption.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-pull-pull-corruption.out	Fri Apr 04 23:13:32 2008 +0200
@@ -3,6 +3,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pulling from ../source2
 pulling from ../source1
--- a/tests/test-pull-r	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-pull-r	Fri Apr 04 23:13:32 2008 +0200
@@ -14,6 +14,9 @@
 hg init copy
 cd copy
 
+echo '% pull a missing revision'
+hg pull -qr missing ../repo
+
 echo '% pull -r 0'
 hg pull -qr 0 ../repo
 hg log
--- a/tests/test-pull-r.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-pull-r.out	Fri Apr 04 23:13:32 2008 +0200
@@ -15,6 +15,8 @@
 date:        Thu Jan 01 00:00:00 1970 +0000
 summary:     add foo
 
+% pull a missing revision
+abort: unknown revision 'missing'!
 % pull -r 0
 changeset:   0:bbd179dfa0a7
 tag:         tip
--- a/tests/test-pull-update.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-pull-update.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 adding foo
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % should fail
 pulling from ../tt
--- a/tests/test-pull.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-pull.out	Fri Apr 04 23:13:32 2008 +0200
@@ -9,6 +9,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
--- a/tests/test-push-hook-lock.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-push-hook-lock.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,7 @@
 adding foo
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pushing to ../2
 searching for changes
--- a/tests/test-push-http.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-push-http.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 adding a
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % expect ssl error
 pushing to http://localhost/
@@ -6,14 +7,14 @@
 ssl required
 % serve errors
 % expect authorization error
+abort: authorization failed
 pushing to http://localhost/
 searching for changes
-push not authorized
 % serve errors
 % expect authorization error: must have authorized user
+abort: authorization failed
 pushing to http://localhost/
 searching for changes
-push not authorized
 % serve errors
 % expect success
 pushing to http://localhost/
@@ -26,12 +27,12 @@
 changegroup hook: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_URL=remote:http 
 rolling back last transaction
 % expect authorization error: all users denied
+abort: authorization failed
 pushing to http://localhost/
 searching for changes
-push not authorized
 % serve errors
 % expect authorization error: some users denied, users must be authenticated
+abort: authorization failed
 pushing to http://localhost/
 searching for changes
-push not authorized
 % serve errors
--- a/tests/test-push-r.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-push-r.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
    rev    offset  length   base linkrev nodeid       p1           p2
      0         0       3      0       0 362fef284ce2 000000000000 000000000000
--- a/tests/test-push-warn.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-push-warn.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pushing to ../a
 searching for changes
@@ -23,9 +24,12 @@
 adding file changes
 added 2 changesets with 1 changes to 1 files
 adding foo
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 merging foo
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-race	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+mkdir t
+cd t
+hg init
+echo a > a
+hg add a
+hg commit -m test
+
+# do we ever miss a sub-second change?
+for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
+    hg co -qC 0
+    echo b > a
+    hg st
+done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-race.out	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,20 @@
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
+M a
--- a/tests/test-rebuildstate	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-rebuildstate	Fri Apr 04 23:13:32 2008 +0200
@@ -11,14 +11,9 @@
 hg add baz
 hg rm bar
 
-echo '% state dump'
-hg debugstate | cut -b 1-16,37- | sort
+hg debugrebuildstate
+echo '% state dump after'
+hg debugstate --nodates | sort
 echo '% status'
 hg st -A
 
-hg debugrebuildstate
-echo '% state dump'
-hg debugstate | cut -b 1-16,37- | sort
-echo '% status'
-hg st -A
-
--- a/tests/test-rebuildstate.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-rebuildstate.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,14 +1,6 @@
 adding bar
 adding foo
-% state dump
-a   0         -1 baz
-n 644          0 foo
-r   0          0 bar
-% status
-A baz
-R bar
-C foo
-% state dump
+% state dump after
 n 666         -1 bar
 n 666         -1 foo
 % status
--- a/tests/test-remove	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-remove	Fri Apr 04 23:13:32 2008 +0200
@@ -1,45 +1,110 @@
 #!/bin/sh
 
+remove() {
+    hg rm $@
+    hg st
+    # do not use ls -R, which recurses in .hg subdirs on Mac OS X 10.5
+    find . -name .hg -prune -o -type f -print | sort
+    hg up -C
+}
+
 hg init a
 cd a
 echo a > foo
-hg rm foo
+
+echo % file not managed
+remove foo
+
 hg add foo
-hg commit -m 1 -d "1000000 0"
-hg remove
+hg commit -m1
+
+# the table cases
+
+echo % 00 state added, options none
+echo b > bar
+hg add bar
+remove bar
+
+echo % 01 state clean, options none
+remove foo
+
+echo % 02 state modified, options none
+echo b >> foo
+remove foo
+
+echo % 03 state missing, options none
 rm foo
-hg remove foo
-hg revert --all
+remove foo
+
+echo % 10 state added, options -f
+echo b > bar
+hg add bar
+remove -f bar
+rm bar
+
+echo % 11 state clean, options -f
+remove -f foo
+
+echo % 12 state modified, options -f
+echo b >> foo
+remove -f foo
+
+echo % 13 state missing, options -f
 rm foo
-hg remove --after
-hg commit -m 2 -d "1000000 0"
-hg export --nodates 0
-hg export --nodates 1
-hg log -p -r 0
-hg log -p -r 1
+remove -f foo
+
+echo % 20 state added, options -A
+echo b > bar
+hg add bar
+remove -A bar
 
-echo a > a
-hg add a
-hg rm a
-hg rm -f a
-echo b > b
-mkdir c
-echo d > c/d
-hg ci -A -m 3 -d "1000001 0"
-echo c >> b
-hg rm b
-hg rm -f b
-hg rm -A c/d
-hg st
-cat c/d
-hg revert c
-hg rm -A
-hg st
-hg rm -A c
-hg st
-rm c/d
-hg rm -A
-hg st
+echo % 21 state clean, options -A
+remove -A foo
+
+echo % 22 state modified, options -A
+echo b >> foo
+remove -A foo
+
+echo % 23 state missing, options -A
+rm foo
+remove -A foo
+
+echo % 30 state added, options -Af
+echo b > bar
+hg add bar
+remove -Af bar
+rm bar
+
+echo % 31 state clean, options -Af
+remove -Af foo
+
+echo % 32 state modified, options -Af
+echo b >> foo
+remove -Af foo
 
-cd ..
-hg clone a b
+echo % 33 state missing, options -Af
+rm foo
+remove -Af foo
+
+# test some directory stuff
+
+mkdir test
+echo a > test/foo
+echo b > test/bar
+hg ci -Am2
+
+echo % dir, options none
+rm test/bar
+remove test
+
+echo % dir, options -f
+rm test/bar
+remove -f test
+
+echo % dir, options -A
+rm test/bar
+remove -A test
+
+echo % dir, options -Af
+rm test/bar
+remove -Af test
--- a/tests/test-remove.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-remove.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,66 +1,100 @@
-not removing foo: file is not managed
-abort: no files specified
-undeleting foo
-removing foo
-# HG changeset patch
-# User test
-# Date 1000000 0
-# Node ID 8ba83d44753d6259db5ce6524974dd1174e90f47
-# Parent  0000000000000000000000000000000000000000
-1
-
-diff -r 000000000000 -r 8ba83d44753d foo
---- /dev/null
-+++ b/foo
-@@ -0,0 +1,1 @@
-+a
-# HG changeset patch
-# User test
-# Date 1000000 0
-# Node ID a1fce69c50d97881c5c014ab23f580f720c78678
-# Parent  8ba83d44753d6259db5ce6524974dd1174e90f47
-2
-
-diff -r 8ba83d44753d -r a1fce69c50d9 foo
---- a/foo
-+++ /dev/null
-@@ -1,1 +0,0 @@
--a
-changeset:   0:8ba83d44753d
-user:        test
-date:        Mon Jan 12 13:46:40 1970 +0000
-summary:     1
-
-diff -r 000000000000 -r 8ba83d44753d foo
---- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-+++ b/foo	Mon Jan 12 13:46:40 1970 +0000
-@@ -0,0 +1,1 @@
-+a
-
-changeset:   1:a1fce69c50d9
-tag:         tip
-user:        test
-date:        Mon Jan 12 13:46:40 1970 +0000
-summary:     2
-
-diff -r 8ba83d44753d -r a1fce69c50d9 foo
---- a/foo	Mon Jan 12 13:46:40 1970 +0000
-+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
-@@ -1,1 +0,0 @@
--a
-
-not removing a: file has been marked for add (use -f to force removal)
-adding a
-adding b
-adding c/d
-not removing b: file is modified (use -f to force removal)
-R b
-R c/d
-d
-undeleting c/d
-R b
-R b
-removing c/d
-R b
-R c/d
-3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% file not managed
+? foo
+./foo
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 00 state added, options none
+not removing bar: file has been marked for add (use -f to force removal)
+A bar
+./bar
+./foo
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+% 01 state clean, options none
+R foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 02 state modified, options none
+not removing foo: file is modified (use -f to force removal)
+M foo
+./foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 03 state missing, options none
+R foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 10 state added, options -f
+? bar
+./bar
+./foo
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 11 state clean, options -f
+R foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 12 state modified, options -f
+R foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 13 state missing, options -f
+R foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 20 state added, options -A
+not removing bar: file still exists (use -f to force removal)
+A bar
+./bar
+./foo
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+% 21 state clean, options -A
+not removing foo: file still exists (use -f to force removal)
+./foo
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 22 state modified, options -A
+not removing foo: file still exists (use -f to force removal)
+M foo
+./foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 23 state missing, options -A
+R foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 30 state added, options -Af
+? bar
+./bar
+./foo
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 31 state clean, options -Af
+R foo
+./foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 32 state modified, options -Af
+R foo
+./foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% 33 state missing, options -Af
+R foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adding test/bar
+adding test/foo
+% dir, options none
+removing test/foo
+removing test/bar
+R test/bar
+R test/foo
+./foo
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% dir, options -f
+removing test/foo
+removing test/bar
+R test/bar
+R test/foo
+./foo
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% dir, options -A
+not removing test/foo: file still exists (use -f to force removal)
+removing test/bar
+R test/bar
+./foo
+./test/foo
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% dir, options -Af
+removing test/foo
+removing test/bar
+R test/bar
+R test/foo
+./foo
+./test/foo
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-rename-after-merge.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-rename-after-merge.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,6 +1,7 @@
 % create source repository
 adding a
 % fork source repository
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b
 % update source repository
--- a/tests/test-rename-dir-merge.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-rename-dir-merge.out	Fri Apr 04 23:13:32 2008 +0200
@@ -4,6 +4,7 @@
 moving a/a to b/a
 moving a/b to b/b
 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+created new head
 resolving manifests
  overwrite None partial False
  ancestor f9b20c0d4c51 local ce36d17b18fb+ remote 55119e611c80
@@ -70,4 +71,5 @@
 A b/c
   a/c
 ? b/d
+created new head
 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88
--- a/tests/test-rename-dir-merge2.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-rename-dir-merge2.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 adding a/f
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 moving a/f to b/f
 adding a/aa/g
--- a/tests/test-rename-merge1.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-rename-merge1.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,6 @@
 checkout
 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+created new head
 merge
 resolving manifests
  overwrite None partial False
--- a/tests/test-rename-merge2.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-rename-merge2.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,3 +1,4 @@
+created new head
 --------------
 test L:up a   R:nc a b W:       - 1  get local a to b
 --------------
@@ -28,6 +29,7 @@
 C a
 --------------
 
+created new head
 --------------
 test L:nc a b R:up a   W:       - 2  get rem change to a and b
 --------------
@@ -59,6 +61,7 @@
   a
 --------------
 
+created new head
 --------------
 test L:up a   R:nm a b W:       - 3  get local a change to b, remove a
 --------------
@@ -89,6 +92,7 @@
   a
 --------------
 
+created new head
 --------------
 test L:nm a b R:up a   W:       - 4  get remote change to b
 --------------
@@ -117,6 +121,7 @@
   a
 --------------
 
+created new head
 --------------
 test L:       R:nc a b W:       - 5  get b
 --------------
@@ -142,6 +147,7 @@
 C a
 --------------
 
+created new head
 --------------
 test L:nc a b R:       W:       - 6  nothing
 --------------
@@ -165,6 +171,7 @@
 C b
 --------------
 
+created new head
 --------------
 test L:       R:nm a b W:       - 7  get b
 --------------
@@ -191,6 +198,7 @@
 M b
 --------------
 
+created new head
 --------------
 test L:nm a b R:       W:       - 8  nothing
 --------------
@@ -213,6 +221,7 @@
 C b
 --------------
 
+created new head
 --------------
 test L:um a b R:um a b W:       - 9  do merge with ancestor in a
 --------------
@@ -234,6 +243,7 @@
 M b
 --------------
 
+created new head
 --------------
 test L:nm a b R:nm a c W:       - 11 get c, keep b
 --------------
@@ -266,6 +276,7 @@
 C b
 --------------
 
+created new head
 --------------
 test L:nc a b R:up b   W:       - 12 merge b no ancestor
 --------------
@@ -288,6 +299,7 @@
 C a
 --------------
 
+created new head
 --------------
 test L:up b   R:nm a b W:       - 13 merge b no ancestor
 --------------
@@ -311,6 +323,7 @@
 M b
 --------------
 
+created new head
 --------------
 test L:nc a b R:up a b W:       - 14 merge b no ancestor
 --------------
@@ -335,6 +348,7 @@
 M b
 --------------
 
+created new head
 --------------
 test L:up b   R:nm a b W:       - 15 merge b no ancestor, remove a
 --------------
@@ -358,6 +372,7 @@
 M b
 --------------
 
+created new head
 --------------
 test L:nc a b R:up a b W:       - 16 get a, merge b no ancestor
 --------------
@@ -382,6 +397,7 @@
 M b
 --------------
 
+created new head
 --------------
 test L:up a b R:nc a b W:       - 17 keep a, merge b no ancestor
 --------------
@@ -404,6 +420,7 @@
 C a
 --------------
 
+created new head
 --------------
 test L:nm a b R:up a b W:       - 18 merge b no ancestor
 --------------
@@ -428,6 +445,7 @@
 M b
 --------------
 
+created new head
 --------------
 test L:up a b R:nm a b W:       - 19 merge b no ancestor, prompt remove a
 --------------
@@ -450,6 +468,7 @@
 C a
 --------------
 
+created new head
 --------------
 test L:up a   R:um a b W:       - 20 merge a and b to b, remove a
 --------------
@@ -479,6 +498,7 @@
   a
 --------------
 
+created new head
 --------------
 test L:um a b R:up a   W:       - 21 merge a and b to b
 --------------
@@ -506,6 +526,7 @@
   a
 --------------
 
+created new head
 --------------
 test L:nm a b R:up a c W:       - 23 get c, keep b
 --------------
--- a/tests/test-revert.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-revert.out	Fri Apr 04 23:13:32 2008 +0200
@@ -68,6 +68,7 @@
 reverting a
 %% issue332
 adding b/b
+created new head
 reverting b/b
 forgetting newdir/newfile
 reverting b/b
--- a/tests/test-revlog-packentry.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-revlog-packentry.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,6 +1,7 @@
 adding foo
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 adding foo
+created new head
    rev    offset  length   base linkrev nodeid       p1           p2
      0         0       0      0       0 b80de5d13875 000000000000 000000000000
      1         0      24      0       1 0376abec49b8 000000000000 000000000000
--- a/tests/test-serve	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-serve	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,15 @@
 #!/bin/sh
 
+hgserve()
+{
+    hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -v $@ \
+        | sed -e 's/:[0-9][0-9]*//g' -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
+    cat hg.pid >> "$DAEMON_PIDS"
+    sleep 1
+    kill `cat hg.pid`
+    sleep 1
+}
+
 hg init test
 cd test
 
@@ -14,33 +24,16 @@
 fi
 
 echo % With -v
-hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -v | sed -e 's,:[0-9][0-9]*/,/,'
-cat hg.pid >> "$DAEMON_PIDS"
-sleep 1
-kill `cat hg.pid`
-sleep 1
+hgserve
 
 echo % With --prefix foo
-hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -v --prefix foo | sed -e 's,:[0-9][0-9]*/,/,'
-cat hg.pid >> "$DAEMON_PIDS"
-sleep 1
-kill `cat hg.pid`
-sleep 1
+hgserve --prefix foo
 
 echo % With --prefix /foo
-hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -v --prefix /foo | sed -e 's,:[0-9][0-9]*/,/,'
-cat hg.pid >> "$DAEMON_PIDS"
-sleep 1
-kill `cat hg.pid`
-sleep 1
+hgserve --prefix /foo
 
 echo % With --prefix foo/
-hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -v --prefix foo/ | sed -e 's,:[0-9][0-9]*/,/,'
-cat hg.pid >> "$DAEMON_PIDS"
-sleep 1
-kill `cat hg.pid`
-sleep 1
+hgserve --prefix foo/
 
 echo % With --prefix /foo/
-hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -v --prefix /foo/ | sed -e 's,:[0-9][0-9]*/,/,'
-cat hg.pid >> "$DAEMON_PIDS"
+hgserve --prefix /foo/
--- a/tests/test-serve.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-serve.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,12 +1,12 @@
 % Without -v
 access log created - .hg/hgrc respected
 % With -v
-listening at http://localhost/
+listening at http://localhost/ (127.0.0.1)
 % With --prefix foo
-listening at http://localhost/foo/
+listening at http://localhost/foo/ (127.0.0.1)
 % With --prefix /foo
-listening at http://localhost/foo/
+listening at http://localhost/foo/ (127.0.0.1)
 % With --prefix foo/
-listening at http://localhost/foo/
+listening at http://localhost/foo/ (127.0.0.1)
 % With --prefix /foo/
-listening at http://localhost/foo/
+listening at http://localhost/foo/ (127.0.0.1)
--- a/tests/test-simple-update.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-simple-update.out	Fri Apr 04 23:13:32 2008 +0200
@@ -4,6 +4,7 @@
 crosschecking files in changesets and manifests
 checking files
 1 files, 1 changesets, 1 total revisions
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 pulling from ../branch
--- a/tests/test-ssh	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-ssh	Fri Apr 04 23:13:32 2008 +0200
@@ -29,7 +29,7 @@
 
 cat <<EOF > badhook
 import sys
-sys.stdout.write("KABOOM")
+sys.stdout.write("KABOOM\n")
 EOF
 
 echo "# creating 'remote'"
--- a/tests/test-ssh-clone-r.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-ssh-clone-r.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,5 +1,6 @@
 # creating 'remote'
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
    rev    offset  length   base linkrev nodeid       p1           p2
      0         0       3      0       0 362fef284ce2 000000000000 000000000000
@@ -31,6 +32,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -42,6 +44,7 @@
 adding manifests
 adding file changes
 added 2 changesets with 2 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -53,6 +56,7 @@
 adding manifests
 adding file changes
 added 3 changesets with 3 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -64,6 +68,7 @@
 adding manifests
 adding file changes
 added 4 changesets with 4 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -75,6 +80,7 @@
 adding manifests
 adding file changes
 added 2 changesets with 2 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -86,6 +92,7 @@
 adding manifests
 adding file changes
 added 3 changesets with 3 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -97,6 +104,7 @@
 adding manifests
 adding file changes
 added 4 changesets with 5 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -108,6 +116,7 @@
 adding manifests
 adding file changes
 added 5 changesets with 6 changes to 3 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -119,6 +128,7 @@
 adding manifests
 adding file changes
 added 5 changesets with 5 changes to 2 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
--- a/tests/test-ssh.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-ssh.out	Fri Apr 04 23:13:32 2008 +0200
@@ -6,6 +6,7 @@
 streaming all changes
 XXX files to transfer, XXX bytes of data
 transferred XXX bytes in XXX seconds (XXX XB/sec)
+updating working directory
 XXX files updated, XXX files merged, XXX files removed, XXX files unresolved
 checking changesets
 checking manifests
@@ -18,6 +19,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 # verify
 checking changesets
@@ -70,6 +72,7 @@
 checking files
 2 files, 2 changesets, 3 total revisions
 bleah
+created new head
 # push should succeed even though it has an unexpected response
 pushing to ssh://user@dummy/remote
 searching for changes
@@ -78,8 +81,7 @@
 remote: adding manifests
 remote: adding file changes
 remote: added 1 changesets with 1 changes to 1 files
-abort: unexpected response:
-'KABOOM1\n'
+remote: KABOOM
 changeset:   3:ac7448082955
 tag:         tip
 parent:      1:572896fe480d
--- a/tests/test-static-http.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-static-http.out	Fri Apr 04 23:13:32 2008 +0200
@@ -12,6 +12,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -34,6 +35,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
@@ -44,6 +46,7 @@
 default = static-http://localhost/
 % test with empty repo (issue965)
 no changes found
+updating working directory
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 checking changesets
 checking manifests
--- a/tests/test-status	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-status	Fri Apr 04 23:13:32 2008 +0200
@@ -23,7 +23,6 @@
 touch modified removed deleted ignored
 echo "^ignored$" > .hgignore
 hg ci -A -m 'initial checkin' -d "1000000 0"
-sleep 1 # make sure mtime is changed
 touch modified added unknown ignored
 hg add added
 hg remove removed
--- a/tests/test-symlink-basic	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-symlink-basic	Fri Apr 04 23:13:32 2008 +0200
@@ -38,3 +38,13 @@
 hg cp -v dangling dangling2
 hg st -Cmard
 $TESTDIR/readlink.py dangling dangling2
+
+echo '% issue995'
+hg up -C
+mkdir dir
+ln -s dir dirlink
+hg ci -qAm 'add dirlink'
+mkdir newdir
+mv dir newdir/dir
+mv dirlink newdir/dirlink
+hg mv -A dirlink newdir/dirlink
--- a/tests/test-symlink-basic.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-symlink-basic.out	Fri Apr 04 23:13:32 2008 +0200
@@ -27,3 +27,5 @@
   dangling
 dangling -> void
 dangling2 -> void
+% issue995
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
--- a/tests/test-symlinks.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-symlinks.out	Fri Apr 04 23:13:32 2008 +0200
@@ -19,6 +19,7 @@
 ? a/b/c/demo
 adding a/b/c/demo
 2. clone it
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 # git symlink diff
 diff --git a/a/b/c/demo b/a/b/c/demo
--- a/tests/test-tag	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-tag	Fri Apr 04 23:13:32 2008 +0200
@@ -10,11 +10,21 @@
 
 echo foo >> .hgtags
 hg tag -d "1000000 0" "bleah2" || echo "failed"
-hg tag -d "1000000 0" -r 0 "bleah2" 1 || echo "failed"
 
 hg revert .hgtags
+hg tag -d "1000000 0" -r 0 x y z y y z || echo "failed"
+hg tag -d "1000000 0" tap nada dot tip null . || echo "failed"
+hg tag -d "1000000 0" "bleah" || echo "failed"
+hg tag -d "1000000 0" "blecch" "bleah" || echo "failed"
+
+hg tag -d "1000000 0" --remove "blecch" || echo "failed"
+hg tag -d "1000000 0" --remove "bleah" "blecch" "blough" || echo "failed"
+
 hg tag -d "1000000 0" -r 0 "bleah0"
-hg tag -l -d "1000000 0" "bleah1" 1
+hg tag -l -d "1000000 0" -r 1 "bleah1"
+hg tag -d "1000000 0" gack gawk gorp
+hg tag -d "1000000 0" -f gack
+hg tag -d "1000000 0" --remove gack gorp
 
 cat .hgtags
 cat .hg/localtags
--- a/tests/test-tag.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-tag.out	Fri Apr 04 23:13:32 2008 +0200
@@ -18,12 +18,26 @@
 
 abort: working copy of .hgtags is changed (please commit .hgtags manually)
 failed
-use of 'hg tag NAME [REV]' is deprecated, please use 'hg tag [-r REV] NAME' instead
-abort: use only one form to specify the revision
+abort: tag names must be unique
+failed
+abort: the name 'tip' is reserved
+failed
+abort: tag 'bleah' already exists (use -f to force)
 failed
-use of 'hg tag NAME [REV]' is deprecated, please use 'hg tag [-r REV] NAME' instead
+abort: tag 'bleah' already exists (use -f to force)
+failed
+abort: tag 'blecch' does not exist
+failed
+abort: tag 'blecch' does not exist
+failed
 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah
 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0
+868cc8fbb43b754ad09fa109885d243fc49adae7 gack
+868cc8fbb43b754ad09fa109885d243fc49adae7 gawk
+868cc8fbb43b754ad09fa109885d243fc49adae7 gorp
+3807bcf62c5614cb6c16436b514d7764ca5f1631 gack
+0000000000000000000000000000000000000000 gack
+0000000000000000000000000000000000000000 gorp
 3ecf002a1c572a2f3bb4e665417e60fca65bbd42 bleah1
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar
--- a/tests/test-tags.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-tags.out	Fri Apr 04 23:13:32 2008 +0200
@@ -13,6 +13,7 @@
 0acdaf898367+ first
 0acdaf898367+ first
 M a
+created new head
 8216907a933d tip
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
@@ -21,6 +22,7 @@
 tip                                6:e2174d339386
 first                              0:0acdaf898367
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 .hgtags@c071f74ab5eb, line 2: cannot parse entry
 .hgtags@c071f74ab5eb, line 4: node 'foo' is not well formed
 .hgtags@4ca6f1b1a68c, line 2: node 'x' is not well formed
@@ -40,6 +42,7 @@
 
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 tip                                4:36195b728445
 bar                                1:b204a97e6e8d
 changeset:   5:57e1983b4a60
@@ -50,7 +53,7 @@
 
 tip                                5:57e1983b4a60
 % remove nonexistent tag
-abort: tag foobar does not exist
+abort: tag 'foobar' does not exist
 changeset:   5:57e1983b4a60
 tag:         tip
 user:        test
@@ -60,9 +63,10 @@
 tip                                5:d8bb4d1eff25
 bar                                0:b409d9da318e
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+created new head
 tip                                6:b5ff9d142648
 bar                                0:b409d9da318e
-abort: a tag named bar already exists (use -f to force)
+abort: tag 'bar' already exists (use -f to force)
 tip                                6:b5ff9d142648
 bar                                0:b409d9da318e
 adding foo
@@ -72,8 +76,8 @@
 tip                                4:40af5d225513
 bar                                2:72b852876a42
 adding foo
-abort: localtag tag is local
-abort: globaltag tag is global
+abort: tag 'localtag' is not a global tag
+abort: tag 'globaltag' is not a local tag
 tip                                1:a0b6fe111088
 localtag                           0:bbd179dfa0a7 local
 globaltag                          0:bbd179dfa0a7
--- a/tests/test-transplant.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-transplant.out	Fri Apr 04 23:13:32 2008 +0200
@@ -2,6 +2,7 @@
 adding r2
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 adding b1
+created new head
 adding b2
 adding b3
 4  b3
@@ -9,6 +10,7 @@
 2 0:17ab29e464c6  b1
 1  r2
 0  r1
+updating working directory
 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
 % rebase b onto r1
@@ -26,6 +28,7 @@
 2 0:17ab29e464c6  b1
 1  r2
 0  r1
+updating working directory
 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
 % rebase b onto r1, skipping b2
@@ -46,6 +49,7 @@
 adding manifests
 adding file changes
 added 2 changesets with 2 changes to 2 files
+updating working directory
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 searching for changes
 applying 37a1297eb21b
@@ -71,6 +75,7 @@
 0  r1
 % skip local changes transplanted to the source
 adding b4
+updating working directory
 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
 searching for changes
 applying 4333daefcb15
@@ -81,6 +86,7 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 searching for changes
 searching for changes
@@ -100,6 +106,7 @@
 removing toremove
 adding bar
 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+created new head
 applying a1e30dd1b8e7
 patching file foo
 Hunk #1 FAILED at 0
--- a/tests/test-up-local-change.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-up-local-change.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,4 +1,5 @@
 adding a
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 diff -r 33aaa84a386b a
@@ -95,13 +96,14 @@
 +abc
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 adding b
+created new head
 M a
 changeset:   1:802f095af299
 user:        test
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     2
 
-abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
+abort: crosses branches (use 'hg merge' or 'hg update -C' to discard changes)
 failed
 abort: outstanding uncommitted changes
 failed
--- a/tests/test-update-reverse.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-update-reverse.out	Fri Apr 04 23:13:32 2008 +0200
@@ -1,6 +1,7 @@
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 Main should be gone
 a
+created new head
 changeset:   3:ded32b0db104
 tag:         tip
 user:        test
--- a/tests/test-url-rev.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-url-rev.out	Fri Apr 04 23:13:32 2008 +0200
@@ -5,6 +5,7 @@
 adding manifests
 adding file changes
 added 2 changesets with 2 changes to 1 files
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % heads
 changeset:   1:cd2a86ecc814
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-walkrepo.py	Fri Apr 04 23:13:32 2008 +0200
@@ -0,0 +1,53 @@
+import os
+import os.path
+from mercurial import hg, ui
+from mercurial.util import walkrepos, set, frozenset
+from os import mkdir, chdir
+from os.path import join as pjoin
+
+u = ui.ui()
+sym = hasattr(os, 'symlink') and hasattr(os.path, 'samestat')
+
+hg.repository(u, 'top1', create=1)
+mkdir('subdir')
+chdir('subdir')
+hg.repository(u, 'sub1', create=1)
+mkdir('subsubdir')
+chdir('subsubdir')
+hg.repository(u, 'subsub1', create=1)
+chdir(os.path.pardir)
+if sym:
+    os.symlink(os.path.pardir, 'circle')
+    os.symlink(pjoin('subsubdir', 'subsub1'), 'subsub1')
+
+def runtest():
+    reposet = frozenset(walkrepos('.', followsym=True))
+    if sym and (len(reposet) != 3):
+        print "reposet = %r" % (reposet,)
+        raise SystemExit(1, "Found %d repositories when I should have found 3" % (len(reposet),))
+    if (not sym) and (len(reposet) != 2):
+        print "reposet = %r" % (reposet,)
+        raise SystemExit(1, "Found %d repositories when I should have found 2" % (len(reposet),))
+    sub1set = frozenset((pjoin('.', 'sub1'),
+                         pjoin('.', 'circle', 'subdir', 'sub1')))
+    if len(sub1set & reposet) != 1:
+        print "sub1set = %r" % (sub1set,)
+        print "reposet = %r" % (reposet,)
+        raise SystemExit(1, "sub1set and reposet should have exactly one path in common.")
+    sub2set = frozenset((pjoin('.', 'subsub1'),
+                         pjoin('.', 'subsubdir', 'subsub1')))
+    if len(sub2set & reposet) != 1:
+        print "sub2set = %r" % (sub2set,)
+        print "reposet = %r" % (reposet,)
+        raise SystemExit(1, "sub1set and reposet should have exactly one path in common.")
+    sub3 = pjoin('.', 'circle', 'top1')
+    if sym and not (sub3 in reposet):
+        print "reposet = %r" % (reposet,)
+        raise SystemExit(1, "Symbolic links are supported and %s is not in reposet" % (sub3,))
+
+runtest()
+if sym:
+    # Simulate not having symlinks.
+    del os.path.samestat
+    sym = False
+    runtest()
--- a/tests/test-win32text	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-win32text	Fri Apr 04 23:13:32 2008 +0200
@@ -99,3 +99,17 @@
 hg ci -m 7 -d'0 0'
 python print.py < f5.sh
 hg cat f5.sh | python print.py
+
+echo '% just linefeed' > linefeed
+hg ci -qAm 8 linefeed
+python print.py < linefeed
+hg cat linefeed | python print.py
+hg st -q
+hg revert -a linefeed
+python print.py < linefeed
+hg st -q
+echo modified >> linefeed
+hg st -q
+hg revert -a
+hg st -q
+python print.py < linefeed
--- a/tests/test-win32text.out	Fri Apr 04 23:09:54 2008 +0200
+++ b/tests/test-win32text.out	Fri Apr 04 23:13:32 2008 +0200
@@ -44,6 +44,7 @@
 
 
 
+updating working directory
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 adding dupe/a
@@ -177,3 +178,10 @@
 
 # empty<CR><LF>
 # empty<LF>
+% just linefeed<LF>
+% just linefeed<LF>
+no changes needed to linefeed
+% just linefeed<LF>
+M linefeed
+reverting linefeed
+% just linefeed<CR><LF>