# HG changeset patch # User Patrick Mezard # Date 1225888627 -3600 # Node ID e319c91689104301974121a5fdfb5d829e32caca # Parent a31968146f3cd9e88398ca9481f5b390546c1be5 hg_delta_editor: register svn file copies diff --git a/hg_delta_editor.py b/hg_delta_editor.py --- 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): diff --git a/tests/fixtures/renames.sh b/tests/fixtures/renames.sh 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 diff --git a/tests/fixtures/renames.svndump b/tests/fixtures/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 + + diff --git a/tests/run.py b/tests/run.py --- 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(), ]) diff --git a/tests/test_fetch_command.py b/tests/test_fetch_command.py --- 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()), diff --git a/tests/test_fetch_renames.py b/tests/test_fetch_renames.py 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)