# HG changeset patch # User Augie Fackler # Date 1225838296 21600 # Node ID 430af23bef4a0f7f321a2514fd3f4b47a096403e # Parent a8b9c7e7c2ac2eec48686533b347d5e600e9e743 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. diff --git a/fetch_command.py b/fetch_command.py --- 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: diff --git a/hg_delta_editor.py b/hg_delta_editor.py --- 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 diff --git a/svnwrap/svn_swig_wrapper.py b/svnwrap/svn_swig_wrapper.py --- 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 ' diff --git a/tests/fixtures/tagged_vendor_and_oldest_not_trunk.svndump b/tests/fixtures/tagged_vendor_and_oldest_not_trunk.svndump 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 + + 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 @@ -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),