changeset 6549:2af1b9de62b3

Merge with crew-stable
author Patrick Mezard <pmezard@gmail.com>
date Mon, 14 Apr 2008 23:04:34 +0200
parents 962eb403165b (current diff) 075b2c9aed37 (diff)
children 628da4a91628
files hgext/convert/subversion.py mercurial/localrepo.py
diffstat 8 files changed, 265 insertions(+), 169 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/convert/subversion.py	Mon Apr 14 14:34:38 2008 +0200
+++ b/hgext/convert/subversion.py	Mon Apr 14 23:04:34 2008 +0200
@@ -185,6 +185,8 @@
             self.ra = self.transport.ra
             self.ctx = self.transport.client
             self.base = svn.ra.get_repos_root(self.ra)
+            # Module is either empty or a repository path starting with
+            # a slash and not ending with a slash.
             self.module = self.url[len(self.base):]
             self.rootmodule = self.module
             self.commits = {}
@@ -535,24 +537,6 @@
         svn.ra.reparent(self.ra, svn_url.encode(self.encoding))
 
     def expandpaths(self, rev, paths, parents):
-        def get_entry_from_path(path, module=self.module):
-            # Given the repository url of this wc, say
-            #   "http://server/plone/CMFPlone/branches/Plone-2_0-branch"
-            # extract the "entry" portion (a relative path) from what
-            # svn log --xml says, ie
-            #   "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py"
-            # that is to say "tests/PloneTestCase.py"
-            if path.startswith(module):
-                relative = path[len(module):]
-                if relative.startswith('/'):
-                    return relative[1:]
-                else:
-                    return relative
-
-            # The path is outside our tracked tree...
-            self.ui.debug('%r is not under %r, ignoring\n' % (path, module))
-            return None
-
         entries = []
         copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions.
         copies = {}
@@ -563,24 +547,25 @@
             self.reparent(self.module)
 
         for path, ent in paths:
-            entrypath = get_entry_from_path(path, module=self.module)
+            entrypath = self.getrelpath(path)
             entry = entrypath.decode(self.encoding)
 
             kind = svn.ra.check_path(self.ra, entrypath, revnum)
             if kind == svn.core.svn_node_file:
-                if ent.copyfrom_path:
-                    copyfrom_path = get_entry_from_path(ent.copyfrom_path)
-                    if copyfrom_path:
-                        self.ui.debug("Copied to %s from %s@%s\n" %
-                                      (entrypath, copyfrom_path,
-                                       ent.copyfrom_rev))
-                        # It's probably important for hg that the source
-                        # exists in the revision's parent, not just the
-                        # ent.copyfrom_rev
-                        fromkind = svn.ra.check_path(self.ra, copyfrom_path, ent.copyfrom_rev)
-                        if fromkind != 0:
-                            copies[self.recode(entry)] = self.recode(copyfrom_path)
                 entries.append(self.recode(entry))
+                if not ent.copyfrom_path or not parents:
+                    continue
+                # Copy sources not in parent revisions cannot be represented,
+                # ignore their origin for now
+                pmodule, prevnum = self.revsplit(parents[0])[1:]
+                if ent.copyfrom_rev < prevnum:
+                    continue
+                copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule)
+                if not copyfrom_path:
+                    continue
+                self.ui.debug("copied to %s from %s@%s\n" %
+                              (entrypath, copyfrom_path, ent.copyfrom_rev))
+                copies[self.recode(entry)] = self.recode(copyfrom_path)
             elif kind == 0: # gone, but had better be a deleted *file*
                 self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
 
@@ -590,8 +575,8 @@
                 # a root revision.
                 uuid, old_module, fromrev = self.revsplit(parents[0])
 
-                basepath = old_module + "/" + get_entry_from_path(path, module=self.module)
-                entrypath = old_module + "/" + get_entry_from_path(path, module=self.module)
+                basepath = old_module + "/" + self.getrelpath(path)
+                entrypath = basepath
 
                 def lookup_parts(p):
                     rc = None
@@ -647,7 +632,7 @@
                         # 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 = get_entry_from_path("/" + child, module=old_module)
+                        entrypath = self.getrelpath("/" + child, module=old_module)
                         if entrypath:
                             entry = self.recode(entrypath.decode(self.encoding))
                             if entry in copies:
@@ -680,7 +665,7 @@
                     # 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 = get_entry_from_path("/" + child, module=self.module)
+                    entrypath = self.getrelpath("/" + child)
                     # print child, self.module, entrypath
                     if entrypath:
                         # Need to filter out directories here...
@@ -691,39 +676,30 @@
                 # Copies here (must copy all from source)
                 # Probably not a real problem for us if
                 # source does not exist
-
-                # Can do this with the copy command "hg copy"
-                # if ent.copyfrom_path:
-                #     copyfrom_entry = get_entry_from_path(ent.copyfrom_path.decode(self.encoding),
-                #             module=self.module)
-                #     copyto_entry = entrypath
-                #
-                #     print "copy directory", copyfrom_entry, 'to', copyto_entry
-                #
-                #     copies.append((copyfrom_entry, copyto_entry))
-
-                if ent.copyfrom_path:
-                    copyfrom_path = ent.copyfrom_path.decode(self.encoding)
-                    copyfrom_entry = get_entry_from_path(copyfrom_path, module=self.module)
-                    if copyfrom_entry:
-                        copyfrom[path] = ent
-                        self.ui.debug("mark %s came from %s\n" % (path, copyfrom[path]))
-
-                        # Good, /probably/ a regular copy. Really should check
-                        # to see whether the parent revision actually contains
-                        # the directory in question.
-                        children = self._find_children(self.recode(copyfrom_path), ent.copyfrom_rev)
-                        children.sort()
-                        for child in children:
-                            entrypath = get_entry_from_path("/" + child, module=self.module)
-                            if entrypath:
-                                entry = entrypath.decode(self.encoding)
-                                # print "COPY COPY From", copyfrom_entry, entry
-                                copyto_path = path + entry[len(copyfrom_entry):]
-                                copyto_entry =  get_entry_from_path(copyto_path, module=self.module)
-                                # print "COPY", entry, "COPY To", copyto_entry
-                                copies[self.recode(copyto_entry)] = self.recode(entry)
-                                # copy from quux splort/quuxfile
+                if not ent.copyfrom_path or not parents:
+                    continue
+                # Copy sources not in parent revisions cannot be represented,
+                # ignore their origin for now
+                pmodule, prevnum = self.revsplit(parents[0])[1:]
+                if ent.copyfrom_rev < prevnum:
+                    continue
+                copyfrompath = ent.copyfrom_path.decode(self.encoding)
+                copyfrompath = self.getrelpath(copyfrompath, 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:
+                        continue
+                    entry = entrypath.decode(self.encoding)
+                    copytopath = path + entry[len(copyfrompath):]
+                    copytopath = self.getrelpath(copytopath)
+                    copies[self.recode(copytopath)] = self.recode(entry, pmodule)
 
         return (util.unique(entries), copies)
 
@@ -732,6 +708,13 @@
             from_revnum, to_revnum = to_revnum, from_revnum
 
         self.child_cset = None
+
+        def isdescendantof(parent, child):
+            if not child or not parent or not child.startswith(parent):
+                return False
+            subpath = child[len(parent):]
+            return len(subpath) > 1 and subpath[0] == '/'
+
         def parselogentry(orig_paths, revnum, author, date, message):
             """Return the parsed commit object or None, and True if
             the revision is a branch root.
@@ -755,10 +738,21 @@
             if root_paths:
                 path, ent = root_paths[-1]
                 if ent.copyfrom_path:
+                    # If dir was moved while one of its file was removed 
+                    # the log may look like:
+                    # A /dir   (from /dir:x)
+                    # A /dir/a (from /dir/a:y)
+                    # A /dir/b (from /dir/b:z)
+                    # ...
+                    # for all remaining children.
+                    # Let's take the highest child element from rev as source.
+                    copies = [(p,e) for p,e in orig_paths[:-1] 
+                          if isdescendantof(ent.copyfrom_path, e.copyfrom_path)]
+                    fromrev = max([e.copyfrom_rev for p,e in copies] + [ent.copyfrom_rev])
                     branched = True
                     newpath = ent.copyfrom_path + self.module[len(path):]
                     # ent.copyfrom_rev may not be the actual last revision
-                    previd = self.latest(newpath, ent.copyfrom_rev)
+                    previd = self.latest(newpath, fromrev)
                     if previd is not None:
                         prevmodule, prevnum = self.revsplit(previd)[1:]
                         if prevnum >= self.startrev:
@@ -771,8 +765,7 @@
             paths = []
             # filter out unrelated paths
             for path, ent in orig_paths:
-                if not path.startswith(self.module):
-                    self.ui.debug("boring@%s: %s\n" % (revnum, path))
+                if self.getrelpath(path) is None:
                     continue
                 paths.append((path, ent))
 
@@ -885,6 +878,26 @@
         rpath = '/'.join([self.base, path]).strip('/')
         return ['%s/%s' % (path, x) for x in svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()]
 
+    def getrelpath(self, path, module=None):
+        if module is None:
+            module = self.module
+        # Given the repository url of this wc, say
+        #   "http://server/plone/CMFPlone/branches/Plone-2_0-branch"
+        # extract the "entry" portion (a relative path) from what
+        # svn log --xml says, ie
+        #   "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py"
+        # that is to say "tests/PloneTestCase.py"
+        if path.startswith(module):
+            relative = path.rstrip('/')[len(module):]
+            if relative.startswith('/'):
+                return relative[1:]
+            elif relative == '':
+                return relative
+
+        # The path is outside our tracked tree...
+        self.ui.debug('%r is not under %r, ignoring\n' % (path, module))
+        return None
+
 pre_revprop_change = '''#!/bin/sh
 
 REPOS="$1"
--- a/mercurial/cmdutil.py	Mon Apr 14 14:34:38 2008 +0200
+++ b/mercurial/cmdutil.py	Mon Apr 14 23:04:34 2008 +0200
@@ -1018,11 +1018,16 @@
         minrev, maxrev = min(revs), max(revs)
         for file_, node in iterfiles():
             filelog = repo.file(file_)
-            # A zero count may be a directory or deleted file, so
-            # try to find matching entries on the slow path.
             if filelog.count() == 0:
-                slowpath = True
-                break
+                if node is None:
+                    # A zero count may be a directory or deleted file, so
+                    # try to find matching entries on the slow path.
+                    slowpath = True
+                    break
+                else:
+                    ui.warn(_('%s:%s copy source revision cannot be found!\n') 
+                            % (file_, short(node)))
+                    continue
             for rev, copied in filerevgen(filelog, node):
                 if rev <= maxrev:
                     if rev < minrev:
--- a/mercurial/localrepo.py	Mon Apr 14 14:34:38 2008 +0200
+++ b/mercurial/localrepo.py	Mon Apr 14 23:04:34 2008 +0200
@@ -712,17 +712,17 @@
             #
             meta["copy"] = cp
             if not manifest2: # not a branch merge
-                meta["copyrev"] = hex(manifest1.get(cp, nullid))
+                meta["copyrev"] = hex(manifest1[cp])
                 fp2 = nullid
             elif fp2 != nullid: # copied on remote side
-                meta["copyrev"] = hex(manifest1.get(cp, nullid))
+                meta["copyrev"] = hex(manifest1[cp])
             elif fp1 != nullid: # copied on local side, reversed
-                meta["copyrev"] = hex(manifest2.get(cp))
+                meta["copyrev"] = hex(manifest2[cp])
                 fp2 = fp1
             elif cp in manifest2: # directory rename on local side
                 meta["copyrev"] = hex(manifest2[cp])
             else: # directory rename on remote side
-                meta["copyrev"] = hex(manifest1.get(cp, nullid))
+                meta["copyrev"] = hex(manifest1[cp])
             self.ui.debug(_(" %s: copy %s:%s\n") %
                           (fn, cp, meta["copyrev"]))
             fp1 = nullid
--- a/mercurial/verify.py	Mon Apr 14 14:34:38 2008 +0200
+++ b/mercurial/verify.py	Mon Apr 14 23:04:34 2008 +0200
@@ -245,7 +245,14 @@
                 rp = fl.renamed(n)
                 if rp:
                     fl2 = repo.file(rp[0])
-                    rev = fl2.rev(rp[1])
+                    if fl2.count() == 0:
+                        err(flr, _("empty or missing copy source revlog %s:%s")
+                            % (rp[0], short(rp[1])), f)
+                    elif rp[1] == nullid:
+                        err(flr, _("copy source revision is nullid %s:%s")
+                            % (rp[0], short(rp[1])), f)
+                    else:
+                        rev = fl2.rev(rp[1])
             except KeyboardInterrupt:
                 repo.ui.warn(_("interrupted"))
                 raise
--- a/tests/test-convert-svn-branches	Mon Apr 14 14:34:38 2008 +0200
+++ b/tests/test-convert-svn-branches	Mon Apr 14 23:04:34 2008 +0200
@@ -35,41 +35,48 @@
 echo % update svn repository
 svn co $svnurl A | fix_path
 cd A
-echo hello > trunk/letter.txt
-echo hey > trunk/letter2.txt
-echo ho > trunk/letter3.txt
+echo a > trunk/a
+echo b > trunk/b
+echo c > trunk/c
 # Add a file within branches, used to confuse branch detection
-echo a > branches/readme.txt
-svn add trunk/letter.txt trunk/letter2.txt trunk/letter3.txt branches/readme.txt
+echo d > branches/notinbranch
+svn add trunk/a trunk/b trunk/c branches/notinbranch
 svn ci -m hello
 
-echo % branch to old letters
+echo % branch to old
 svn copy trunk branches/old
-svn rm branches/old/letter3.txt
-svn ci -m "branch trunk, remove letter3"
+svn rm branches/old/c
+svn ci -m "branch trunk, remove c"
 svn up
 
 echo % update trunk
-"$TESTDIR/svn-safe-append.py" "what can I say ?" trunk/letter.txt
-svn ci -m "change letter"
+"$TESTDIR/svn-safe-append.py" a trunk/a
+svn ci -m "change a"
 
 echo % update old branch
-"$TESTDIR/svn-safe-append.py" "what's up ?" branches/old/letter2.txt
-svn ci -m "change letter2"
+"$TESTDIR/svn-safe-append.py" b branches/old/b
+svn ci -m "change b"
 
 echo % create a cross-branch revision
-svn move -m "move letter2" trunk/letter2.txt \
-    branches/old/letter3.txt
-"$TESTDIR/svn-safe-append.py" "I am fine" branches/old/letter3.txt
-svn ci -m "move and update letter3.txt"
+svn move -m "move b" trunk/b branches/old/c
+"$TESTDIR/svn-safe-append.py" c branches/old/c
+svn ci -m "move and update c"
 
 echo % update old branch again
-"$TESTDIR/svn-safe-append.py" "bye" branches/old/letter2.txt
-svn ci -m "change letter2 again"
+"$TESTDIR/svn-safe-append.py" b branches/old/b
+svn ci -m "change b again"
+
+echo % move back and forth between branch of similar names
+# This used to generate fake copy records
+svn up
+svn move branches/old branches/old2
+svn ci -m "move to old2"
+svn move branches/old2 branches/old
+svn ci -m "move back to old"
 
 echo % update trunk again
-"$TESTDIR/svn-safe-append.py" "how are you ?" trunk/letter.txt
-svn ci -m "last change to letter"
+"$TESTDIR/svn-safe-append.py" a trunk/a
+svn ci -m "last change to a"
 cd ..
 
 echo % convert trunk and branches
@@ -77,15 +84,15 @@
 
 echo % branch again from a converted revision
 cd A
-svn copy -r 1 $svnurl/trunk branches/old2
-svn ci -m "branch trunk@1 into old2"
+svn copy -r 1 $svnurl/trunk branches/old3
+svn ci -m "branch trunk@1 into old3"
 cd ..
 
 echo % convert again
 hg convert --datesort $svnurl A-hg
 
 cd A-hg
-hg glog --template '#rev# #desc|firstline# files: #files#\n'
+hg glog --template 'branch=#branches# #rev# #desc|firstline# files: #files#\n'
 hg branches | sed 's/:.*/:/'
 hg tags -q
 cd ..
--- a/tests/test-convert-svn-branches.out	Mon Apr 14 14:34:38 2008 +0200
+++ b/tests/test-convert-svn-branches.out	Mon Apr 14 23:04:34 2008 +0200
@@ -9,95 +9,122 @@
 A    A/branches
 A    A/tags
 Checked out revision 1.
-A         trunk/letter.txt
-A         trunk/letter2.txt
-A         trunk/letter3.txt
-A         branches/readme.txt
-Adding         branches/readme.txt
-Adding         trunk/letter.txt
-Adding         trunk/letter2.txt
-Adding         trunk/letter3.txt
+A         trunk/a
+A         trunk/b
+A         trunk/c
+A         branches/notinbranch
+Adding         branches/notinbranch
+Adding         trunk/a
+Adding         trunk/b
+Adding         trunk/c
 Transmitting file data ....
 Committed revision 2.
-% branch to old letters
+% branch to old
 A         branches/old
-D         branches/old/letter3.txt
+D         branches/old/c
 Adding         branches/old
-Adding         branches/old/letter.txt
-Adding         branches/old/letter2.txt
-Deleting       branches/old/letter3.txt
+Adding         branches/old/a
+Adding         branches/old/b
+Deleting       branches/old/c
 
 Committed revision 3.
 At revision 3.
 % update trunk
-Sending        trunk/letter.txt
+Sending        trunk/a
 Transmitting file data .
 Committed revision 4.
 % update old branch
-Sending        branches/old/letter2.txt
+Sending        branches/old/b
 Transmitting file data .
 Committed revision 5.
 % create a cross-branch revision
-A         branches/old/letter3.txt
-D         trunk/letter2.txt
-Adding         branches/old/letter3.txt
-Deleting       trunk/letter2.txt
+A         branches/old/c
+D         trunk/b
+Adding         branches/old/c
+Deleting       trunk/b
 Transmitting file data .
 Committed revision 6.
 % update old branch again
-Sending        branches/old/letter2.txt
+Sending        branches/old/b
 Transmitting file data .
 Committed revision 7.
+% move back and forth between branch of similar names
+At revision 7.
+A         branches/old2
+D         branches/old/a
+D         branches/old/b
+D         branches/old/c
+D         branches/old
+Deleting       branches/old
+Adding         branches/old2
+
+Committed revision 8.
+A         branches/old
+D         branches/old2/a
+D         branches/old2/b
+D         branches/old2/c
+D         branches/old2
+Adding         branches/old
+Deleting       branches/old2
+
+Committed revision 9.
 % update trunk again
-Sending        trunk/letter.txt
+Sending        trunk/a
 Transmitting file data .
-Committed revision 8.
+Committed revision 10.
 % convert trunk and branches
 initializing destination A-hg repository
 scanning source...
 sorting...
 converting...
-8 init projA
-7 hello
-6 branch trunk, remove letter3
-5 change letter
-4 change letter2
-3 move and update letter3.txt
-2 move and update letter3.txt
-1 change letter2 again
-0 last change to letter
+10 init projA
+9 hello
+8 branch trunk, remove c
+7 change a
+6 change b
+5 move and update c
+4 move and update c
+3 change b again
+2 move to old2
+1 move back to old
+0 last change to a
 % branch again from a converted revision
 Checked out revision 1.
-A         branches/old2
-Adding         branches/old2
+A         branches/old3
+Adding         branches/old3
 
-Committed revision 9.
+Committed revision 11.
 % convert again
 scanning source...
 sorting...
 converting...
-0 branch trunk@1 into old2
-o  9 branch trunk@1 into old2 files:
+0 branch trunk@1 into old3
+o  branch=old3 11 branch trunk@1 into old3 files:
 |
-| o  8 last change to letter files: letter.txt
+| o  branch= 10 last change to a files: a
 | |
-| | o  7 change letter2 again files: letter2.txt
+| | o  branch=old 9 move back to old files:
 | | |
-| o |  6 move and update letter3.txt files: letter2.txt
+| | o  branch=old2 8 move to old2 files:
 | | |
-| | o  5 move and update letter3.txt files: letter3.txt
+| | o  branch=old 7 change b again files: b
 | | |
-| | o  4 change letter2 files: letter2.txt
+| o |  branch= 6 move and update c files: b
+| | |
+| | o  branch=old 5 move and update c files: c
 | | |
-| o |  3 change letter files: letter.txt
+| | o  branch=old 4 change b files: b
+| | |
+| o |  branch= 3 change a files: a
 | | |
-+---o  2 branch trunk, remove letter3 files: letter.txt letter2.txt
-| |
-| o  1 hello files: letter.txt letter2.txt letter3.txt
+| | o  branch=old 2 branch trunk, remove c files: a b
+| |/
+| o  branch= 1 hello files: a b c
 |/
-o  0 init projA files:
+o  branch= 0 init projA files:
 
-old2                           9:
-default                        8:
-old                            7:
+old3                           11:
+default                        10:
+old                            9:
+old2                           8:
 tip
--- a/tests/test-convert-svn-move	Mon Apr 14 14:34:38 2008 +0200
+++ b/tests/test-convert-svn-move	Mon Apr 14 23:04:34 2008 +0200
@@ -27,8 +27,10 @@
 mkdir trunk
 echo a > trunk/a
 mkdir trunk/d1
+mkdir trunk/d2
 echo b > trunk/d1/b
 echo c > trunk/d1/c
+echo d > trunk/d2/d
 cd ..
 
 svnurl=file://$svnpath/svn-repo/projA
@@ -50,10 +52,16 @@
 svn add subproject/branches
 svn ci -m createbranches
 svn mv $svnurl/subproject/d1 $svnurl/subproject/trunk/d1 -m moved1
+svn mv $svnurl/subproject/d2 $svnurl/subproject/trunk/d2 -m moved2
 svn up
 "$TESTDIR/svn-safe-append.py" b subproject/trunk/d1/b
-svn ci -m changeb
+svn rm subproject/trunk/d2
+svn ci -m "changeb and rm d2"
 svn mv $svnurl/subproject/trunk/d1 $svnurl/subproject/branches/d1 -m moved1again
+echo % copy a file from a past revision
+svn copy -r 7 $svnurl/subproject/trunk/d2/d $svnurl/subproject/trunk -m copyfilefrompast
+echo % copy a directory from a past revision
+svn copy -r 7 $svnurl/subproject/trunk/d2 $svnurl/subproject/trunk -m copydirfrompast
 cd ..
 
 echo % convert trunk and branches
--- a/tests/test-convert-svn-move.out	Mon Apr 14 14:34:38 2008 +0200
+++ b/tests/test-convert-svn-move.out	Mon Apr 14 23:04:34 2008 +0200
@@ -4,6 +4,8 @@
 Adding         projA/trunk/d1
 Adding         projA/trunk/d1/b
 Adding         projA/trunk/d1/c
+Adding         projA/trunk/d2
+Adding         projA/trunk/d2/d
 
 Committed revision 1.
 % update svn repository
@@ -12,6 +14,8 @@
 A    A/trunk/d1
 A    A/trunk/d1/b
 A    A/trunk/d1/c
+A    A/trunk/d2
+A    A/trunk/d2/d
 Checked out revision 1.
 Sending        trunk/a
 Sending        trunk/d1/c
@@ -25,6 +29,8 @@
 A    subproject/d1
 A    subproject/d1/b
 A    subproject/d1/c
+A    subproject/d2
+A    subproject/d2/d
 Updated to revision 3.
 A         subproject/trunk
 Adding         subproject/trunk
@@ -36,35 +42,58 @@
 Committed revision 5.
 
 Committed revision 6.
+
+Committed revision 7.
 A    subproject/trunk/d1
 A    subproject/trunk/d1/b
 A    subproject/trunk/d1/c
+A    subproject/trunk/d2
+A    subproject/trunk/d2/d
 D    subproject/d1
-Updated to revision 6.
+D    subproject/d2
+Updated to revision 7.
+D         subproject/trunk/d2/d
+D         subproject/trunk/d2
 Sending        subproject/trunk/d1/b
+Deleting       subproject/trunk/d2
 Transmitting file data .
-Committed revision 7.
+Committed revision 8.
+
+Committed revision 9.
+% copy a file from a past revision
 
-Committed revision 8.
+Committed revision 10.
+% copy a directory from a past revision
+
+Committed revision 11.
 % convert trunk and branches
 initializing destination A-hg repository
 scanning source...
 sorting...
 converting...
-6 createtrunk
-5 moved1
-4 moved1
-3 changeb
-2 changeb
-1 moved1again
-0 moved1again
-o  6 moved1again files: d1/b d1/c
+9 createtrunk
+8 moved1
+7 moved1
+6 moved2
+5 changeb and rm d2
+4 changeb and rm d2
+3 moved1again
+2 moved1again
+1 copyfilefrompast
+0 copydirfrompast
+o  9 copydirfrompast files: d2/d
 |
-| o  5 moved1again files:
+o  8 copyfilefrompast files: d
+|
+o  7 moved1again files: d1/b d1/c
+|
+| o  6 moved1again files:
 | |
-o |  4 changeb files: d1/b
+o |  5 changeb and rm d2 files: d1/b d2/d
 | |
-| o  3 changeb files: b
+| o  4 changeb and rm d2 files: b
+| |
+o |  3 moved2 files: d2/d
 | |
 o |  2 moved1 files: d1/b d1/c
 | |
@@ -72,5 +101,5 @@
 |
 o  0 createtrunk files:
 
-default                        6:
-d1                             5:
+default                        9:
+d1                             6: