changeset 313:942f198b8ff5

hg_delta_editor: detect new branches issued from non-branch directories This fix solves the following case: let's /dumb/layout/project be an existing project. To normalize the trunk/branches/tags layout, people may do: $ mkdir /project $ mv /dumb/layout/project /project/project # Oups, should have been trunk! $ mv /project/project /project/trunk trunk creation was ignore because: - update_branch_map() sees it come from a non-branch copy source and ignores it (case #3). - since it is not in self.branches, add_directory() ignores the non-existing path. Then trunk is left uninitialized. To solve this, we allow update_branch_map() to detect branches copied from non-canonical locations.
author Patrick Mezard <pmezard@gmail.com>
date Sun, 03 May 2009 21:42:42 -0500
parents 4dc197f533c1
children 2257bfc01749
files hg_delta_editor.py tests/fixtures/renamedproject.sh tests/fixtures/renamedproject.svndump tests/test_fetch_branches.py
diffstat 4 files changed, 621 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -297,8 +297,8 @@ class HgChangeReceiver(delta.Editor):
             return path[len(test)+1:], self._localname(test), test
         if existing:
             return None, None, None
-        if path.startswith('trunk/'):
-            path = test.split('/')[1:]
+        if path == 'trunk' or path.startswith('trunk/'):
+            path = path.split('/')[1:]
             test = 'trunk'
         elif path.startswith('branches/'):
             elts = path.split('/')
@@ -497,10 +497,10 @@ class HgChangeReceiver(delta.Editor):
             # 1. Is the file located inside any currently known
             #    branch?  If yes, then we're done with it, this isn't
             #    interesting.
-            # 2. Does the file have copyfrom information that means it
-            #    is a copy from the root of some other branch?  If
-            #    yes, then we're done: this is a new branch, and we
-            #    record the copyfrom in added_branches
+            # 2. Does the file have copyfrom information? If yes, then
+            #    we're done: this is a new branch, and we record the
+            #    copyfrom in added_branches if it comes from the root
+            #    of another branch, or create it from scratch.
             # 3. Neither of the above. This could be a branch, but it
             #    might never work out for us. It's only ever a branch
             #    (as far as we're concerned) if it gets committed to,
@@ -529,6 +529,10 @@ class HgChangeReceiver(delta.Editor):
                         self.branches_to_delete.add(known) # case 5
             parent = self._determine_parent_branch(
                 p, paths[p].copyfrom_path, paths[p].copyfrom_rev, revision.revnum)
+            if not parent and paths[p].copyfrom_path:
+                bpath, branch = self._path_and_branch_for_path(p, False)
+                if bpath is not None and branch not in self.branches:
+                    parent = {branch: (None, 0, revision.revnum)}
             added_branches.update(parent)
         for t in tags_to_delete:
             del self.tags[t]
new file mode 100755
--- /dev/null
+++ b/tests/fixtures/renamedproject.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Convert a project moving from a non-canonical to canonical
+# layout, exercizing the missing plaintext code paths. It also tests
+# branch creations where the branch source is not a canonical branch.
+#
+
+mkdir temp
+cd temp
+
+svnadmin create testrepo
+svnurl=file://`pwd`/testrepo
+
+mkdir project-orig
+cd project-orig
+echo a > a
+echo b > b
+echo c > c
+mkdir d
+echo a > d/a
+cd ..
+
+# Let's suppose it was actually branched in a previous life
+mkdir project-branch
+cd project-branch
+echo a > a
+echo b > b
+cd ..
+
+svn import project-orig $svnurl/project-orig -m "init project"
+svn import project-branch $svnurl/project-branch -m "init branch"
+
+svn mkdir $svnurl/project -m "create new project hierarchy"
+svn mv $svnurl/project-orig $svnurl/project/project -m "rename as project"
+svn mv $svnurl/project/project $svnurl/project/trunk -m "rename as project"
+
+svn mkdir $svnurl/project/branches -m "add branches root"
+svn mv $svnurl/project-branch $svnurl/project/misplaced -m "incorrect move of the branch"
+svn mv $svnurl/project/misplaced $svnurl/project/branches/branch -m "move of the branch"
+
+svn co $svnurl/project
+cd project
+echo a >> trunk/a
+svn ci -m "change a"
+echo a >> trunk/a
+echo b >> trunk/b
+svn rm trunk/c
+echo a >> trunk/d/a
+svn ci -m "change files in trunk"
+# Try the same thing with the branch
+echo a >> branches/branch/a
+svn rm branches/branch/b
+svn ci -m "change a in branch"
+cd ..
+
+# Add this to make test_rebuildmeta happy, needs something to convert
+svn import project-orig $svnurl/trunk -m "init fake trunk for rebuild_meta"
+
+svnadmin dump testrepo > ../renamedproject.svndump
new file mode 100644
--- /dev/null
+++ b/tests/fixtures/renamedproject.svndump
@@ -0,0 +1,536 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 169a5fe1-3c9f-4eef-ad86-f932c54e53dc
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2009-05-01T17:53:40.957980Z
+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
+2009-05-01T17:53:41.129010Z
+PROPS-END
+
+Node-path: project-orig
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: project-orig/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: project-orig/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: project-orig/c
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1
+Text-content-sha1: 2b66fd261ee5c6cfc8de7fa466bab600bcfe4f69
+Content-length: 12
+
+PROPS-END
+c
+
+
+Node-path: project-orig/d
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: project-orig/d/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
+
+
+Revision-number: 2
+Prop-content-length: 113
+Content-length: 113
+
+K 7
+svn:log
+V 11
+init branch
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:41.193926Z
+PROPS-END
+
+Node-path: project-branch
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: project-branch/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: project-branch/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
+
+
+Revision-number: 3
+Prop-content-length: 130
+Content-length: 130
+
+K 7
+svn:log
+V 28
+create new project hierarchy
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:41.236131Z
+PROPS-END
+
+Node-path: project
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 4
+Prop-content-length: 119
+Content-length: 119
+
+K 7
+svn:log
+V 17
+rename as project
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:41.288164Z
+PROPS-END
+
+Node-path: project/project
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 3
+Node-copyfrom-path: project-orig
+
+
+Node-path: project-orig
+Node-action: delete
+
+
+Revision-number: 5
+Prop-content-length: 119
+Content-length: 119
+
+K 7
+svn:log
+V 17
+rename as project
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:41.335332Z
+PROPS-END
+
+Node-path: project/trunk
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 4
+Node-copyfrom-path: project/project
+
+
+Node-path: project/project
+Node-action: delete
+
+
+Revision-number: 6
+Prop-content-length: 119
+Content-length: 119
+
+K 7
+svn:log
+V 17
+add branches root
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:41.377684Z
+PROPS-END
+
+Node-path: project/branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 7
+Prop-content-length: 130
+Content-length: 130
+
+K 7
+svn:log
+V 28
+incorrect move of the branch
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:41.426928Z
+PROPS-END
+
+Node-path: project/misplaced
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 6
+Node-copyfrom-path: project-branch
+
+
+Node-path: project-branch
+Node-action: delete
+
+
+Revision-number: 8
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 18
+move of the branch
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:41.478644Z
+PROPS-END
+
+Node-path: project/branches/branch
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 7
+Node-copyfrom-path: project/misplaced
+
+
+Node-path: project/misplaced
+Node-action: delete
+
+
+Revision-number: 9
+Prop-content-length: 109
+Content-length: 109
+
+K 7
+svn:log
+V 8
+change a
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:42.078428Z
+PROPS-END
+
+Node-path: project/trunk/a
+Node-kind: file
+Node-action: change
+Text-content-length: 4
+Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb
+Text-content-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29
+Content-length: 4
+
+a
+a
+
+
+Revision-number: 10
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 21
+change files in trunk
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:43.109915Z
+PROPS-END
+
+Node-path: project/trunk/a
+Node-kind: file
+Node-action: change
+Text-content-length: 6
+Text-content-md5: 7d4ebf8f298d22fc349a91725b00af1c
+Text-content-sha1: 92f31bc48f52339253fce6cad9f2f0c95b302f7e
+Content-length: 6
+
+a
+a
+a
+
+
+Node-path: project/trunk/b
+Node-kind: file
+Node-action: change
+Text-content-length: 4
+Text-content-md5: 06ac26ed8b614fc0b141e4542aa067c2
+Text-content-sha1: f6980469e74f7125178e88ec571e06fe6ce86e95
+Content-length: 4
+
+b
+b
+
+
+Node-path: project/trunk/d/a
+Node-kind: file
+Node-action: change
+Text-content-length: 4
+Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb
+Text-content-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29
+Content-length: 4
+
+a
+a
+
+
+Node-path: project/trunk/c
+Node-action: delete
+
+
+Revision-number: 11
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 18
+change a in branch
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:44.100553Z
+PROPS-END
+
+Node-path: project/branches/branch/a
+Node-kind: file
+Node-action: change
+Text-content-length: 4
+Text-content-md5: 0d227f1abf8c2932d342e9b99cc957eb
+Text-content-sha1: d7c8127a20a396cff08af086a1c695b0636f0c29
+Content-length: 4
+
+a
+a
+
+
+Node-path: project/branches/branch/b
+Node-action: delete
+
+
+Revision-number: 12
+Prop-content-length: 134
+Content-length: 134
+
+K 7
+svn:log
+V 32
+init fake trunk for rebuild_meta
+K 10
+svn:author
+V 7
+pmezard
+K 8
+svn:date
+V 27
+2009-05-01T17:53:45.089483Z
+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/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/c
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2
+Text-content-md5: 2cd6ee2c70b0bde53fbe6cac3c8b8bb1
+Text-content-sha1: 2b66fd261ee5c6cfc8de7fa466bab600bcfe4f69
+Content-length: 12
+
+PROPS-END
+c
+
+
+Node-path: trunk/d
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/d/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
+
+
--- a/tests/test_fetch_branches.py
+++ b/tests/test_fetch_branches.py
@@ -9,10 +9,11 @@ import wrappers
 
 
 class TestFetchBranches(test_util.TestBase):
-    def _load_fixture_and_fetch(self, fixture_name, stupid, noupdate=True):
+    def _load_fixture_and_fetch(self, fixture_name, stupid, noupdate=True,
+                                subdir=''):
         return test_util.load_fixture_and_fetch(fixture_name, self.repo_path,
                                                 self.wc_path, stupid=stupid,
-                                                noupdate=noupdate)
+                                                noupdate=noupdate, subdir=subdir)
 
     def _load_fixture_and_fetch_with_anchor(self, fixture_name, anchor):
         test_util.load_svndump_fixture(self.repo_path, fixture_name)
@@ -84,6 +85,19 @@ class TestFetchBranches(test_util.TestBa
         self.assertEqual(repo[None].branch(), 'branch')
         self.assertEqual(repo[None].parents()[0], repo[repo.branchheads()[0]])
 
+    def test_branches_weird_moves(self, stupid=False):
+        repo = self._load_fixture_and_fetch('renamedproject.svndump', stupid,
+                                            subdir='project')
+        heads = [repo[n] for n in repo.heads()]
+        heads = dict((ctx.branch(), ctx) for ctx in heads)
+        mdefault = sorted(heads['default'].manifest().keys())
+        mbranch = sorted(heads['branch'].manifest().keys())
+        self.assertEqual(mdefault, ['a', 'b', 'd/a'])
+        self.assertEqual(mbranch, ['a'])
+
+    def test_branches_weird_moves_stupid(self):
+        self.test_branches_weird_moves(True)
+
 def suite():
     all = [unittest.TestLoader().loadTestsFromTestCase(TestFetchBranches),
           ]