changeset 59:430af23bef4a

Performance fix for branches-from-tags in real replay, which is tied up with changes that fix problems when trunk is not the oldest branch. Also includes fixes for copying from a tag that we chose not to create (eg tagging a vendor branch) and includes tests for all of those things.
author Augie Fackler <durin42@gmail.com>
date Tue, 04 Nov 2008 16:38:16 -0600
parents a8b9c7e7c2ac
children 41dc00c7aef1
files fetch_command.py hg_delta_editor.py svnwrap/svn_swig_wrapper.py tests/fixtures/tagged_vendor_and_oldest_not_trunk.svndump tests/test_fetch_command.py
diffstat 5 files changed, 387 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -435,7 +435,8 @@ def stupid_svn_server_pull_rev(ui, svn, 
                         files_touched.add(d)
         if delete_all_files:
             for p in hg_editor.repo[parent_ha].manifest().iterkeys():
-                files_touched.add(p)
+                if p:
+                    files_touched.add(p)
         if not used_diff:
             for p in reduce(operator.add, [[os.path.join(x[0], y) for y in x[2]]
                                            for x in
@@ -447,7 +448,8 @@ def stupid_svn_server_pull_rev(ui, svn, 
                 files_touched.add(p_real)
             for p in hg_editor.repo[parent_ha].manifest().iterkeys():
                 # TODO this might not be a required step.
-                files_touched.add(p)
+                if p:
+                    files_touched.add(p)
         date = r.date.replace('T', ' ').replace('Z', '').split('.')[0]
         date += ' -0000'
         def filectxfn(repo, memctx, path):
@@ -465,6 +467,8 @@ def stupid_svn_server_pull_rev(ui, svn, 
         extra = {}
         if b:
             extra['branch'] = b
+        if '' in files_touched:
+            files_touched.remove('')
         if parent_ha != node.nullid or files_touched:
             # TODO(augie) remove this debug code? Or maybe it's sane to have it.
             for f in files_touched:
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -246,16 +246,17 @@ class HgChangeReceiver(delta.Editor):
         added_branches = {}
         added_tags = {}
         tags_to_delete = set()
+        branches_to_delete = set()
         for p in paths:
             if self._is_path_valid(p):
                 fi, br = self._path_and_branch_for_path(p)
                 if fi == '' and br not in self.branches:
-                    # TODO handle creating a branch from a tag
                     src_p = paths[p].copyfrom_path
                     src_rev = paths[p].copyfrom_rev
                     src_tag = self._is_path_tag(src_p)
 
-                    if not src_p or not (self._is_path_valid(src_p) or src_tag):
+                    if not ((src_p and self._is_path_valid(src_p)) or
+                            (src_tag and src_tag in self.tags)):
                         # we'll imply you're a branch off of trunk
                         # if you have no path, but if you do, it must be valid
                         # or else we assume trunk as well
@@ -272,6 +273,10 @@ class HgChangeReceiver(delta.Editor):
                         (src_p,
                         src_branch) = self._path_and_branch_for_path(src_p)
                     added_branches[br] = src_branch, src_rev, revision.revnum
+                elif fi == '' and br in self.branches:
+                    br2 = br or 'default'
+                    if br2 not in self.repo.branchtags() and paths[p].action == 'D':
+                        branches_to_delete.add(br)
                 elif br in added_branches:
                     if paths[p].copyfrom_rev > added_branches[br][1]:
                         x,y,z = added_branches[br]
@@ -302,6 +307,8 @@ class HgChangeReceiver(delta.Editor):
                         tags_to_delete.add(t_name)
         for t in tags_to_delete:
             del self.tags[t]
+        for br in branches_to_delete:
+            del self.branches[br]
         self.tags.update(added_tags)
         self.branches.update(added_branches)
         self._save_metadata()
@@ -339,11 +346,8 @@ class HgChangeReceiver(delta.Editor):
             parents = (self.get_parent_revision(rev.revnum, branch),
                        revlog.nullid)
             if branch is not None:
-                if branch not in self.branches:
+                if branch not in self.branches and branch not in self.repo.branchtags():
                     continue
-                if parents == (revlog.nullid, revlog.nullid):
-                    assert False, ('a non-trunk branch should probably have'
-                                   ' parents figured out by this point')
                 extra['branch'] = branch
             parent_ctx = self.repo.changectx(parents[0])
             def filectxfn(repo, memctx, path):
@@ -353,9 +357,9 @@ class HgChangeReceiver(delta.Editor):
                     raise IOError()
                 # TODO(augie) tag copies from files
                 flags = parent_ctx.flags(path)
-                is_exec = self.current_files_exec.get(current_file, 
+                is_exec = self.current_files_exec.get(current_file,
                                                       'x' in flags)
-                is_link = self.current_files_symlink.get(current_file, 
+                is_link = self.current_files_symlink.get(current_file,
                                                          'l' in flags)
                 if current_file in self.current_files:
                     data = self.current_files[current_file]
@@ -497,7 +501,7 @@ 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 
+        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
@@ -533,12 +537,22 @@ class HgChangeReceiver(delta.Editor):
                 self.commit_branches_empty[branch] = False
         if not self._is_path_valid(path) or not copyfrom_path:
             return
-        if copyfrom_path and not self._is_path_valid(copyfrom_path):
-            self.missing_plaintexts.add('%s/' % path)
-            return
+        if copyfrom_path:
+            tag = self._is_path_tag(copyfrom_path)
+            if tag not in self.tags:
+                tag = None
+            if not self._is_path_valid(copyfrom_path) and not tag:
+                self.missing_plaintexts.add('%s/' % path)
+                return
 
-        cp_f, br_from = self._path_and_branch_for_path(copyfrom_path)
-        new_hash = self.get_parent_revision(copyfrom_revision + 1, br_from)
+        if tag:
+            source_branch, source_rev = self.tags[tag]
+            cp_f = ''
+        else:
+            source_rev = copyfrom_revision
+            cp_f, source_branch = self._path_and_branch_for_path(copyfrom_path)
+        new_hash = self.get_parent_revision(source_rev + 1,
+                                            source_branch)
         if new_hash == node.nullid:
             self.missing_plaintexts.add('%s/' % path)
             return
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -337,7 +337,7 @@ class SubversionRepo(object):
         except core.SubversionException, e:
             # can I depend on this number being constant?
             if (e.message == "Server doesn't support the replay command"
-                or e.apr_err == 170003 
+                or e.apr_err == 170003
                 or e.message == 'The requested report is unknown.'
                 or e.apr_err == 200007):
                 raise SubversionRepoCanNotReplay, ('This Subversion server '
new file mode 100644
--- /dev/null
+++ b/tests/fixtures/tagged_vendor_and_oldest_not_trunk.svndump
@@ -0,0 +1,325 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 0e935f28-8caa-dd11-b3dc-00105ae0362c
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-11-04T16:18:04.048615Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 102
+Content-length: 102
+
+K 7
+svn:log
+V 3
+btt
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2008-11-04T16:18:29.661251Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: tags
+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: 119
+Content-length: 119
+
+K 7
+svn:log
+V 19
+Add the vendor dir.
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2008-11-04T16:19:16.816658Z
+PROPS-END
+
+Node-path: vendor
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: vendor/foobaz
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: vendor/foobaz/alpha
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: 9f9f90dbe3e5ee1218c86b8839db1995
+Content-length: 16
+
+PROPS-END
+alpha
+
+
+Node-path: vendor/foobaz/beta
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 5
+Text-content-md5: f0cf2a92516045024a0c99147b28f05b
+Content-length: 15
+
+PROPS-END
+beta
+
+
+Node-path: vendor/foobaz/delta
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: d2840cc81bc032bd1141b56687d0f93c
+Content-length: 16
+
+PROPS-END
+delta
+
+
+Node-path: vendor/foobaz/gamma
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: 303febb9068384eca46b5b6516843b35
+Content-length: 16
+
+PROPS-END
+gamma
+
+
+Revision-number: 3
+Prop-content-length: 139
+Content-length: 139
+
+K 7
+svn:log
+V 39
+Adding oldest data, which is not trunk.
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2008-11-04T16:22:06.704260Z
+PROPS-END
+
+Node-path: branches/oldest
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: branches/oldest/five
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 5
+Text-content-md5: 014835e36358e38c7f7897d6571e4529
+Content-length: 15
+
+PROPS-END
+five
+
+
+Node-path: branches/oldest/four
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 5
+Text-content-md5: 75ffdb827341e578959bfcabde3789d8
+Content-length: 15
+
+PROPS-END
+four
+
+
+Node-path: branches/oldest/one
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: 5bbf5a52328e7439ae6e719dfe712200
+Content-length: 14
+
+PROPS-END
+one
+
+
+Node-path: branches/oldest/three
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: febe6995bad457991331348f7b9c85fa
+Content-length: 16
+
+PROPS-END
+three
+
+
+Node-path: branches/oldest/two
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: c193497a1a06b2c72230e6146ff47080
+Content-length: 14
+
+PROPS-END
+two
+
+
+Revision-number: 4
+Prop-content-length: 105
+Content-length: 105
+
+K 7
+svn:log
+V 6
+delete
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2008-11-04T16:22:49.936769Z
+PROPS-END
+
+Node-path: trunk
+Node-action: delete
+
+
+Revision-number: 5
+Prop-content-length: 124
+Content-length: 124
+
+K 7
+svn:log
+V 24
+create trunk from branch
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2008-11-04T16:23:00.562964Z
+PROPS-END
+
+Node-path: tags/foobaz_1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: vendor/foobaz
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 3
+Node-copyfrom-path: branches/oldest
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Revision-number: 6
+Prop-content-length: 146
+Content-length: 146
+
+K 7
+svn:log
+V 46
+copy data from a vendor branch tag into trunk.
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2008-11-04T16:23:32.980956Z
+PROPS-END
+
+Node-path: trunk/foobaz
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 5
+Node-copyfrom-path: tags/foobaz_1
+
+
--- a/tests/test_fetch_command.py
+++ b/tests/test_fetch_command.py
@@ -109,6 +109,18 @@ class TestBasicRepoLayout(unittest.TestC
                     'file_renamed_in_from_outside_btt.svndump')
         self.assert_('LICENSE.file' in repo['tip'])
 
+    def test_oldest_not_trunk_and_tag_vendor_branch(self):
+        repo = self._load_fixture_and_fetch(
+            'tagged_vendor_and_oldest_not_trunk.svndump')
+        self.assertEqual(node.hex(repo['oldest'].node()),
+                         'd73002bcdeffe389a8df81ee43303d36e79e8ca4')
+        self.assertEqual(repo['tip'].parents()[0].parents()[0],
+                         repo['oldest'])
+        self.assertEqual(node.hex(repo['tip'].node()),
+                         '9cf09e6ff7fa938188c3bcc9dd87abd7842c080c')
+        #'1316ef606dda89354ee8c4df725e6264177b5129')
+
+
 class TestStupidPull(unittest.TestCase):
     def setUp(self):
         self.oldwd = os.getcwd()
@@ -140,6 +152,21 @@ class TestStupidPull(unittest.TestCase):
         self.assertEqual(repo['tip'], repo['default'])
         self.assertEqual(len(repo.heads()), 2)
 
+    def test_oldest_not_trunk_and_tag_vendor_branch(self):
+        test_util.load_svndump_fixture(self.repo_path,
+                                'tagged_vendor_and_oldest_not_trunk.svndump')
+        fetch_command.fetch_revisions(ui.ui(),
+                                      svn_url='file://%s' % self.repo_path,
+                                      hg_repo_path=self.wc_path,
+                                      stupid=True)
+        repo = hg.repository(ui.ui(), self.wc_path)
+        self.assertEqual(node.hex(repo['oldest'].node()),
+                         'd73002bcdeffe389a8df81ee43303d36e79e8ca4')
+        self.assertEqual(repo['tip'].parents()[0].parents()[0],
+                         repo['oldest'])
+        self.assertEqual(node.hex(repo['tip'].node()),
+                         '9cf09e6ff7fa938188c3bcc9dd87abd7842c080c')
+
 def suite():
     all = [unittest.TestLoader().loadTestsFromTestCase(TestBasicRepoLayout),
            unittest.TestLoader().loadTestsFromTestCase(TestStupidPull),