changeset 11161:a2c32edc407b

Merge with i18n
author Matt Mackall <mpm@selenic.com>
date Tue, 11 May 2010 17:12:10 -0500
parents b203a95fe68b (diff) 8c8a713f6dac (current diff)
children d6f378562397
files
diffstat 52 files changed, 910 insertions(+), 225 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/mergetools.hgrc	Sun May 09 00:15:13 2010 +0200
+++ b/contrib/mergetools.hgrc	Tue May 11 17:12:10 2010 -0500
@@ -13,7 +13,7 @@
 gvimdiff.priority=-9
 
 merge.checkconflicts=True
-merge.priority=-10
+merge.priority=-100
 
 gpyfm.gui=True
 
--- a/doc/hgrc.5.txt	Sun May 09 00:15:13 2010 +0200
+++ b/doc/hgrc.5.txt	Tue May 11 17:12:10 2010 -0500
@@ -454,7 +454,8 @@
   Default: ``$local $base $other``
 ``premerge``
   Attempt to run internal non-interactive 3-way merge tool before
-  launching external tool.
+  launching external tool.  Options are ``true``, ``false``, or ``keep``
+  to leave markers in the file if the premerge fails.
   Default: True
 ``binary``
   This tool can merge binary files. Defaults to False, unless tool
@@ -462,13 +463,21 @@
 ``symlink``
   This tool can merge symlinks. Defaults to False, even if tool was
   selected by file pattern match.
-``checkconflicts``
-  Check whether there are conflicts even though the tool reported
-  success.
+``check``
+  A list of merge success-checking options:
+
+  ``changed``
+    Ask whether merge was successful when the merged file shows no changes.
+  ``conflicts``
+    Check whether there are conflicts even though the tool reported success.
+  ``prompt``
+    Always prompt for merge success, regardless of success reported by tool.
+
+``checkchanged``
+  True is equivalent to ``check = changed``.
   Default: False
-``checkchanged``
-  Check whether outputs were written even though the tool reported
-  success.
+``checkconflicts``
+  True is equivalent to ``check = conflicts``.
   Default: False
 ``fixeol``
   Attempt to fix up EOL changes caused by the merge tool.
--- a/hgext/acl.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/acl.py	Tue May 11 17:12:10 2010 -0500
@@ -80,9 +80,11 @@
   pretxnchangegroup.acl = python:hgext.acl.hook
 
   [acl]
-  # Check whether the source of incoming changes is in this list where
-  # "serve" == ssh or http, and "push", "pull" and "bundle" are the
-  # corresponding hg commands.
+  # Allow or deny access for incoming changes only if their source is
+  # listed here, let them pass otherwise. Source is "serve" for all
+  # remote access (http or ssh), "push", "pull" or "bundle" when the
+  # related commands are run locally.
+  # Default: serve
   sources = serve
 
   [acl.deny.branches] 
@@ -126,7 +128,7 @@
   src/main/resources/DONT-TOUCH-THIS.txt = *
 
   [acl.allow]
-  # if acl.allow not present, all users allowed by default
+  # if acl.allow is not present, all users are allowed by default
   # empty acl.allow = no users allowed
 
   # User "doc_writer" has write access to any file under the "docs"
@@ -148,7 +150,7 @@
 
 from mercurial.i18n import _
 from mercurial import util, match
-import getpass, urllib, grp
+import getpass, urllib
 
 def _getusers(ui, group):
 
@@ -159,7 +161,10 @@
 
     ui.debug('acl: "%s" not defined in [acl.groups]\n' % group)
     # If no users found in group definition, get users from OS-level group
-    return grp.getgrnam(group).gr_mem
+    try:
+        return util.groupmembers(group)
+    except KeyError:
+        raise util.Abort(_("group '%s' is undefined") % group)
 
 def _usermatch(ui, user, usersorgroups):
 
--- a/hgext/convert/bzr.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/bzr.py	Tue May 11 17:12:10 2010 -0500
@@ -109,18 +109,16 @@
             # the file is not available anymore - was deleted
             raise IOError(_('%s is not available in %s anymore') %
                     (name, rev))
+        mode = self._modecache[(name, rev)]
         if kind == 'symlink':
             target = revtree.get_symlink_target(fileid)
             if target is None:
                 raise util.Abort(_('%s.%s symlink has no target')
                                  % (name, rev))
-            return target
+            return target, mode
         else:
             sio = revtree.get_file(fileid)
-            return sio.read()
-
-    def getmode(self, name, rev):
-        return self._modecache[(name, rev)]
+            return sio.read(), mode
 
     def getchanges(self, version):
         # set up caches: modecache and revtree
--- a/hgext/convert/common.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/common.py	Tue May 11 17:12:10 2010 -0500
@@ -77,15 +77,10 @@
         raise NotImplementedError()
 
     def getfile(self, name, rev):
-        """Return file contents as a string. rev is the identifier returned
-        by a previous call to getchanges(). Raise IOError to indicate that
-        name was deleted in rev.
-        """
-        raise NotImplementedError()
-
-    def getmode(self, name, rev):
-        """Return file mode, eg. '', 'x', or 'l'. rev is the identifier
-        returned by a previous call to getchanges().
+        """Return a pair (data, mode) where data is the file content
+        as a string and mode one of '', 'x' or 'l'. rev is the
+        identifier returned by a previous call to getchanges(). Raise
+        IOError to indicate that name was deleted in rev.
         """
         raise NotImplementedError()
 
@@ -192,8 +187,8 @@
         changeset.  'files' is a list of (path, version) tuples,
         'copies' is a dictionary mapping destinations to sources,
         'source' is the source repository, and 'revmap' is a mapfile
-        of source revisions to converted revisions. Only getfile(),
-        getmode(), and lookuprev() should be called on 'source'.
+        of source revisions to converted revisions. Only getfile() and
+        lookuprev() should be called on 'source'.
 
         Note that the sink repository is not told to update itself to
         a particular revision (or even what that revision would be)
--- a/hgext/convert/convcmd.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/convcmd.py	Tue May 11 17:12:10 2010 -0500
@@ -72,6 +72,25 @@
             ui.note(_("convert: %s\n") % inst)
     raise util.Abort(_('%s: unknown repository type') % path)
 
+class progresssource(object):
+    def __init__(self, ui, source, filecount):
+        self.ui = ui
+        self.source = source
+        self.filecount = filecount
+        self.retrieved = 0
+
+    def getfile(self, file, rev):
+        self.retrieved += 1
+        self.ui.progress(_('retrieving file'), self.retrieved,
+                         item=file, total=self.filecount)
+        return self.source.getfile(file, rev)
+
+    def lookuprev(self, rev):
+        return self.source.lookuprev(rev)
+
+    def close(self):
+        self.ui.progress(_('retrieving file'), None)
+
 class converter(object):
     def __init__(self, ui, source, dest, revmapfile, opts):
 
@@ -111,11 +130,13 @@
             if n in known or n in self.map:
                 continue
             known.add(n)
+            self.ui.progress(_('scanning'), len(known), unit=_('revisions'))
             commit = self.cachecommit(n)
             parents[n] = []
             for p in commit.parents:
                 parents[n].append(p)
                 visit.append(p)
+        self.ui.progress(_('scanning'), None)
 
         return parents
 
@@ -302,8 +323,10 @@
             parents = [self.map.get(p, p) for p in parents]
         except KeyError:
             parents = [b[0] for b in pbranches]
+        source = progresssource(self.ui, self.source, len(files))
         newnode = self.dest.putcommit(files, copies, parents, commit,
-                                      self.source, self.map)
+                                      source, self.map)
+        source.close()
         self.source.converted(rev, newnode)
         self.map[rev] = newnode
 
@@ -321,7 +344,7 @@
             c = None
 
             self.ui.status(_("converting...\n"))
-            for c in t:
+            for i, c in enumerate(t):                
                 num -= 1
                 desc = self.commitcache[c].desc
                 if "\n" in desc:
@@ -331,7 +354,10 @@
                 # 'utf-8'
                 self.ui.status("%d %s\n" % (num, recode(desc)))
                 self.ui.note(_("source: %s\n") % recode(c))
+                self.ui.progress(_('converting'), i, unit=_('revisions'),
+                                 total=len(t))
                 self.copy(c)
+            self.ui.progress(_('converting'), None)
 
             tags = self.source.gettags()
             ctags = {}
--- a/hgext/convert/cvs.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/cvs.py	Tue May 11 17:12:10 2010 -0500
@@ -200,7 +200,7 @@
         self._parse()
         return self.heads
 
-    def _getfile(self, name, rev):
+    def getfile(self, name, rev):
 
         def chunkedread(fp, count):
             # file-objects returned by socked.makefile() do not handle
@@ -216,6 +216,7 @@
                 output.write(data)
             return output.getvalue()
 
+        self._parse()
         if rev.endswith("(DEAD)"):
             raise IOError
 
@@ -255,18 +256,8 @@
                 else:
                     raise util.Abort(_("unknown CVS response: %s") % line)
 
-    def getfile(self, file, rev):
-        self._parse()
-        data, mode = self._getfile(file, rev)
-        self.modecache[(file, rev)] = mode
-        return data
-
-    def getmode(self, file, rev):
-        return self.modecache[(file, rev)]
-
     def getchanges(self, rev):
         self._parse()
-        self.modecache = {}
         return sorted(self.files[rev].iteritems()), {}
 
     def getcommit(self, rev):
--- a/hgext/convert/darcs.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/darcs.py	Tue May 11 17:12:10 2010 -0500
@@ -157,11 +157,11 @@
     def getfile(self, name, rev):
         if rev != self.lastrev:
             raise util.Abort(_('internal calling inconsistency'))
-        return open(os.path.join(self.tmppath, name), 'rb').read()
-
-    def getmode(self, name, rev):
-        mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
-        return (mode & 0111) and 'x' or ''
+        path = os.path.join(self.tmppath, name)
+        data = open(path, 'rb').read()
+        mode = os.lstat(path).st_mode
+        mode = (mode & 0111) and 'x' or ''
+        return data, mode
 
     def gettags(self):
         return self.tags
--- a/hgext/convert/filemap.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/filemap.py	Tue May 11 17:12:10 2010 -0500
@@ -100,8 +100,7 @@
 #
 # - Filter and rename files.  This is mostly wrapped by the filemapper
 #   class above. We hide the original filename in the revision that is
-#   returned by getchanges to be able to find things later in getfile
-#   and getmode.
+#   returned by getchanges to be able to find things later in getfile.
 #
 # - Return only revisions that matter for the files we're interested in.
 #   This involves rewriting the parents of the original revision to
@@ -318,10 +317,9 @@
         self.convertedorder.append((rev, True, None))
         self._discard(*parents)
 
-        # Get the real changes and do the filtering/mapping.
-        # To be able to get the files later on in getfile and getmode,
-        # we hide the original filename in the rev part of the return
-        # value.
+        # Get the real changes and do the filtering/mapping. To be
+        # able to get the files later on in getfile, we hide the
+        # original filename in the rev part of the return value.
         changes, copies = self.base.getchanges(rev)
         newnames = {}
         files = []
@@ -345,10 +343,6 @@
         realname, realrev = rev
         return self.base.getfile(realname, realrev)
 
-    def getmode(self, name, rev):
-        realname, realrev = rev
-        return self.base.getmode(realname, realrev)
-
     def gettags(self):
         return self.base.gettags()
 
--- a/hgext/convert/git.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/git.py	Tue May 11 17:12:10 2010 -0500
@@ -67,10 +67,9 @@
         return data
 
     def getfile(self, name, rev):
-        return self.catfile(rev, "blob")
-
-    def getmode(self, name, rev):
-        return self.modecache[(name, rev)]
+        data = self.catfile(rev, "blob")
+        mode = self.modecache[(name, rev)]
+        return data, mode
 
     def getchanges(self, version):
         self.modecache = {}
--- a/hgext/convert/gnuarch.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/gnuarch.py	Tue May 11 17:12:10 2010 -0500
@@ -54,7 +54,6 @@
         self.changes = {}
         self.parents = {}
         self.tags = {}
-        self.modecache = {}
         self.catlogparser = Parser()
         self.locale = locale.getpreferredencoding()
         self.archives = []
@@ -142,16 +141,9 @@
         if not os.path.exists(os.path.join(self.tmppath, name)):
             raise IOError
 
-        data, mode = self._getfile(name, rev)
-        self.modecache[(name, rev)] = mode
-
-        return data
-
-    def getmode(self, name, rev):
-        return self.modecache[(name, rev)]
+        return self._getfile(name, rev)
 
     def getchanges(self, rev):
-        self.modecache = {}
         self._update(rev)
         changes = []
         copies = {}
--- a/hgext/convert/hg.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/hg.py	Tue May 11 17:12:10 2010 -0500
@@ -134,11 +134,11 @@
         files = dict(files)
         def getfilectx(repo, memctx, f):
             v = files[f]
-            data = source.getfile(f, v)
-            e = source.getmode(f, v)
+            data, mode = source.getfile(f, v)
             if f == '.hgtags':
                 data = self._rewritetags(source, revmap, data)
-            return context.memfilectx(f, data, 'l' in e, 'x' in e, copies.get(f))
+            return context.memfilectx(f, data, 'l' in mode, 'x' in mode,
+                                      copies.get(f))
 
         pl = []
         for p in parents:
@@ -266,13 +266,11 @@
 
     def getfile(self, name, rev):
         try:
-            return self.changectx(rev)[name].data()
+            fctx = self.changectx(rev)[name]
+            return fctx.data(), fctx.flags()
         except error.LookupError, err:
             raise IOError(err)
 
-    def getmode(self, name, rev):
-        return self.changectx(rev).manifest().flags(name)
-
     def getchanges(self, rev):
         ctx = self.changectx(rev)
         parents = self.parents(ctx)
--- a/hgext/convert/monotone.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/monotone.py	Tue May 11 17:12:10 2010 -0500
@@ -192,18 +192,16 @@
 
         return (files.items(), copies)
 
-    def getmode(self, name, rev):
-        self.mtnloadmanifest(rev)
-        node, attr = self.files.get(name, (None, ""))
-        return attr
-
     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)
+            data = self.mtnrun("get_file_of", name, r=rev)
         except:
             raise IOError() # file was deleted or renamed
+        self.mtnloadmanifest(rev)
+        node, attr = self.files.get(name, (None, ""))
+        return data, attr
 
     def getcommit(self, rev):
         certs   = self.mtngetcerts(rev)
--- a/hgext/convert/p4.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/p4.py	Tue May 11 17:12:10 2010 -0500
@@ -41,7 +41,6 @@
         self.parent = {}
         self.encoding = "latin_1"
         self.depotname = {}           # mapping from local name to depot name
-        self.modecache = {}
         self.re_type = re.compile(
             "([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)"
             "(\+\w+)?$")
@@ -183,17 +182,12 @@
         if mode is None:
             raise IOError(0, "bad stat")
 
-        self.modecache[(name, rev)] = mode
-
         if keywords:
             contents = keywords.sub("$\\1$", contents)
         if mode == "l" and contents.endswith("\n"):
             contents = contents[:-1]
 
-        return contents
-
-    def getmode(self, name, rev):
-        return self.modecache[(name, rev)]
+        return contents, mode
 
     def getchanges(self, rev):
         return self.files[rev], {}
--- a/hgext/convert/subversion.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/convert/subversion.py	Tue May 11 17:12:10 2010 -0500
@@ -371,22 +371,13 @@
 
         return self.heads
 
-    def getfile(self, file, rev):
-        data, mode = self._getfile(file, rev)
-        self.modecache[(file, rev)] = mode
-        return data
-
-    def getmode(self, file, rev):
-        return self.modecache[(file, rev)]
-
     def getchanges(self, rev):
         if self._changescache and self._changescache[0] == rev:
             return self._changescache[1]
         self._changescache = None
-        self.modecache = {}
         (paths, parents) = self.paths[rev]
         if parents:
-            files, copies = self.expandpaths(rev, paths, parents)
+            files, self.removed, copies = self.expandpaths(rev, paths, parents)
         else:
             # Perform a full checkout on roots
             uuid, module, revnum = self.revsplit(rev)
@@ -395,6 +386,7 @@
             files = [n for n, e in entries.iteritems()
                      if e.kind == svn.core.svn_node_file]
             copies = {}
+            self.removed = set()
 
         files.sort()
         files = zip(files, [rev] * len(files))
@@ -610,10 +602,7 @@
         return prevmodule
 
     def expandpaths(self, rev, paths, parents):
-        entries = []
-        # Map of entrypath, revision for finding source of deleted
-        # revisions.
-        copyfrom = {}
+        changed, removed = set(), set()
         copies = {}
 
         new_module, revnum = self.revsplit(rev)[1:]
@@ -621,12 +610,14 @@
             self.module = new_module
             self.reparent(self.module)
 
-        for path, ent in paths:
+        for i, (path, ent) in enumerate(paths):
+            self.ui.progress(_('scanning paths'), i, item=path,
+                             total=len(paths))
             entrypath = self.getrelpath(path)
 
             kind = self._checkpath(entrypath, revnum)
             if kind == svn.core.svn_node_file:
-                entries.append(self.recode(entrypath))
+                changed.add(self.recode(entrypath))
                 if not ent.copyfrom_path or not parents:
                     continue
                 # Copy sources not in parent revisions cannot be
@@ -644,54 +635,39 @@
                 self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
                 pmodule, prevnum = self.revsplit(parents[0])[1:]
                 parentpath = pmodule + "/" + entrypath
-                self.ui.debug("entry %s\n" % parentpath)
-
-                # We can avoid the reparent calls if the module has
-                # not changed but it probably does not worth the pain.
-                prevmodule = self.reparent('')
-                fromkind = svn.ra.check_path(self.ra, parentpath.strip('/'),
-                                             prevnum)
-                self.reparent(prevmodule)
+                fromkind = self._checkpath(entrypath, prevnum, pmodule)
 
                 if fromkind == svn.core.svn_node_file:
-                    entries.append(self.recode(entrypath))
+                    removed.add(self.recode(entrypath))
                 elif fromkind == svn.core.svn_node_dir:
-                    if ent.action == 'C':
-                        children = self._find_children(path, prevnum)
-                    else:
-                        oroot = parentpath.strip('/')
-                        nroot = path.strip('/')
-                        children = self._find_children(oroot, prevnum)
-                        children = [s.replace(oroot, nroot) for s in children]
-
-                    for child in children:
-                        childpath = self.getrelpath("/" + child, pmodule)
-                        if not childpath:
-                            continue
-                        if childpath in copies:
-                            del copies[childpath]
-                        entries.append(childpath)
+                    oroot = parentpath.strip('/')
+                    nroot = path.strip('/')
+                    children = self._iterfiles(oroot, prevnum)
+                    for childpath in children:
+                        childpath = childpath.replace(oroot, nroot)
+                        childpath = self.getrelpath("/" + childpath, pmodule)
+                        if childpath:
+                            removed.add(self.recode(childpath))
                 else:
                     self.ui.debug('unknown path in revision %d: %s\n' % \
                                   (revnum, path))
-            elif kind == svn.core.svn_node_dir:
-                # If the directory just had a prop change,
-                # then we shouldn't need to look for its children.
+            elif kind == svn.core.svn_node_dir:                
                 if ent.action == 'M':
+                    # If the directory just had a prop change,
+                    # then we shouldn't need to look for its children.
                     continue
+                elif ent.action == 'R' and parents:
+                    # If a directory is replacing a file, mark the previous
+                    # file as deleted
+                    pmodule, prevnum = self.revsplit(parents[0])[1:]
+                    pkind = self._checkpath(entrypath, prevnum, pmodule)
+                    if pkind == svn.core.svn_node_file:
+                        removed.add(self.recode(entrypath))
 
-                children = sorted(self._find_children(path, revnum))
-                for child in children:
-                    # Can we move a child directory and its
-                    # parent in the same commit? (probably can). Could
-                    # cause problems if instead of revnum -1,
-                    # we have to look in (copyfrom_path, revnum - 1)
-                    entrypath = self.getrelpath("/" + child)
-                    if entrypath:
-                        # Need to filter out directories here...
-                        kind = self._checkpath(entrypath, revnum)
-                        if kind != svn.core.svn_node_dir:
-                            entries.append(self.recode(entrypath))
+                for childpath in self._iterfiles(path, revnum):
+                    childpath = self.getrelpath("/" + childpath)
+                    if childpath:
+                        changed.add(self.recode(childpath))
 
                 # Handle directory copies
                 if not ent.copyfrom_path or not parents:
@@ -704,20 +680,20 @@
                 copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule)
                 if not copyfrompath:
                     continue
-                copyfrom[path] = ent
                 self.ui.debug("mark %s came from %s:%d\n"
                               % (path, copyfrompath, ent.copyfrom_rev))
-                children = self._find_children(ent.copyfrom_path, ent.copyfrom_rev)
-                children.sort()
-                for child in children:
-                    entrypath = self.getrelpath("/" + child, pmodule)
-                    if not entrypath:
+                children = self._iterfiles(ent.copyfrom_path, ent.copyfrom_rev)
+                for childpath in children:
+                    childpath = self.getrelpath("/" + childpath, pmodule)
+                    if not childpath:
                         continue
-                    copytopath = path + entrypath[len(copyfrompath):]
+                    copytopath = path + childpath[len(copyfrompath):]
                     copytopath = self.getrelpath(copytopath)
-                    copies[self.recode(copytopath)] = self.recode(entrypath)
+                    copies[self.recode(copytopath)] = self.recode(childpath)
 
-        return (list(set(entries)), copies)
+        self.ui.progress(_('scanning paths'), None)
+        changed.update(removed)
+        return (list(changed), removed, copies)
 
     def _fetch_revisions(self, from_revnum, to_revnum):
         if from_revnum < to_revnum:
@@ -844,8 +820,10 @@
                 raise util.Abort(_('svn: branch has no revision %s') % to_revnum)
             raise
 
-    def _getfile(self, file, rev):
+    def getfile(self, file, rev):
         # TODO: ra.get_file transmits the whole file instead of diffs.
+        if file in self.removed:
+            raise IOError()         
         mode = ''
         try:
             new_module, revnum = self.revsplit(rev)[1:]
@@ -874,12 +852,14 @@
                 data = data[len(link_prefix):]
         return data, mode
 
-    def _find_children(self, path, revnum):
+    def _iterfiles(self, path, revnum):
+        """Enumerate all files in path at revnum, recursively."""
         path = path.strip('/')
         pool = Pool()
         rpath = '/'.join([self.baseurl, urllib.quote(path)]).strip('/')
-        return ['%s/%s' % (path, x) for x in
-                svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()]
+        entries = svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool)            
+        return ((path + '/' + p) for p, e in entries.iteritems()
+                if e.kind == svn.core.svn_node_file)
 
     def getrelpath(self, path, module=None):
         if module is None:
@@ -901,11 +881,18 @@
         self.ui.debug('%r is not under %r, ignoring\n' % (path, module))
         return None
 
-    def _checkpath(self, path, revnum):
-        # ra.check_path does not like leading slashes very much, it leads
-        # to PROPFIND subversion errors
-        return svn.ra.check_path(self.ra, path.strip('/'), revnum)
-
+    def _checkpath(self, path, revnum, module=None):
+        if module is not None:
+            prevmodule = self.reparent('')
+            path = module + '/' + path
+        try:
+            # ra.check_path does not like leading slashes very much, it leads
+            # to PROPFIND subversion errors
+            return svn.ra.check_path(self.ra, path.strip('/'), revnum)
+        finally:
+            if module is not None:
+                self.reparent(prevmodule)
+    
     def _getlog(self, paths, start, end, limit=0, discover_changed_paths=True,
                 strict_node_history=False):
         # Normalize path names, svn >= 1.5 only wants paths relative to
@@ -1107,12 +1094,11 @@
         # Apply changes to working copy
         for f, v in files:
             try:
-                data = source.getfile(f, v)
+                data, mode = source.getfile(f, v)
             except IOError:
                 self.delete.append(f)
             else:
-                e = source.getmode(f, v)
-                self.putfile(f, e, data)
+                self.putfile(f, mode, data)
                 if f in copies:
                     self.copies.append([copies[f], f])
         files = [f[0] for f in files]
--- a/hgext/patchbomb.py	Sun May 09 00:15:13 2010 +0200
+++ b/hgext/patchbomb.py	Tue May 11 17:12:10 2010 -0500
@@ -34,6 +34,7 @@
   to = recipient1, recipient2, ...
   cc = cc1, cc2, ...
   bcc = bcc1, bcc2, ...
+  reply-to = address1, address2, ...
 
 Use ``[patchbomb]`` as configuration section name if you need to
 override global ``[email]`` address settings.
@@ -390,8 +391,9 @@
         msgs = getpatchmsgs(list(getpatches(revs)))
 
     def getaddrs(opt, prpt=None, default=None):
-        if opts.get(opt):
-            return mail.addrlistencode(ui, opts.get(opt), _charsets,
+        addrs = opts.get(opt.replace('-', '_'))
+        if addrs:
+            return mail.addrlistencode(ui, addrs, _charsets,
                                        opts.get('test'))
 
         addrs = (ui.config('email', opt) or
@@ -404,6 +406,7 @@
     to = getaddrs('to', 'To')
     cc = getaddrs('cc', 'Cc', '')
     bcc = getaddrs('bcc')
+    replyto = getaddrs('reply-to')
 
     ui.write('\n')
 
@@ -442,6 +445,8 @@
             m['Cc']  = ', '.join(cc)
         if bcc:
             m['Bcc'] = ', '.join(bcc)
+        if replyto:
+            m['Reply-To'] = ', '.join(replyto)
         if opts.get('test'):
             ui.status(_('Displaying '), subj, ' ...\n')
             ui.flush()
@@ -493,6 +498,7 @@
           ('n', 'test', None, _('print messages that would be sent')),
           ('m', 'mbox', '',
            _('write messages to mbox file instead of sending them')),
+          ('', 'reply-to', [], _('email addresses replies should be sent to')),
           ('s', 'subject', '',
            _('subject of first message (intro or single patch)')),
           ('', 'in-reply-to', '',
--- a/mercurial/bundlerepo.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/bundlerepo.py	Tue May 11 17:12:10 2010 -0500
@@ -166,7 +166,7 @@
             localrepo.localrepository.__init__(self, ui, self._tempparent)
 
         if path:
-            self._url = 'bundle:' + path + '+' + bundlename
+            self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
         else:
             self._url = 'bundle:' + bundlename
 
--- a/mercurial/cmdutil.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/cmdutil.py	Tue May 11 17:12:10 2010 -0500
@@ -396,6 +396,12 @@
 
         if after:
             if not exists:
+                if rename:
+                    ui.warn(_('%s: not recording move - %s does not exist\n') %
+                            (relsrc, reltarget))
+                else:
+                    ui.warn(_('%s: not recording copy - %s does not exist\n') %
+                            (relsrc, reltarget))
                 return
         elif not dryrun:
             try:
--- a/mercurial/context.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/context.py	Tue May 11 17:12:10 2010 -0500
@@ -724,7 +724,7 @@
         def findflag(ctx):
             mnode = ctx.changeset()[0]
             node, flag = self._repo.manifest.find(mnode, orig)
-            ff = self._repo.dirstate.flagfunc(lambda x: flag or None)
+            ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
             try:
                 return ff(path)
             except OSError:
@@ -935,12 +935,16 @@
         """get a file context from the working directory"""
         return self._filectxfn(self._repo, self, path)
 
+    def commit(self):
+        """commit context to the repo"""
+        return self._repo.commitctx(self)
+
 class memfilectx(object):
     """memfilectx represents an in-memory file to commit.
 
     See memctx for more details.
     """
-    def __init__(self, path, data, islink, isexec, copied):
+    def __init__(self, path, data, islink=False, isexec=False, copied=None):
         """
         path is the normalized file path relative to repository root.
         data is the file content as a string.
--- a/mercurial/filemerge.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/filemerge.py	Tue May 11 17:12:10 2010 -0500
@@ -7,7 +7,7 @@
 
 from node import short
 from i18n import _
-import util, simplemerge, match
+import util, simplemerge, match, error
 import os, tempfile, re, filecmp
 
 def _toolstr(ui, tool, part, default=""):
@@ -16,6 +16,9 @@
 def _toolbool(ui, tool, part, default=False):
     return ui.configbool("merge-tools", tool + "." + part, default)
 
+def _toollist(ui, tool, part, default=[]):
+    return ui.configlist("merge-tools", tool + "." + part, default)
+
 _internal = ['internal:' + s
              for s in 'fail local other merge prompt dump'.split()]
 
@@ -176,7 +179,18 @@
     ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
 
     # do we attempt to simplemerge first?
-    if _toolbool(ui, tool, "premerge", not (binary or symlink)):
+    try:
+        premerge = _toolbool(ui, tool, "premerge", not (binary or symlink))
+    except error.ConfigError:
+        premerge = _toolstr(ui, tool, "premerge").lower()
+        valid = 'keep'.split()
+        if premerge not in valid:
+            _valid = ', '.join(["'" + v + "'" for v in valid])
+            raise error.ConfigError(_("%s.premerge not valid "
+                                      "('%s' is neither boolean nor %s)") %
+                                    (tool, premerge, _valid))
+
+    if premerge:
         r = simplemerge.simplemerge(ui, a, b, c, quiet=True)
         if not r:
             ui.debug(" premerge successful\n")
@@ -184,7 +198,8 @@
             os.unlink(b)
             os.unlink(c)
             return 0
-        util.copyfile(back, a) # restore from backup and try again
+        if premerge != 'keep':
+            util.copyfile(back, a) # restore from backup and try again
 
     env = dict(HG_FILE=fd,
                HG_MY_NODE=short(mynode),
@@ -211,11 +226,20 @@
             lambda x: '"%s"' % util.localpath(replace[x.group()[1:]]), args)
         r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env)
 
-    if not r and _toolbool(ui, tool, "checkconflicts"):
+    if not r and (_toolbool(ui, tool, "checkconflicts") or
+                  'conflicts' in _toollist(ui, tool, "check")):
         if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()):
             r = 1
 
-    if not r and _toolbool(ui, tool, "checkchanged"):
+    checked = False
+    if 'prompt' in _toollist(ui, tool, "check"):
+        checked = True
+        if ui.promptchoice(_("was merge of '%s' successful (yn)?") % fd,
+                           (_("&Yes"), _("&No")), 1):
+            r = 1
+
+    if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
+                                  'changed' in _toollist(ui, tool, "check")):
         if filecmp.cmp(repo.wjoin(fd), back):
             if ui.promptchoice(_(" output file %s appears unchanged\n"
                                  "was merge successful (yn)?") % fd,
--- a/mercurial/hg.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/hg.py	Tue May 11 17:12:10 2010 -0500
@@ -15,8 +15,8 @@
 import errno, os, shutil
 
 def _local(path):
-    return (os.path.isfile(util.drop_scheme('file', path)) and
-            bundlerepo or localrepo)
+    path = util.expandpath(util.drop_scheme('file', path))
+    return (os.path.isfile(path) and bundlerepo or localrepo)
 
 def addbranchrevs(lrepo, repo, branches, revs):
     if not branches:
--- a/mercurial/httprepo.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/httprepo.py	Tue May 11 17:12:10 2010 -0500
@@ -208,6 +208,12 @@
         return util.chunkbuffer(zgenerator(f))
 
     def unbundle(self, cg, heads, source):
+        '''Send cg (a readable file-like object representing the
+        changegroup to push, typically a chunkbuffer object) to the
+        remote server as a bundle. Return an integer response code:
+        non-zero indicates a successful push (see
+        localrepository.addchangegroup()), and zero indicates either
+        error or nothing to push.'''
         # have to stream bundle to a temp file because we do not have
         # http 1.1 chunked transfer.
 
--- a/mercurial/localrepo.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/localrepo.py	Tue May 11 17:12:10 2010 -0500
@@ -25,7 +25,7 @@
 
     def __init__(self, baseui, path=None, create=0):
         repo.repository.__init__(self)
-        self.root = os.path.realpath(path)
+        self.root = os.path.realpath(util.expandpath(path))
         self.path = os.path.join(self.root, ".hg")
         self.origroot = path
         self.opener = util.opener(self.path)
@@ -1507,6 +1507,13 @@
             lock.release()
 
     def push(self, remote, force=False, revs=None):
+        '''Push outgoing changesets (limited by revs) from the current
+        repository to remote. Return an integer:
+          - 0 means HTTP error *or* nothing to push
+          - 1 means we pushed and remote head count is unchanged *or*
+            we have outgoing changesets but refused to push
+          - other values as described by addchangegroup()
+        '''
         # there are two ways to push to remote repo:
         #
         # addchangegroup assumes local user can lock remote
@@ -1521,11 +1528,18 @@
 
     def prepush(self, remote, force, revs):
         '''Analyze the local and remote repositories and determine which
-        changesets need to be pushed to the remote.  Return a tuple
-        (changegroup, remoteheads).  changegroup is a readable file-like
-        object whose read() returns successive changegroup chunks ready to
-        be sent over the wire.  remoteheads is the list of remote heads.
-        '''
+        changesets need to be pushed to the remote. Return value depends
+        on circumstances:
+
+        If we are not going to push anything, return a tuple (None,
+        outgoing) where outgoing is 0 if there are no outgoing
+        changesets and 1 if there are, but we refuse to push them
+        (e.g. would create new remote heads).
+
+        Otherwise, return a tuple (changegroup, remoteheads), where
+        changegroup is a readable file-like object whose read() returns
+        successive changegroup chunks ready to be sent over the wire and
+        remoteheads is the list of remote heads.'''
         common = {}
         remote_heads = remote.heads()
         inc = self.findincoming(remote, common, remote_heads, force=force)
@@ -1640,17 +1654,26 @@
         return cg, remote_heads
 
     def push_addchangegroup(self, remote, force, revs):
+        '''Push a changegroup by locking the remote and sending the
+        addchangegroup command to it. Used for local and old SSH repos.
+        Return an integer: see push().
+        '''
         lock = remote.lock()
         try:
             ret = self.prepush(remote, force, revs)
             if ret[0] is not None:
                 cg, remote_heads = ret
+                # here, we return an integer indicating remote head count change
                 return remote.addchangegroup(cg, 'push', self.url())
+            # and here we return 0 for "nothing to push" or 1 for
+            # "something to push but I refuse"
             return ret[1]
         finally:
             lock.release()
 
     def push_unbundle(self, remote, force, revs):
+        '''Push a changegroup by unbundling it on the remote.  Used for new
+        SSH and HTTP repos. Return an integer: see push().'''
         # local repo finds heads on server, finds out what revs it
         # must push.  once revs transferred, if server finds it has
         # different heads (someone else won commit/push race), server
@@ -1661,7 +1684,10 @@
             cg, remote_heads = ret
             if force:
                 remote_heads = ['force']
+            # ssh: return remote's addchangegroup()
+            # http: return remote's addchangegroup() or 0 for error
             return remote.unbundle(cg, remote_heads, 'push')
+        # as in push_addchangegroup()
         return ret[1]
 
     def changegroupinfo(self, nodes, source):
@@ -2025,12 +2051,14 @@
         return util.chunkbuffer(gengroup())
 
     def addchangegroup(self, source, srctype, url, emptyok=False):
-        """add changegroup to repo.
+        """Add the changegroup returned by source.read() to this repo.
+        srctype is a string like 'push', 'pull', or 'unbundle'.  url is
+        the URL of the repo where this changegroup is coming from.
 
-        return values:
+        Return an integer summarizing the change to this repo:
         - nothing changed or no source: 0
         - more heads than before: 1+added heads (2..n)
-        - less heads than before: -1-removed heads (-2..-n)
+        - fewer heads than before: -1-removed heads (-2..-n)
         - number of heads stays the same: 1
         """
         def csmap(x):
--- a/mercurial/posix.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/posix.py	Tue May 11 17:12:10 2010 -0500
@@ -259,6 +259,12 @@
     except KeyError:
         return str(gid)
 
+def groupmembers(name):
+    """Return the list of members of the group with the given
+    name, KeyError if the group does not exist.
+    """
+    return list(grp.getgrnam(name).gr_mem)
+
 def spawndetached(args):
     return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
                       args[0], args)
--- a/mercurial/revlog.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/revlog.py	Tue May 11 17:12:10 2010 -0500
@@ -444,7 +444,10 @@
         i = ''
         try:
             f = self.opener(self.indexfile)
-            i = f.read(_prereadsize)
+            if "nonlazy" in getattr(self.opener, 'options', {}):
+                i = f.read()
+            else:
+                i = f.read(_prereadsize)
             if len(i) > 0:
                 v = struct.unpack(versionformat, i[:4])[0]
         except IOError, inst:
--- a/mercurial/sshrepo.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/sshrepo.py	Tue May 11 17:12:10 2010 -0500
@@ -217,6 +217,10 @@
         return self.do_cmd("changegroupsubset", bases=bases, heads=heads)
 
     def unbundle(self, cg, heads, source):
+        '''Send cg (a readable file-like object representing the
+        changegroup to push, typically a chunkbuffer object) to the
+        remote server as a bundle. Return an integer indicating the
+        result of the push (see localrepository.addchangegroup()).'''
         d = self.call("unbundle", heads=' '.join(map(hex, heads)))
         if d:
             # remote may send "unsynced changes"
@@ -242,6 +246,9 @@
             self.abort(error.ResponseError(_("unexpected response:"), r))
 
     def addchangegroup(self, cg, source, url):
+        '''Send a changegroup to the remote server.  Return an integer
+        similar to unbundle(). DEPRECATED, since it requires locking the
+        remote.'''
         d = self.call("addchangegroup")
         if d:
             self.abort(error.RepoError(_("push refused: %s") % d))
--- a/mercurial/statichttprepo.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/statichttprepo.py	Tue May 11 17:12:10 2010 -0500
@@ -77,6 +77,7 @@
             return httprangereader(f, urlopener)
         return o
 
+    opener.options = {'nonlazy': 1}
     return opener
 
 class statichttprepository(localrepo.localrepository):
--- a/mercurial/windows.py	Sun May 09 00:15:13 2010 +0200
+++ b/mercurial/windows.py	Tue May 11 17:12:10 2010 -0500
@@ -363,6 +363,10 @@
     # current line but on the new one. Keep room for it.
     return 79
 
+def groupmembers(name):
+    # Don't support groups on Windows for now
+    raise KeyError()
+
 try:
     # override functions with win32 versions if possible
     from win32 import *
--- a/tests/svn/move.svndump	Sun May 09 00:15:13 2010 +0200
+++ b/tests/svn/move.svndump	Tue May 11 17:12:10 2010 -0500
@@ -1,6 +1,6 @@
 SVN-fs-dump-format-version: 2
 
-UUID: 9de99ecc-876b-46e5-bc59-bff9b2b58b1e
+UUID: 7d15f7c2-5863-4c16-aa2a-3418b1721d3a
 
 Revision-number: 0
 Prop-content-length: 56
@@ -9,7 +9,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:26.678698Z
+2010-05-09T13:02:37.336239Z
 PROPS-END
 
 Revision-number: 1
@@ -27,7 +27,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:27.278689Z
+2010-05-09T13:02:37.372834Z
 PROPS-END
 
 Node-path: trunk
@@ -124,7 +124,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:28.312955Z
+2010-05-09T13:02:38.049068Z
 PROPS-END
 
 Node-path: trunk/a
@@ -166,7 +166,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:29.183467Z
+2010-05-09T13:02:39.044479Z
 PROPS-END
 
 Node-path: subproject
@@ -195,7 +195,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:30.300975Z
+2010-05-09T13:02:40.057804Z
 PROPS-END
 
 Node-path: subproject/trunk
@@ -222,7 +222,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:31.354398Z
+2010-05-09T13:02:41.058871Z
 PROPS-END
 
 Node-path: subproject/branches
@@ -249,7 +249,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:32.121901Z
+2010-05-09T13:02:42.046689Z
 PROPS-END
 
 Node-path: subproject/trunk/d1
@@ -278,7 +278,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:32.317815Z
+2010-05-09T13:02:42.071413Z
 PROPS-END
 
 Node-path: subproject/trunk/d2
@@ -307,7 +307,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:33.418320Z
+2010-05-09T13:02:43.062018Z
 PROPS-END
 
 Node-path: subproject/trunk/d1/b
@@ -341,7 +341,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:34.126542Z
+2010-05-09T13:02:44.047997Z
 PROPS-END
 
 Node-path: subproject/branches/d1
@@ -370,7 +370,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:34.436015Z
+2010-05-09T13:02:44.086619Z
 PROPS-END
 
 Node-path: subproject/trunk/d
@@ -397,7 +397,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:34.803189Z
+2010-05-09T13:02:44.111550Z
 PROPS-END
 
 Node-path: subproject/trunk/d2
@@ -422,7 +422,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:36.531735Z
+2010-05-09T13:02:45.067982Z
 PROPS-END
 
 Node-path: subproject/trunk/d3
@@ -484,7 +484,7 @@
 K 8
 svn:date
 V 27
-2009-06-21T14:32:38.281829Z
+2010-05-09T13:02:47.061259Z
 PROPS-END
 
 Node-path: subproject/trunk/d3/d31
@@ -498,3 +498,72 @@
 Node-copyfrom-path: subproject/trunk/d3
 
 
+Revision-number: 14
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 9
+add d4old
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2010-05-09T13:02:49.063363Z
+PROPS-END
+
+Node-path: subproject/trunk/d4old
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: subproject/trunk/d4old/g
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: f5302386464f953ed581edac03556e55
+Text-content-sha1: a5938ace3f424be1a26904781cdb06d55b614e6b
+Content-length: 12
+
+PROPS-END
+g
+
+
+Revision-number: 15
+Prop-content-length: 125
+Content-length: 125
+
+K 7
+svn:log
+V 23
+rename d4old into d4new
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2010-05-09T13:02:51.047304Z
+PROPS-END
+
+Node-path: subproject/trunk/d4new
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 14
+Node-copyfrom-path: subproject/trunk/d4old
+
+
+Node-path: subproject/trunk/d4old
+Node-action: delete
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/svn/replace.svndump	Tue May 11 17:12:10 2010 -0500
@@ -0,0 +1,241 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 4a895937-439c-4e56-b7b0-fa1c8acc0c20
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2010-05-09T14:57:31.007802Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 108
+Content-length: 108
+
+K 7
+svn:log
+V 7
+initial
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2010-05-09T14:57:32.094732Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/a
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3
+Text-content-sha1: 3f786850e387550fdab836ed7e6dc881de23001b
+Content-length: 12
+
+PROPS-END
+a
+
+
+Node-path: trunk/d
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/d/b
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: 3b5d5c3712955042212316173ccf37be
+Text-content-sha1: 89e6c98d92887913cadf06b2adb97f26cde4849b
+Content-length: 12
+
+PROPS-END
+b
+
+
+Node-path: trunk/dlink
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 6
+Text-content-md5: cca56829f18345718a4980bb02b6d8c3
+Text-content-sha1: 7c54cc5d472b78c94a04382df34b0f4f0f4f2d49
+Content-length: 39
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+link d
+
+Node-path: trunk/dlink2
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 6
+Text-content-md5: cca56829f18345718a4980bb02b6d8c3
+Text-content-sha1: 7c54cc5d472b78c94a04382df34b0f4f0f4f2d49
+Content-length: 39
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+link d
+
+Node-path: trunk/dlink3
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 6
+Text-content-md5: cca56829f18345718a4980bb02b6d8c3
+Text-content-sha1: 7c54cc5d472b78c94a04382df34b0f4f0f4f2d49
+Content-length: 39
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+link d
+
+Revision-number: 2
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 15
+clobber symlink
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2010-05-09T14:57:33.071117Z
+PROPS-END
+
+Node-path: trunk/dlink3
+Node-kind: file
+Node-action: change
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: e29311f6f1bf1af907f9ef9f44b8328b
+Text-content-sha1: e983f374794de9c64e3d1c1de1d490c0756eeeff
+Content-length: 12
+
+PROPS-END
+d
+
+
+Revision-number: 3
+Prop-content-length: 106
+Content-length: 106
+
+K 7
+svn:log
+V 8
+clobber1
+K 10
+svn:author
+V 4
+evil
+K 8
+svn:date
+V 27
+2010-05-09T14:57:35.268057Z
+PROPS-END
+
+Node-path: trunk/a
+Node-kind: dir
+Node-action: delete
+
+Node-path: trunk/a
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/d
+
+
+
+
+Node-path: trunk/dlink
+Node-kind: dir
+Node-action: delete
+
+Node-path: trunk/dlink
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/d
+
+
+
+
+Revision-number: 4
+Prop-content-length: 106
+Content-length: 106
+
+K 7
+svn:log
+V 8
+clobber2
+K 10
+svn:author
+V 4
+evil
+K 8
+svn:date
+V 27
+2010-05-09T14:57:35.521816Z
+PROPS-END
+
+Node-path: trunk/dlink3
+Node-kind: file
+Node-action: delete
+
+Node-path: trunk/dlink3
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 3
+Node-copyfrom-path: trunk/dlink2
+Text-copy-source-md5: cca56829f18345718a4980bb02b6d8c3
+Text-copy-source-sha1: 7c54cc5d472b78c94a04382df34b0f4f0f4f2d49
+
+
+
+
--- a/tests/svn/svndump-move.sh	Sun May 09 00:15:13 2010 +0200
+++ b/tests/svn/svndump-move.sh	Tue May 11 17:12:10 2010 -0500
@@ -68,6 +68,16 @@
 svn copy subproject/trunk/d3 subproject/trunk/d4
 svn rm subproject/trunk/d3/d31
 svn ci -m "copy dir and remove subdir"
+
+# Test directory moves
+svn up
+mkdir -p subproject/trunk/d4old
+echo g > subproject/trunk/d4old/g
+svn add subproject/trunk/d4old
+svn ci -m "add d4old"
+svn mv subproject/trunk/d4old subproject/trunk/d4new
+svn ci -m "rename d4old into d4new"
+
 cd ..
 
 svnadmin dump svn-repo > ../move.svndump
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/svn/svndump-replace.sh	Tue May 11 17:12:10 2010 -0500
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+RSVN="`pwd`/rsvn.py"
+export PATH=/bin:/usr/bin
+mkdir temp
+cd temp
+
+svnadmin create repo
+svn co file://`pwd`/repo wc
+
+cd wc
+mkdir trunk branches
+cd trunk
+echo a > a
+mkdir d
+echo b > d/b
+ln -s d dlink
+ln -s d dlink2
+ln -s d dlink3
+cd ..
+svn add *
+svn ci -m 'initial'
+# Clobber symlink with file with similar content
+cd trunk
+ls -Alh
+readlink dlink3 > dlink3tmp
+rm dlink3
+mv dlink3tmp dlink3
+svn propdel svn:special dlink3
+svn ci -m 'clobber symlink'
+cd ..
+svn up
+
+# Clobber files and symlink with directories
+cd ..
+cat > clobber.rsvn <<EOF
+rdelete trunk/a
+rdelete trunk/dlink
+rcopy trunk/d trunk/a
+rcopy trunk/d trunk/dlink
+EOF
+
+python $RSVN --message=clobber1 --username=evil `pwd`/repo < clobber.rsvn
+
+# Clobber non-symlink with symlink with same content (kudos openwrt)
+cat > clobber.rsvn <<EOF
+rdelete trunk/dlink3
+rcopy trunk/dlink2 trunk/dlink3
+EOF
+
+python $RSVN --message=clobber2 --username=evil `pwd`/repo < clobber.rsvn
+
+svn log -v file://`pwd`/repo
+
+svnadmin dump repo > ../replace.svndump
--- a/tests/test-acl	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-acl	Tue May 11 17:12:10 2010 -0500
@@ -7,7 +7,7 @@
 
     echo "Pushing as user $user"
     echo 'hgrc = """'
-    sed -e 1,2d b/.hg/hgrc | grep -v "$HGTMP"
+    sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py
     echo '"""'
     if test -f acl.config; then
 	echo 'acl.config = """'
@@ -165,4 +165,12 @@
 echo "@group is allowed inside anything but foo/Bar/"
 do_push fred
 
+echo 'Invalid group'
+# Disable the fakegroups trick to get real failures
+grep -v fakegroups $config > config.tmp
+mv config.tmp $config
+echo '[acl.allow]' >> $config
+echo "** = @unlikelytoexist" >> $config
+do_push fred 2>&1 | grep unlikelytoexist
 
+true
--- a/tests/test-acl.out	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-acl.out	Tue May 11 17:12:10 2010 -0500
@@ -1557,3 +1557,8 @@
 no rollback information available
 0:6675d58eff77
 
+Invalid group
+** = @unlikelytoexist
+acl: "unlikelytoexist" not defined in [acl.groups]
+error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
+abort: group 'unlikelytoexist' is undefined
--- a/tests/test-bookmarks	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-bookmarks	Tue May 11 17:12:10 2010 -0500
@@ -15,7 +15,8 @@
 hg bookmarks
 
 echo % list bookmarks with color
-hg --config extensions.color= bookmarks --color=always
+hg --config extensions.color= --config color.mode=ansi \
+    bookmarks --color=always
 
 echo a > a
 hg add a
--- a/tests/test-bookmarks-current	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-bookmarks-current	Tue May 11 17:12:10 2010 -0500
@@ -18,7 +18,8 @@
 hg bookmark
 
 echo % list bookmarks with color
-hg --config extensions.color= bookmark --color=always
+hg --config extensions.color= --config color.mode=ansi \
+    bookmark --color=always
 
 echo % update to bookmark X
 hg update X
--- a/tests/test-churn	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-churn	Tue May 11 17:12:10 2010 -0500
@@ -53,7 +53,8 @@
 hg ci -Am "removed d/g/f2.txt" -u user1 -d 14:00 d/g/f2.txt
 hg churn --diffstat
 echo % churn --diffstat with color
-hg --config extensions.color= churn --diffstat --color=always
+hg --config extensions.color= churn --config color.mode=ansi \
+    --diffstat --color=always
 
 echo % changeset number churn
 hg churn -c
--- a/tests/test-convert-svn-move	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-convert-svn-move	Tue May 11 17:12:10 2010 -0500
@@ -28,5 +28,43 @@
 
 cd A-hg
 hg glog --template '{rev} {desc|firstline} files: {files}\n'
+echo '% check move copy records'
+hg st --rev 12:13 --copies
+echo '% check branches'
 hg branches | sed 's/:.*/:/'
 cd ..
+
+mkdir test-replace
+cd test-replace
+svnadmin create svn-repo
+cat "$TESTDIR/svn/replace.svndump" | svnadmin load svn-repo > /dev/null
+
+echo '% convert files being replaced by directories'
+hg convert svn-repo hg-repo
+cd hg-repo
+echo '% manifest before'
+hg -v manifest -r 1
+echo '% manifest after clobber1'
+hg -v manifest -r 2
+echo '% manifest after clobber2'
+hg -v manifest -r 3
+echo '% try updating'
+hg up -qC default
+cd ..
+
+echo '% test convert progress bar'
+
+echo "progress=" >> $HGRCPATH
+echo "[progress]" >> $HGRCPATH
+echo "assume-tty=1" >> $HGRCPATH
+echo "delay=0" >> $HGRCPATH
+echo "refresh=0" >> $HGRCPATH
+
+cat > filtercr.py <<EOF
+import sys, re
+for line in sys.stdin:
+    line = re.sub(r'\r+[^\n]', lambda m: '\n' + m.group()[-1:], line)
+    sys.stdout.write(line)
+EOF
+
+hg convert svn-repo hg-progress 2>&1 | python filtercr.py
--- a/tests/test-convert-svn-move.out	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-convert-svn-move.out	Tue May 11 17:12:10 2010 -0500
@@ -3,18 +3,24 @@
 scanning source...
 sorting...
 converting...
-11 createtrunk
-10 moved1
-9 moved1
-8 moved2
-7 changeb and rm d2
-6 changeb and rm d2
-5 moved1again
-4 moved1again
-3 copyfilefrompast
-2 copydirfrompast
-1 add d3
-0 copy dir and remove subdir
+13 createtrunk
+12 moved1
+11 moved1
+10 moved2
+9 changeb and rm d2
+8 changeb and rm d2
+7 moved1again
+6 moved1again
+5 copyfilefrompast
+4 copydirfrompast
+3 add d3
+2 copy dir and remove subdir
+1 add d4old
+0 rename d4old into d4new
+o  13 rename d4old into d4new files: d4new/g d4old/g
+|
+o  12 add d4old files: d4old/g
+|
 o  11 copy dir and remove subdir files: d3/d31/e d4/d31/e d4/f
 |
 o  10 add d3 files: d3/d31/e d3/f
@@ -39,5 +45,79 @@
 |
 o  0 createtrunk files:
 
-default                       11:
+% check move copy records
+A d4new/g
+  d4old/g
+R d4old/g
+% check branches
+default                       13:
 d1                             6:
+% convert files being replaced by directories
+initializing destination hg-repo repository
+scanning source...
+sorting...
+converting...
+3 initial
+2 clobber symlink
+1 clobber1
+0 clobber2
+% manifest before
+644   a
+644   d/b
+644 @ dlink
+644 @ dlink2
+644   dlink3
+% manifest after clobber1
+644   a/b
+644   d/b
+644   dlink/b
+644 @ dlink2
+644   dlink3
+% manifest after clobber2
+644   a/b
+644   d/b
+644   dlink/b
+644 @ dlink2
+644 @ dlink3
+% try updating
+% test convert progress bar
+
+scanning [ <=>                                                              ] 1
+scanning [  <=>                                                             ] 2
+scanning [   <=>                                                            ] 3
+scanning [    <=>                                                           ] 4
+                                                                                
+converting [                                                              ] 0/4
+retrieving file [==========>                                              ] 1/5
+retrieving file [=====================>                                   ] 2/5
+retrieving file [=================================>                       ] 3/5
+retrieving file [============================================>            ] 4/5
+retrieving file [========================================================>] 5/5
+                                                                                
+converting [==============>                                               ] 1/4
+scanning paths [                                                          ] 0/1
+                                                                                
+retrieving file [========================================================>] 1/1
+                                                                                
+converting [==============================>                               ] 2/4
+scanning paths [                                                          ] 0/2
+scanning paths [============================>                             ] 1/2
+                                                                                
+retrieving file [=============>                                           ] 1/4
+retrieving file [===========================>                             ] 2/4
+retrieving file [=========================================>               ] 3/4
+retrieving file [========================================================>] 4/4
+                                                                                
+converting [=============================================>                ] 3/4
+scanning paths [                                                          ] 0/1
+                                                                                
+retrieving file [========================================================>] 1/1
+                                                                                
+initializing destination hg-progress repository
+scanning source...
+sorting...
+converting...
+3 initial
+2 clobber symlink
+1 clobber1
+0 clobber2
--- a/tests/test-copy2	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-copy2	Tue May 11 17:12:10 2010 -0500
@@ -19,6 +19,9 @@
 hg st -A
 hg commit -m1
 
+echo "# copy --after to a nonexistant target filename"
+hg cp -A foo dummy
+
 echo "# dry-run; should show that foo is clean"
 hg copy --dry-run foo bar
 hg st -A
--- a/tests/test-copy2.out	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-copy2.out	Tue May 11 17:12:10 2010 -0500
@@ -11,6 +11,8 @@
 # should print a warning that this is not a real copy; foo is added
 bar has not been committed yet, so no copy data will be stored for foo.
 A foo
+# copy --after to a nonexistant target filename
+foo: not recording copy - dummy does not exist
 # dry-run; should show that foo is clean
 C foo
 # should show copy
--- a/tests/test-grep	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-grep	Tue May 11 17:12:10 2010 -0500
@@ -22,7 +22,8 @@
 echo % simple
 hg grep port port
 echo % simple with color
-hg --config extensions.color= grep --color=always port port
+hg --config extensions.color= grep --config color.mode=ansi \
+    --color=always port port
 echo % all
 hg grep --traceback --all -nu port port
 echo % other
--- a/tests/test-log	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-log	Tue May 11 17:12:10 2010 -0500
@@ -119,7 +119,8 @@
 hg log -d -1
 
 echo '% log -p -l2 --color=always'
-hg --config extensions.color= log -p -l2 --color=always
+hg --config extensions.color= --config color.mode=ansi \
+    log -p -l2 --color=always
 
 echo '% log -r tip --stat'
 hg log -r tip --stat
--- a/tests/test-mq-guards	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-mq-guards	Tue May 11 17:12:10 2010 -0500
@@ -100,7 +100,8 @@
 echo % list patches and guards
 hg qguard -l
 echo % list patches and guards with color
-hg --config extensions.color= qguard -l --color=always
+hg --config extensions.color= qguard --config color.mode=ansi \
+    -l --color=always
 echo % list series
 hg qseries -v
 echo % list guards
--- a/tests/test-patchbomb	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-patchbomb	Tue May 11 17:12:10 2010 -0500
@@ -135,6 +135,14 @@
 hg email --date '1970-1-1 0:1' -n --intro -f quux -t foo -c bar -s test \
   -r 0:1 | fixheaders
 
+echo "% test reply-to via config"
+hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -r 2 \
+  --config patchbomb.reply-to='baz@example.com' | fixheaders
+
+echo "% test reply-to via command line"
+hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -r 2 \
+  --reply-to baz --reply-to fred | fixheaders
+
 echo "% tagging csets"
 hg tag -r0 zero zero.foo
 hg tag -r1 one one.patch
--- a/tests/test-patchbomb.out	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-patchbomb.out	Tue May 11 17:12:10 2010 -0500
@@ -1062,6 +1062,68 @@
 @@ -0,0 +1,1 @@
 +b
 
+% test reply-to via config
+This patch series consists of 1 patches.
+
+
+Displaying [PATCH] test ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH] test
+X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
+Message-Id: <ff2c9fa2018b15fa74b3.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:00 +0000
+From: quux
+To: foo
+Cc: bar
+Reply-To: baz@example.com
+
+# HG changeset patch
+# User test
+# Date 3 0
+# Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
+# Parent  97d72e5f12c7e84f85064aa72e5a297142c36ed9
+c
+
+diff -r 97d72e5f12c7 -r ff2c9fa2018b c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/c	Thu Jan 01 00:00:03 1970 +0000
+@@ -0,0 +1,1 @@
++c
+
+% test reply-to via command line
+This patch series consists of 1 patches.
+
+
+Displaying [PATCH] test ...
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [PATCH] test
+X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
+Message-Id: <ff2c9fa2018b15fa74b3.60@
+User-Agent: Mercurial-patchbomb
+Date: Thu, 01 Jan 1970 00:01:00 +0000
+From: quux
+To: foo
+Cc: bar
+Reply-To: baz, fred
+
+# HG changeset patch
+# User test
+# Date 3 0
+# Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
+# Parent  97d72e5f12c7e84f85064aa72e5a297142c36ed9
+c
+
+diff -r 97d72e5f12c7 -r ff2c9fa2018b c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/c	Thu Jan 01 00:00:03 1970 +0000
+@@ -0,0 +1,1 @@
++c
+
 % tagging csets
 % test inline for single named patch
 This patch series consists of 1 patches.
--- a/tests/test-rename	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-rename	Tue May 11 17:12:10 2010 -0500
@@ -23,6 +23,9 @@
 hg update -C
 rm d2/c
 
+echo "# rename --after a single file to a nonexistant target filename"
+hg rename --after d1/a dummy
+
 echo "# move a single file to an existing directory"
 hg rename d1/d11/a1 d2
 hg status -C
--- a/tests/test-rename.out	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-rename.out	Tue May 11 17:12:10 2010 -0500
@@ -8,6 +8,8 @@
   d1/d11/a1
 R d1/d11/a1
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+# rename --after a single file to a nonexistant target filename
+d1/a: not recording move - dummy does not exist
 # move a single file to an existing directory
 A d2/a1
   d1/d11/a1
@@ -170,6 +172,8 @@
 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
 # move --after some files under d1 to d2/d21 (glob)
 moving d1/a to d2/d21/a
+d1/b: not recording move - d2/d21/b does not exist
+d1/ba: not recording move - d2/d21/ba does not exist
 moving d1/d11/a1 to d2/d21/a1
 A d2/d21/a
   d1/a
--- a/tests/test-schemes	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-schemes	Tue May 11 17:12:10 2010 -0500
@@ -7,6 +7,7 @@
 [schemes]
 l = http://localhost:$HGPORT/
 parts = http://{1}:$HGPORT/
+z = file:\$PWD/
 EOF
 
 hg init test
@@ -22,5 +23,8 @@
 echo % check that {1} syntax works
 hg incoming --debug parts://localhost | sed 's/[0-9]//g'
 
+echo % check that paths are expanded
+PWD=`pwd` hg incoming z://
+
 echo % errors
 cat errors.log
--- a/tests/test-schemes.out	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-schemes.out	Tue May 11 17:12:10 2010 -0500
@@ -9,4 +9,8 @@
 sending heads command
 searching for changes
 no changes found
+% check that paths are expanded
+comparing with z://
+searching for changes
+no changes found
 % errors
--- a/tests/test-status-color	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-status-color	Tue May 11 17:12:10 2010 -0500
@@ -2,6 +2,8 @@
 
 echo "[extensions]" >> $HGRCPATH
 echo "color=" >> $HGRCPATH
+echo "[color]" >> $HGRCPATH
+echo "mode=ansi" >> $HGRCPATH
 
 hg init repo1
 cd repo1
--- a/tests/test-subrepo-svn	Sun May 09 00:15:13 2010 +0200
+++ b/tests/test-subrepo-svn	Tue May 11 17:12:10 2010 -0500
@@ -63,7 +63,8 @@
 echo % change file in svn and hg, commit
 echo a >> a
 echo alpha >> s/alpha
-hg commit -m 'Message!' | sed "$filterexternal"
+hg commit -m 'Message!' | sed "$filterexternal" \
+    | sed 's:Sending.*s/alpha:Sending        s/alpha:g'
 hg debugsub | sed "$filterpath"
 
 echo