changeset 67:e319c9168910

hg_delta_editor: register svn file copies
author Patrick Mezard <pmezard@gmail.com>
date Wed, 05 Nov 2008 13:37:07 +0100
parents a31968146f3c
children e0c86ebe05e3
files hg_delta_editor.py tests/fixtures/renames.sh tests/fixtures/renames.svndump tests/run.py tests/test_fetch_command.py tests/test_fetch_renames.py
diffstat 6 files changed, 558 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -127,6 +127,8 @@ class HgChangeReceiver(delta.Editor):
         self.current_rev = None
         self.current_files_exec = {}
         self.current_files_symlink = {}
+        # Map fully qualified destination file paths to module source path
+        self.copies = {}
         self.missing_plaintexts = set()
         self.commit_branches_empty = {}
         self.base_revision = None
@@ -351,11 +353,10 @@ class HgChangeReceiver(delta.Editor):
                 extra['branch'] = branch
             parent_ctx = self.repo.changectx(parents[0])
             def filectxfn(repo, memctx, path):
-                copied = None
                 current_file = files[path]
                 if current_file in self.deleted_files:
                     raise IOError()
-                # TODO(augie) tag copies from files
+                copied = self.copies.get(current_file)
                 flags = parent_ctx.flags(path)
                 is_exec = self.current_files_exec.get(current_file,
                                                       'x' in flags)
@@ -501,30 +502,33 @@ class HgChangeReceiver(delta.Editor):
         self.base_revision = None
         if path in self.deleted_files:
             del self.deleted_files[path]
-        if (self._is_path_valid(path) and
-            self._path_and_branch_for_path(path)[0]):
-            self.current_file = path
-            self.should_edit_most_recent_plaintext = False
-            if copyfrom_path:
-                self.ui.status('A+ %s\n' % path)
-                # TODO(augie) handle this better, actually mark a copy
-                (from_file,
-                 from_branch) = self._path_and_branch_for_path(copyfrom_path)
-                if not from_file:
-                    self.missing_plaintexts.add(path)
-                    return
-                ha = self.get_parent_revision(copyfrom_revision + 1,
-                                              from_branch)
-                ctx = self.repo.changectx(ha)
-                if from_file in ctx:
-                    fctx = ctx.filectx(from_file)
-                    cur_file = self.current_file
-                    self.current_files[cur_file] = fctx.data()
-                    self.current_files_symlink[cur_file] = 'l' in fctx.flags()
-                    self.current_files_exec[cur_file] = 'x' in fctx.flags()
-            else:
-                self.ui.status('A %s\n' % path)
-
+        if not self._is_path_valid(path):
+            return
+        fpath, branch = self._path_and_branch_for_path(path)
+        if not fpath:
+            return
+        self.current_file = path
+        self.should_edit_most_recent_plaintext = False
+        if not copyfrom_path:
+            self.ui.status('A %s\n' % path)
+            return
+        self.ui.status('A+ %s\n' % path)
+        (from_file,
+         from_branch) = self._path_and_branch_for_path(copyfrom_path)
+        if not from_file:
+            self.missing_plaintexts.add(path)
+            return
+        ha = self.get_parent_revision(copyfrom_revision + 1,
+                                      from_branch)
+        ctx = self.repo.changectx(ha)
+        if from_file in ctx:
+            fctx = ctx.filectx(from_file)
+            cur_file = self.current_file
+            self.current_files[cur_file] = fctx.data()
+            self.current_files_symlink[cur_file] = 'l' in fctx.flags()
+            self.current_files_exec[cur_file] = 'x' in fctx.flags()
+        if from_branch == branch:
+            self.copies[path] = from_file
 
     @stash_exception_on_self
     def add_directory(self, path, parent_baton, copyfrom_path,
@@ -571,7 +575,8 @@ class HgChangeReceiver(delta.Editor):
                 self.current_files_symlink[fp_c] = 'l' in fctx.flags()
                 if fp_c in self.deleted_files:
                     del self.deleted_files[fp_c]
-                # TODO(augie) tag copies from files
+                if branch == source_branch:
+                    self.copies[fp_c] = f
 
     @stash_exception_on_self
     def change_file_prop(self, file_baton, name, value, pool=None):
new file mode 100755
--- /dev/null
+++ b/tests/fixtures/renames.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Generate renames.svndump
+#
+
+mkdir temp
+cd temp
+
+mkdir project-orig
+cd project-orig
+mkdir trunk
+mkdir branches
+cd ..
+
+svnadmin create testrepo
+svnurl=file://`pwd`/testrepo
+svn import project-orig $svnurl -m "init project"
+
+svn co $svnurl project
+cd project/trunk
+echo a > a
+echo b > b
+mkdir -p da/db
+echo c > da/daf
+echo d > da/db/dbf
+svn add a b da
+svn ci -m "add a and b"
+cd ../branches
+svn cp ../trunk branch1
+svn ci -m "create branch1"
+cd branch1
+echo c > c
+svn add c
+svn ci -m "add c"
+cd ../../trunk
+# Regular copy and rename
+svn cp a a1
+svn mv a a2
+# Copy and update of source and dest
+svn cp b b1
+echo b >> b
+echo c >> b1
+# Directory copy and renaming
+svn cp da da1
+svn mv da da2
+# Test one copy operation in branch
+cd ../branches/branch1
+svn cp c c1
+echo c >> c1
+cd ../..
+svn ci -m "rename and copy a, b and da"
+cd trunk
+# Copy across branch
+svn cp ../branches/branch1/c c
+svn ci -m "copy b from branch1"
+cd ../..
+
+svnadmin dump testrepo > ../renames.svndump
new file mode 100644
--- /dev/null
+++ b/tests/fixtures/renames.svndump
@@ -0,0 +1,393 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 40be4ed3-2bdc-4296-b679-58ef9c475a1c
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-11-01T23:06:00.527827Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 114
+Content-length: 114
+
+K 7
+svn:log
+V 12
+init project
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2008-11-01T23:06:00.567419Z
+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
+
+
+Revision-number: 2
+Prop-content-length: 113
+Content-length: 113
+
+K 7
+svn:log
+V 11
+add a and b
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2008-11-01T23:06:01.198878Z
+PROPS-END
+
+Node-path: trunk/a
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: 60b725f10c9c85c70d97880dfe8191b3
+Content-length: 12
+
+PROPS-END
+a
+
+
+Node-path: trunk/b
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: 3b5d5c3712955042212316173ccf37be
+Content-length: 12
+
+PROPS-END
+b
+
+
+Node-path: trunk/da
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/da/daf
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1
+Content-length: 12
+
+PROPS-END
+c
+
+
+Node-path: trunk/da/db
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/da/db/dbf
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: e29311f6f1bf1af907f9ef9f44b8328b
+Content-length: 12
+
+PROPS-END
+d
+
+
+Revision-number: 3
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 14
+create branch1
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2008-11-01T23:06:03.155920Z
+PROPS-END
+
+Node-path: branches/branch1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Node-path: branches/branch1/a
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/a
+Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3
+
+
+Node-path: branches/branch1/b
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/b
+Text-copy-source-md5: 3b5d5c3712955042212316173ccf37be
+
+
+Node-path: branches/branch1/da
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/da
+
+
+Revision-number: 4
+Prop-content-length: 106
+Content-length: 106
+
+K 7
+svn:log
+V 5
+add c
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2008-11-01T23:06:04.170007Z
+PROPS-END
+
+Node-path: branches/branch1/c
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1
+Content-length: 12
+
+PROPS-END
+c
+
+
+Revision-number: 5
+Prop-content-length: 129
+Content-length: 129
+
+K 7
+svn:log
+V 27
+rename and copy a, b and da
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2008-11-01T23:06:11.193779Z
+PROPS-END
+
+Node-path: branches/branch1/c1
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 4
+Node-copyfrom-path: branches/branch1/c
+Text-copy-source-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1
+Prop-content-length: 34
+Text-content-length: 4
+Text-content-md5: 63fad9092ad37713ebe26b3193f89c41
+Content-length: 38
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+c
+c
+
+
+Node-path: trunk/a1
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/a
+Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Node-path: trunk/a2
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/a
+Text-copy-source-md5: 60b725f10c9c85c70d97880dfe8191b3
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Node-path: trunk/b
+Node-kind: file
+Node-action: change
+Text-content-length: 4
+Text-content-md5: 06ac26ed8b614fc0b141e4542aa067c2
+Content-length: 4
+
+b
+b
+
+
+Node-path: trunk/b1
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/b
+Text-copy-source-md5: 3b5d5c3712955042212316173ccf37be
+Prop-content-length: 34
+Text-content-length: 4
+Text-content-md5: 33cb6785d50937d8d307ebb66d6259a7
+Content-length: 38
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+b
+c
+
+
+Node-path: trunk/da1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/da
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Node-path: trunk/da2
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/da
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Node-path: trunk/a
+Node-action: delete
+
+
+Node-path: trunk/da
+Node-action: delete
+
+
+Revision-number: 6
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 19
+copy b from branch1
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2008-11-01T23:06:13.146723Z
+PROPS-END
+
+Node-path: trunk/c
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 4
+Node-copyfrom-path: branches/branch1/c
+Text-copy-source-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
--- a/tests/run.py
+++ b/tests/run.py
@@ -6,12 +6,14 @@ sys.path.append(os.path.dirname(os.path.
 
 import test_fetch_command
 import test_fetch_command_regexes
+import test_fetch_renames
 import test_push_command
 import test_tags
 
 def suite():
     return unittest.TestSuite([test_fetch_command.suite(),
                                test_fetch_command_regexes.suite(),
+                               test_fetch_renames.suite(),
                                test_push_command.suite(),
                                test_tags.suite(),
                               ])
--- a/tests/test_fetch_command.py
+++ b/tests/test_fetch_command.py
@@ -82,7 +82,7 @@ class TestBasicRepoLayout(unittest.TestC
         self.assertEqual(node.hex(repo[0].node()),
                          'a47d0ce778660a91c31bf2c21c448e9ee296ac90')
         self.assertEqual(node.hex(repo['tip'].node()),
-                         'a7742757189db8aea5f4c6721cbbfb1a09f00ddf')
+                         '179fb7d9bc77eef78288661f0430e0c1dff56b6f')
         self.assertEqual(node.hex(repo['the_branch'].node()),
                          '8ccaba5f0eae124487e413abd904a013f7f6fdeb')
         self.assertEqual(node.hex(repo['the_branch'].parents()[0].node()),
new file mode 100644
--- /dev/null
+++ b/tests/test_fetch_renames.py
@@ -0,0 +1,72 @@
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+from mercurial import hg
+from mercurial import ui
+from mercurial import node
+
+import fetch_command
+import test_util
+
+
+class TestFetchRenames(unittest.TestCase):
+    def setUp(self):
+        self.oldwd = os.getcwd()
+        self.tmpdir = tempfile.mkdtemp('svnwrap_test')
+        self.repo_path = '%s/testrepo' % self.tmpdir
+        self.wc_path = '%s/testrepo_wc' % self.tmpdir
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir)
+        os.chdir(self.oldwd)
+
+    def _load_fixture_and_fetch(self, fixture_name):
+        return test_util.load_fixture_and_fetch(fixture_name, self.repo_path,
+                                                self.wc_path)
+
+    def _debug_print_copies(self, repo):
+        w = sys.stderr.write
+        for rev in repo:
+            ctx = repo[rev]
+            w('%d - %s\n' % (ctx.rev(), ctx.branch()))
+            for f in ctx:
+                fctx = ctx[f]
+                w('%s: %r %r\n' % (f, fctx.data(), fctx.renamed()))
+
+    def test_rename(self):
+        repo = self._load_fixture_and_fetch('renames.svndump')
+        # self._debug_print_copies(repo)
+
+        # Map revnum to mappings of dest name to (source name, dest content)
+        copies = {
+            3: {
+                'a1': ('a', 'a\n'), 
+                'a2': ('a', 'a\n'),
+                'b1': ('b', 'b\nc\n'),
+                'da1/daf': ('da/daf', 'c\n'),
+                'da1/db/dbf': ('da/db/dbf', 'd\n'),
+                'da2/daf': ('da/daf', 'c\n'),
+                'da2/db/dbf': ('da/db/dbf', 'd\n'),
+                },
+            4: {
+                'c1': ('c', 'c\nc\n'),
+                }
+            }
+        for rev in repo:
+            ctx = repo[rev]
+            copymap = copies.get(rev, {})
+            for f in ctx.manifest():
+                cp = ctx[f].renamed()
+                self.assertEqual(bool(cp), bool(copymap.get(f)))
+                if not cp:
+                    continue
+                self.assertEqual(cp[0], copymap[f][0])
+                self.assertEqual(ctx[f].data(), copymap[f][1])
+
+def suite():
+    all = [unittest.TestLoader().loadTestsFromTestCase(TestFetchRenames),
+          ]
+    return unittest.TestSuite(all)