changeset 922:6b7ac659c855

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.
author Jun Fang <junfang@fb.com>
date Mon, 06 Aug 2012 10:35:51 -0700
parents 8faa91951bb1
children 847953806c77
files hgsubversion/svncommands.py tests/run.py tests/test_updatemeta.py
diffstat 3 files changed, 95 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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__))
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)