changeset 893:295a8b48e4e2

Merge
author Augie Fackler <raf@durin42.com>
date Sat, 12 May 2012 10:07:29 -0500
parents 3bfb7e985c47 (current diff) 83cc6e9e8425 (diff)
children 9562f606c4aa
files hgsubversion/svncommands.py tests/test_rebuildmeta.py
diffstat 5 files changed, 100 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/hgsubversion/editor.py
+++ b/hgsubversion/editor.py
@@ -364,7 +364,6 @@ class HgEditor(svnwrap.Editor):
                 else: # pragma: no cover
                     raise hgutil.Abort(*e.args)
             except: # pragma: no cover
-                print len(base), self.current.file
                 self._exception_info = sys.exc_info()
                 raise
         return txdelt_window
--- a/hgsubversion/maps.py
+++ b/hgsubversion/maps.py
@@ -135,8 +135,7 @@ class Tags(dict):
             svncommands.rebuildmeta(repo.ui, repo, ())
             return
         elif ver != self.VERSION:
-            print 'tagmap too new -- please upgrade'
-            raise NotImplementedError
+            raise hgutil.Abort('tagmap too new -- please upgrade')
         for l in f:
             ha, revision, tag = l.split(' ', 2)
             revision = int(revision)
@@ -228,8 +227,7 @@ class RevMap(dict):
             return iter([])
         ver = int(f.readline())
         if ver != cls.VERSION:
-            print 'revmap too new -- please upgrade'
-            raise NotImplementedError
+            raise hgutil.Abort('revmap too new -- please upgrade')
         return f
 
     def _load(self):
@@ -361,8 +359,7 @@ class FileMap(object):
         f = open(self.path)
         ver = int(f.readline())
         if ver != self.VERSION:
-            print 'filemap too new -- please upgrade'
-            raise NotImplementedError
+            raise hgutil.Abort('filemap too new -- please upgrade')
         self.load_fd(f, self.path)
         f.close()
 
--- a/hgsubversion/svncommands.py
+++ b/hgsubversion/svncommands.py
@@ -87,11 +87,26 @@ def verify(ui, repo, args=None, **opts):
 
     return result
 
+def updatemeta(ui, repo, args, **opts):
+    """Do a partial rebuild of the subversion metadata.
+
+    Assumes that the metadata that currently exists is valid, but that
+    some is missing, e.g. because you have pulled some revisions via a
+    native mercurial method.
+
+    """
+
+    return _buildmeta(ui, repo, args, partial=True)
+
 
 def rebuildmeta(ui, repo, args, **opts):
     """rebuild hgsubversion metadata using values stored in revisions
     """
 
+    return _buildmeta(ui, repo, args, partial=False)
+
+def _buildmeta(ui, repo, args, partial=False):
+
     if repo is None:
         raise error.RepoError("There is no Mercurial repository"
                               " here (.hg not found)")
@@ -110,14 +125,32 @@ def rebuildmeta(ui, repo, args, **opts):
     if not os.path.exists(svnmetadir):
         os.makedirs(svnmetadir)
 
+    youngest = 0
+    startrev = 0
+    sofar = []
+    branchinfo = {}
+    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')))
+        except IOError, err:
+            if err.errno != errno.ENOENT:
+                raise
+            ui.status('missing some metadata -- doing a full rebuild')
+
+
     lastpulled = open(os.path.join(svnmetadir, 'lastpulled'), 'wb')
     revmap = open(os.path.join(svnmetadir, 'rev_map'), 'w')
     revmap.write('1\n')
+    revmap.writelines(sofar)
     last_rev = -1
-    branchinfo = {}
-    noderevnums = {}
     tagfile = os.path.join(svnmetadir, 'tagmap')
-    if os.path.exists(maps.Tags.filepath(repo)):
+    if not partial and os.path.exists(maps.Tags.filepath(repo)) :
         os.unlink(maps.Tags.filepath(repo))
     tags = maps.Tags(repo)
 
@@ -126,7 +159,7 @@ def rebuildmeta(ui, repo, args, **opts):
     skipped = set()
     closed = set()
 
-    numrevs = len(repo)
+    numrevs = len(repo) - startrev
 
     subdirfile = open(os.path.join(svnmetadir, 'subdir'), 'w')
     subdirfile.write(subdir.strip('/'))
@@ -136,9 +169,8 @@ def rebuildmeta(ui, repo, args, **opts):
     # it would make us use O(revisions^2) time, so we perform an extra traversal
     # of the repository instead. During this traversal, we find all converted
     # changesets that close a branch, and store their first parent
-    youngest = 0
-    for rev in repo:
-        util.progress(ui, 'prepare', rev, total=numrevs)
+    for rev in xrange(startrev, len(repo)):
+        util.progress(ui, 'prepare', rev - startrev, total=numrevs)
         ctx = repo[rev]
         convinfo = util.getsvnrev(ctx, None)
         if not convinfo:
@@ -154,13 +186,19 @@ def rebuildmeta(ui, repo, args, **opts):
         parentinfo = util.getsvnrev(parentctx, '@')
 
         if droprev(parentinfo) == droprev(convinfo):
-            closed.add(parentctx.rev())
+            if parentctx.rev() < startrev:
+                parentbranch = parentctx.branch()
+                if parentbranch == 'default':
+                    parentbranch = None
+                branchinfo.pop(parentbranch)
+            else:
+                closed.add(parentctx.rev())
 
     lastpulled.write(str(youngest) + '\n')
     util.progress(ui, 'prepare', None, total=numrevs)
 
-    for rev in repo:
-        util.progress(ui, 'rebuild', rev, total=numrevs)
+    for rev in xrange(startrev, len(repo)):
+        util.progress(ui, 'rebuild', rev-startrev, total=numrevs)
         ctx = repo[rev]
         convinfo = util.getsvnrev(ctx, None)
         if not convinfo:
@@ -238,7 +276,6 @@ def rebuildmeta(ui, repo, args, **opts):
         revmap.write('%s %s %s\n' % (revision, ctx.hex(), commitpath))
 
         revision = int(revision)
-        noderevnums[ctx.node()] = revision
         if revision > last_rev:
             last_rev = revision
 
@@ -276,15 +313,19 @@ def rebuildmeta(ui, repo, args, **opts):
             pass
         elif branch not in branchinfo:
             parent = ctx.parents()[0]
-            if (parent.node() in noderevnums
+            if (parent.node() not in skipped
+                and util.getsvnrev(parent, '').startswith('svn:')
                 and parent.branch() != ctx.branch()):
                 parentbranch = parent.branch()
                 if parentbranch == 'default':
                     parentbranch = None
             else:
                 parentbranch = None
+            # branchinfo is a map from mercurial branch to a
+            # (svn branch, svn parent revision, svn revision) tuple
+            parentrev = util.getsvnrev(parent, '@').split('@')[1] or 0
             branchinfo[branch] = (parentbranch,
-                                  noderevnums.get(parent.node(), 0),
+                                  int(parentrev),
                                   revision)
 
     util.progress(ui, 'rebuild', None, total=numrevs)
@@ -519,6 +560,7 @@ table = {
     'listauthors': listauthors,
     'update': update,
     'help': help_,
+    'updatemeta': updatemeta,
     'rebuildmeta': rebuildmeta,
     'updateexternals': svnexternals.updateexternals,
     'verify': verify,
--- a/tests/test_rebuildmeta.py
+++ b/tests/test_rebuildmeta.py
@@ -51,6 +51,43 @@ def _do_case(self, name, stupid, single)
         # remove the wrapper
         context.changectx.children = origchildren
 
+    self._run_assertions(name, stupid, single, src, dest, u)
+
+    wc3_path = self.wc_path + '_partial'
+    src, dest = test_util.hgclone(u,
+                                  self.wc_path,
+                                  wc3_path,
+                                  update=False,
+                                  rev=[0])
+
+    # insert a wrapper that prevents calling changectx.children()
+    extensions.wrapfunction(context.changectx, 'children', failfn)
+
+    try:
+        svncommands.rebuildmeta(u, dest,
+                                args=[test_util.fileurl(repo_path +
+                                                        subdir), ])
+    finally:
+        # remove the wrapper
+        context.changectx.children = origchildren
+
+    dest.pull(src)
+
+    # insert a wrapper that prevents calling changectx.children()
+    extensions.wrapfunction(context.changectx, 'children', failfn)
+    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):
+
     self.assertTrue(os.path.isdir(os.path.join(src.path, 'svn')),
                     'no .hg/svn directory in the source!')
     self.assertTrue(os.path.isdir(os.path.join(src.path, 'svn')),
@@ -68,7 +105,7 @@ def _do_case(self, name, stupid, single)
             self.assertNotEqual(old, new,
                                 'rebuildmeta unexpected match on youngest rev!')
             continue
-        self.assertMultiLineEqual(old, new)
+        self.assertMultiLineEqual(old, new, tf + ' differs')
         self.assertEqual(src.branchtags(), dest.branchtags())
     srcbi = pickle.load(open(os.path.join(src.path, 'svn', 'branch_info')))
     destbi = pickle.load(open(os.path.join(dest.path, 'svn', 'branch_info')))
@@ -107,6 +144,7 @@ skip = set([
 ])
 
 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
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -199,12 +199,12 @@ def _verify_our_modules():
             'from the wrong path!'
         )
 
-def hgclone(ui, source, dest, update=True):
+def hgclone(ui, source, dest, update=True, rev=None):
     if getattr(hg, 'peer', None):
         # Since 1.9 (d976542986d2)
-        src, dest = hg.clone(ui, {}, source, dest, update=update)
+        src, dest = hg.clone(ui, {}, source, dest, update=update, rev=rev)
     else:
-        src, dest = hg.clone(ui, source, dest, update=update)
+        src, dest = hg.clone(ui, source, dest, update=update, rev=rev)
     return src, dest
 
 def svnls(repo_path, path, rev='HEAD'):