# HG changeset patch # User Jun Fang # Date 1344274551 25200 # Node ID 6b7ac659c85574bb819650848af501c95b825baa # Parent 8faa91951bb10b580cbfc2ad065cd26aae47c618 updatemeta: correctly handle empty metadata When the repo metadata is empty (just created/cloned but not populated yet), there is no lastpulled file or the revmap doesn't have a last entry with hash. Currently "hg svn updatemeta" would crash due to unexpected exception. See issue reported at https://bitbucket.org/durin42/hgsubversion/issue/356/updatemeta-crashes-with-traceback-if-there The fix is to check the existence of lastpulled file and the hash entry in revmap, as part of "hg svn updatemeta" command. When they are not present, do a rebuildmetadata. Also added a new unit test, test_updatemeta.py. diff --git a/hgsubversion/svncommands.py b/hgsubversion/svncommands.py --- a/hgsubversion/svncommands.py +++ b/hgsubversion/svncommands.py @@ -64,12 +64,19 @@ def _buildmeta(ui, repo, args, partial=F if partial: try: youngestpath = os.path.join(svnmetadir, 'lastpulled') - youngest = int(util.load_string(youngestpath).strip()) - sofar = list(maps.RevMap.readmapfile(repo)) - lasthash = sofar[-1].split(' ', 2)[1] - startrev = repo[lasthash].rev() + 1 - branchinfo = pickle.load(open(os.path.join(svnmetadir, - 'branch_info'))) + foundpartialinfo = False + if os.path.exists(youngestpath): + youngest = int(util.load_string(youngestpath).strip()) + sofar = list(maps.RevMap.readmapfile(repo)) + if sofar and len(sofar[-1].split(' ', 2)) > 1: + lasthash = sofar[-1].split(' ', 2)[1] + startrev = repo[lasthash].rev() + 1 + branchinfo = pickle.load(open(os.path.join(svnmetadir, + 'branch_info'))) + foundpartialinfo = True + if not foundpartialinfo: + ui.status('missing some metadata -- doing a full rebuild\n') + partial = False except IOError, err: if err.errno != errno.ENOENT: raise diff --git a/tests/run.py b/tests/run.py --- a/tests/run.py +++ b/tests/run.py @@ -32,6 +32,7 @@ def tests(): import test_template_keywords import test_utility_commands import test_unaffected_core + import test_updatemeta import test_urls sys.path.append(os.path.dirname(__file__)) diff --git a/tests/test_updatemeta.py b/tests/test_updatemeta.py new file mode 100644 --- /dev/null +++ b/tests/test_updatemeta.py @@ -0,0 +1,81 @@ +import test_util + +import os +import pickle +import unittest +import test_rebuildmeta + +from mercurial import context +from mercurial import extensions +from mercurial import hg +from mercurial import ui + +from hgsubversion import svncommands +from hgsubversion import svnmeta + + + +def _do_case(self, name, stupid, single): + subdir = test_util.subdir.get(name, '') + layout = 'auto' + if single: + layout = 'single' + repo, repo_path = self.load_and_fetch(name, subdir=subdir, stupid=stupid, + layout=layout) + assert len(self.repo) > 0 + wc2_path = self.wc_path + '_clone' + u = ui.ui() + src, dest = test_util.hgclone(u, self.wc_path, wc2_path, update=False) + src = getattr(src, 'local', lambda: src)() + dest = getattr(dest, 'local', lambda: dest)() + + # insert a wrapper that prevents calling changectx.children() + def failfn(orig, ctx): + self.fail('calling %s is forbidden; it can cause massive slowdowns ' + 'when rebuilding large repositories' % orig) + + origchildren = getattr(context.changectx, 'children') + extensions.wrapfunction(context.changectx, 'children', failfn) + + # test updatemeta on an empty repo + try: + svncommands.updatemeta(u, dest, + args=[test_util.fileurl(repo_path + + subdir), ]) + finally: + # remove the wrapper + context.changectx.children = origchildren + + self._run_assertions(name, stupid, single, src, dest, u) + + +def _run_assertions(self, name, stupid, single, src, dest, u): + test_rebuildmeta._run_assertions(self, name, stupid, single, src, dest, u) + + +skip = set([ + 'project_root_not_repo_root.svndump', + 'corrupt.svndump', +]) + +attrs = {'_do_case': _do_case, + '_run_assertions': _run_assertions, + } +for case in [f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')]: + # this fixture results in an empty repository, don't use it + if case in skip: + continue + bname = 'test_' + case[:-len('.svndump')] + attrs[bname] = test_rebuildmeta.buildmethod(case, bname, False, False) + name = bname + '_stupid' + attrs[name] = test_rebuildmeta.buildmethod(case, name, True, False) + name = bname + '_single' + attrs[name] = test_rebuildmeta.buildmethod(case, name, False, True) + +UpdateMetaTests = type('UpdateMetaTests', (test_util.TestBase,), attrs) + + +def suite(): + all_tests = [unittest.TestLoader().loadTestsFromTestCase(UpdateMetaTests), + ] + return unittest.TestSuite(all_tests)