# HG changeset patch # User Patrick Mezard # Date 1241404962 18000 # Node ID 942f198b8ff56592878eec5ba6d1f726981809cb # Parent 4dc197f533c15af6b432c7f43571473d6ed370ee 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. diff --git a/hg_delta_editor.py b/hg_delta_editor.py --- 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] diff --git a/tests/fixtures/renamedproject.sh b/tests/fixtures/renamedproject.sh 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 diff --git a/tests/fixtures/renamedproject.svndump b/tests/fixtures/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 + + diff --git a/tests/test_fetch_branches.py b/tests/test_fetch_branches.py --- 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), ]