changeset 435:7c576ae19d80

replay: start new replay module that has the relevant functions
author Dirkjan Ochtman <dirkjan@ochtman.nl>
date Tue, 16 Jun 2009 08:43:12 +0200
parents 3e2259fe3c93
children 404162e4bb53
files hgsubversion/cmdutil.py hgsubversion/editor.py hgsubversion/replay.py hgsubversion/wrappers.py
diffstat 4 files changed, 227 insertions(+), 208 deletions(-) [+]
line wrap: on
line diff
--- a/hgsubversion/cmdutil.py
+++ b/hgsubversion/cmdutil.py
@@ -53,42 +53,6 @@ def parentrev(ui, repo, meta, hashes):
     return workingctx
 
 
-def replay_convert_rev(ui, meta, svn, r, tbdelta):
-    # ui is only passed in for similarity with stupid.convert_rev()
-    hg_editor = meta.editor
-    hg_editor.current.rev = r
-    meta.save_tbdelta(tbdelta) # needed by get_replay()
-    svn.get_replay(r.revnum, meta.editor)
-    i = 1
-    if hg_editor.current.missing:
-        meta.ui.debug('Fetching %s files that could not use replay.\n' %
-                      len(hg_editor.current.missing))
-        files_to_grab = set()
-        rootpath = svn.subdir and svn.subdir[1:] or ''
-        for p in hg_editor.current.missing:
-            meta.ui.note('.')
-            meta.ui.flush()
-            if p[-1] == '/':
-                dirpath = p[len(rootpath):]
-                files_to_grab.update([dirpath + f for f,k in
-                                      svn.list_files(dirpath, r.revnum)
-                                      if k == 'f'])
-            else:
-                files_to_grab.add(p[len(rootpath):])
-        meta.ui.note('\nFetching files...\n')
-        for p in files_to_grab:
-            meta.ui.note('.')
-            meta.ui.flush()
-            if i % 50 == 0:
-                svn.init_ra_and_client()
-            i += 1
-            data, mode = svn.get_file(p, r.revnum)
-            hg_editor.current.set(p, data, 'x' in mode, 'l' in mode)
-        hg_editor.current.missing = set()
-        meta.ui.note('\n')
-    return hg_editor.commit_current_delta(tbdelta)
-
-
 def _isdir(svn, branchpath, svndir):
     try:
         svn.list_dir('%s/%s' % (branchpath, svndir))
--- a/hgsubversion/editor.py
+++ b/hgsubversion/editor.py
@@ -1,26 +1,14 @@
 import cStringIO
 import sys
-import traceback
 
-from mercurial import context
 from mercurial import util as hgutil
 from mercurial import revlog
 from mercurial import node
 from svn import delta
 from svn import core
 
-import svnexternals
 import util
 
-class MissingPlainTextError(Exception):
-    """Exception raised when the repo lacks a source file required for replaying
-    a txdelta.
-    """
-
-class ReplayException(Exception):
-    """Exception raised when you try and commit but the replay encountered an
-    exception.
-    """
 
 def ieditor(fn):
     """Helps identify methods used by the SVN editor interface.
@@ -98,165 +86,6 @@ class HgEditor(delta.Editor):
         self.repo = meta.repo
         self.current = RevisionData(meta.ui)
 
-    def _updateexternals(self):
-        if not self.current.externals:
-            return
-        # Accumulate externals records for all branches
-        revnum = self.current.rev.revnum
-        branches = {}
-        for path, entry in self.current.externals.iteritems():
-            if not self.meta.is_path_valid(path):
-                self.ui.warn('WARNING: Invalid path %s in externals\n' % path)
-                continue
-            p, b, bp = self.meta.split_branch_path(path)
-            if bp not in branches:
-                external = svnexternals.externalsfile()
-                parent = self.meta.get_parent_revision(revnum, b)
-                pctx = self.repo[parent]
-                if '.hgsvnexternals' in pctx:
-                    external.read(pctx['.hgsvnexternals'].data())
-                branches[bp] = external
-            else:
-                external = branches[bp]
-            external[p] = entry
-
-        # Register the file changes
-        for bp, external in branches.iteritems():
-            path = bp + '/.hgsvnexternals'
-            if external:
-                self.current.set(path, external.write(), False, False)
-            else:
-                self.current.delete(path)
-
-    def commit_current_delta(self, tbdelta):
-        if self.current.exception is not None:  #pragma: no cover
-            traceback.print_exception(*self.current.exception)
-            raise ReplayException()
-        if self.current.missing:
-            raise MissingPlainTextError()
-        self._updateexternals()
-        # paranoidly generate the list of files to commit
-        files_to_commit = set(self.current.files.keys())
-        files_to_commit.update(self.current.symlinks.keys())
-        files_to_commit.update(self.current.execfiles.keys())
-        files_to_commit.update(self.current.deleted.keys())
-        # back to a list and sort so we get sane behavior
-        files_to_commit = list(files_to_commit)
-        files_to_commit.sort()
-        branch_batches = {}
-        rev = self.current.rev
-        date = self.meta.fixdate(rev.date)
-
-        # build up the branches that have files on them
-        for f in files_to_commit:
-            if not self.meta.is_path_valid(f):
-                continue
-            p, b = self.meta.split_branch_path(f)[:2]
-            if b not in branch_batches:
-                branch_batches[b] = []
-            branch_batches[b].append((p, f))
-
-        closebranches = {}
-        for branch in tbdelta['branches'][1]:
-            branchedits = self.meta.revmap.branchedits(branch, rev)
-            if len(branchedits) < 1:
-                # can't close a branch that never existed
-                continue
-            ha = branchedits[0][1]
-            closebranches[branch] = ha
-
-        # 1. handle normal commits
-        closedrevs = closebranches.values()
-        for branch, files in branch_batches.iteritems():
-            if branch in self.current.emptybranches and files:
-                del self.current.emptybranches[branch]
-            files = dict(files)
-
-            parents = (self.meta.get_parent_revision(rev.revnum, branch),
-                       revlog.nullid)
-            if parents[0] in closedrevs and branch in self.meta.closebranches:
-                continue
-            extra = self.meta.genextra(rev.revnum, branch)
-            if branch is not None:
-                if (branch not in self.meta.branches
-                    and branch not in self.repo.branchtags()):
-                    continue
-            parent_ctx = self.repo.changectx(parents[0])
-            if '.hgsvnexternals' not in parent_ctx and '.hgsvnexternals' in files:
-                # Do not register empty externals files
-                if (files['.hgsvnexternals'] in self.current.files
-                    and not self.current.files[files['.hgsvnexternals']]):
-                    del files['.hgsvnexternals']
-
-            def filectxfn(repo, memctx, path):
-                current_file = files[path]
-                if current_file in self.current.deleted:
-                    raise IOError()
-                copied = self.current.copies.get(current_file)
-                flags = parent_ctx.flags(path)
-                is_exec = self.current.execfiles.get(current_file, 'x' in flags)
-                is_link = self.current.symlinks.get(current_file, 'l' in flags)
-                if current_file in self.current.files:
-                    data = self.current.files[current_file]
-                    if is_link and data.startswith('link '):
-                        data = data[len('link '):]
-                    elif is_link:
-                        self.ui.warn('file marked as link, but contains data: '
-                                     '%s (%r)\n' % (current_file, flags))
-                else:
-                    data = parent_ctx.filectx(path).data()
-                return context.memfilectx(path=path,
-                                          data=data,
-                                          islink=is_link, isexec=is_exec,
-                                          copied=copied)
-            if not self.meta.usebranchnames:
-                extra.pop('branch', None)
-            current_ctx = context.memctx(self.repo,
-                                         parents,
-                                         rev.message or '...',
-                                         files.keys(),
-                                         filectxfn,
-                                         self.meta.authors[rev.author],
-                                         date,
-                                         extra)
-            new_hash = self.repo.commitctx(current_ctx)
-            util.describe_commit(self.ui, new_hash, branch)
-            if (rev.revnum, branch) not in self.meta.revmap:
-                self.meta.revmap[rev.revnum, branch] = new_hash
-
-        # 2. handle branches that need to be committed without any files
-        for branch in self.current.emptybranches:
-            ha = self.meta.get_parent_revision(rev.revnum, branch)
-            if ha == node.nullid:
-                continue
-            parent_ctx = self.repo.changectx(ha)
-            def del_all_files(*args):
-                raise IOError
-            # True here meant nuke all files, shouldn't happen with branch closing
-            if self.current.emptybranches[branch]: #pragma: no cover
-               raise hgutil.Abort('Empty commit to an open branch attempted. '
-                                  'Please report this issue.')
-            extra = self.meta.genextra(rev.revnum, branch)
-            if not self.meta.usebranchnames:
-                extra.pop('branch', None)
-            current_ctx = context.memctx(self.repo,
-                                         (ha, node.nullid),
-                                         rev.message or ' ',
-                                         [],
-                                         del_all_files,
-                                         self.meta.authors[rev.author],
-                                         date,
-                                         extra)
-            new_hash = self.repo.commitctx(current_ctx)
-            util.describe_commit(self.ui, new_hash, branch)
-            if (rev.revnum, branch) not in self.meta.revmap:
-                self.meta.revmap[rev.revnum, branch] = new_hash
-
-        self.current.clear()
-        return closebranches
-	
-    # Here come all the actual editor methods
-
     @ieditor
     def delete_entry(self, path, revision_bogus, parent_baton, pool=None):
         br_path, branch = self.meta.split_branch_path(path)[:2]
new file mode 100644
--- /dev/null
+++ b/hgsubversion/replay.py
@@ -0,0 +1,225 @@
+import traceback
+
+from mercurial import revlog
+from mercurial import node
+from mercurial import context
+from mercurial import util as hgutil
+
+import svnexternals
+import util
+
+
+class MissingPlainTextError(Exception):
+    """Exception raised when the repo lacks a source file required for replaying
+    a txdelta.
+    """
+
+class ReplayException(Exception):
+    """Exception raised when you try and commit but the replay encountered an
+    exception.
+    """
+
+def convert_rev(ui, meta, svn, r, tbdelta):
+    # ui is only passed in for similarity with stupid.convert_rev()
+    hg_editor = meta.editor
+    hg_editor.current.clear()
+    hg_editor.current.rev = r
+    meta.save_tbdelta(tbdelta) # needed by get_replay()
+    svn.get_replay(r.revnum, meta.editor)
+    i = 1
+    if hg_editor.current.missing:
+        meta.ui.debug('Fetching %s files that could not use replay.\n' %
+                      len(hg_editor.current.missing))
+        files_to_grab = set()
+        rootpath = svn.subdir and svn.subdir[1:] or ''
+        for p in hg_editor.current.missing:
+            meta.ui.note('.')
+            meta.ui.flush()
+            if p[-1] == '/':
+                dirpath = p[len(rootpath):]
+                files_to_grab.update([dirpath + f for f,k in
+                                      svn.list_files(dirpath, r.revnum)
+                                      if k == 'f'])
+            else:
+                files_to_grab.add(p[len(rootpath):])
+        meta.ui.note('\nFetching files...\n')
+        for p in files_to_grab:
+            meta.ui.note('.')
+            meta.ui.flush()
+            if i % 50 == 0:
+                svn.init_ra_and_client()
+            i += 1
+            data, mode = svn.get_file(p, r.revnum)
+            hg_editor.current.set(p, data, 'x' in mode, 'l' in mode)
+        hg_editor.current.missing = set()
+        meta.ui.note('\n')
+    _updateexternals(meta, hg_editor.current)
+    return commit_current_delta(meta, tbdelta, hg_editor.current)
+
+
+def _updateexternals(meta, current):
+    if not current.externals:
+        return
+    # Accumulate externals records for all branches
+    revnum = current.rev.revnum
+    branches = {}
+    for path, entry in current.externals.iteritems():
+        if not meta.is_path_valid(path):
+            meta.ui.warn('WARNING: Invalid path %s in externals\n' % path)
+            continue
+        p, b, bp = meta.split_branch_path(path)
+        if bp not in branches:
+            external = svnexternals.externalsfile()
+            parent = meta.get_parent_revision(revnum, b)
+            pctx = meta.repo[parent]
+            if '.hgsvnexternals' in pctx:
+                external.read(pctx['.hgsvnexternals'].data())
+            branches[bp] = external
+        else:
+            external = branches[bp]
+        external[p] = entry
+
+    # Register the file changes
+    for bp, external in branches.iteritems():
+        path = bp + '/.hgsvnexternals'
+        if external:
+            current.set(path, external.write(), False, False)
+        else:
+            current.delete(path)
+
+
+def commit_current_delta(meta, tbdelta, current):
+
+    if current.exception is not None:  #pragma: no cover
+        traceback.print_exception(*current.exception)
+        raise ReplayException()
+    if current.missing:
+        raise MissingPlainTextError()
+
+    # paranoidly generate the list of files to commit
+    files_to_commit = set(current.files.keys())
+    files_to_commit.update(current.symlinks.keys())
+    files_to_commit.update(current.execfiles.keys())
+    files_to_commit.update(current.deleted.keys())
+    # back to a list and sort so we get sane behavior
+    files_to_commit = list(files_to_commit)
+    files_to_commit.sort()
+    branch_batches = {}
+    rev = current.rev
+    date = meta.fixdate(rev.date)
+
+    # build up the branches that have files on them
+    for f in files_to_commit:
+        if not meta.is_path_valid(f):
+            continue
+        p, b = meta.split_branch_path(f)[:2]
+        if b not in branch_batches:
+            branch_batches[b] = []
+        branch_batches[b].append((p, f))
+
+    closebranches = {}
+    for branch in tbdelta['branches'][1]:
+        branchedits = meta.revmap.branchedits(branch, rev)
+        if len(branchedits) < 1:
+            # can't close a branch that never existed
+            continue
+        ha = branchedits[0][1]
+        closebranches[branch] = ha
+
+    # 1. handle normal commits
+    closedrevs = closebranches.values()
+    for branch, files in branch_batches.iteritems():
+
+        if branch in current.emptybranches and files:
+            del current.emptybranches[branch]
+
+        files = dict(files)
+        parents = meta.get_parent_revision(rev.revnum, branch), revlog.nullid
+        if parents[0] in closedrevs and branch in meta.closebranches:
+            continue
+
+        extra = meta.genextra(rev.revnum, branch)
+        if branch is not None:
+            if (branch not in meta.branches
+                and branch not in meta.repo.branchtags()):
+                continue
+
+        parent_ctx = meta.repo.changectx(parents[0])
+        if '.hgsvnexternals' not in parent_ctx and '.hgsvnexternals' in files:
+            # Do not register empty externals files
+            if (files['.hgsvnexternals'] in current.files
+                and not current.files[files['.hgsvnexternals']]):
+                del files['.hgsvnexternals']
+
+        def filectxfn(repo, memctx, path):
+            current_file = files[path]
+            if current_file in current.deleted:
+                raise IOError()
+            copied = current.copies.get(current_file)
+            flags = parent_ctx.flags(path)
+            is_exec = current.execfiles.get(current_file, 'x' in flags)
+            is_link = current.symlinks.get(current_file, 'l' in flags)
+            if current_file in current.files:
+                data = current.files[current_file]
+                if is_link and data.startswith('link '):
+                    data = data[len('link '):]
+                elif is_link:
+                    meta.ui.warn('file marked as link, but contains data: '
+                                 '%s (%r)\n' % (current_file, flags))
+            else:
+                data = parent_ctx.filectx(path).data()
+            return context.memfilectx(path=path,
+                                      data=data,
+                                      islink=is_link, isexec=is_exec,
+                                      copied=copied)
+
+        if not meta.usebranchnames:
+            extra.pop('branch', None)
+        current_ctx = context.memctx(meta.repo,
+                                     parents,
+                                     rev.message or '...',
+                                     files.keys(),
+                                     filectxfn,
+                                     meta.authors[rev.author],
+                                     date,
+                                     extra)
+
+        new_hash = meta.repo.commitctx(current_ctx)
+        util.describe_commit(meta.ui, new_hash, branch)
+        if (rev.revnum, branch) not in meta.revmap:
+            meta.revmap[rev.revnum, branch] = new_hash
+
+    # 2. handle branches that need to be committed without any files
+    for branch in current.emptybranches:
+
+        ha = meta.get_parent_revision(rev.revnum, branch)
+        if ha == node.nullid:
+            continue
+
+        parent_ctx = meta.repo.changectx(ha)
+        def del_all_files(*args):
+            raise IOError
+
+        # True here meant nuke all files, shouldn't happen with branch closing
+        if current.emptybranches[branch]: #pragma: no cover
+            raise hgutil.Abort('Empty commit to an open branch attempted. '
+                               'Please report this issue.')
+
+        extra = meta.genextra(rev.revnum, branch)
+        if not meta.usebranchnames:
+            extra.pop('branch', None)
+
+        current_ctx = context.memctx(meta.repo,
+                                     (ha, node.nullid),
+                                     rev.message or ' ',
+                                     [],
+                                     del_all_files,
+                                     meta.authors[rev.author],
+                                     date,
+                                     extra)
+        new_hash = meta.repo.commitctx(current_ctx)
+        util.describe_commit(meta.ui, new_hash, branch)
+        if (rev.revnum, branch) not in meta.revmap:
+            meta.revmap[rev.revnum, branch] = new_hash
+
+    return closebranches
--- a/hgsubversion/wrappers.py
+++ b/hgsubversion/wrappers.py
@@ -12,12 +12,13 @@ from svn import delta
 
 import cmdutil
 import svnmeta
+import replay
 import stupid as stupidmod
 import svnwrap
 import util
 
 pullfuns = {
-    True: cmdutil.replay_convert_rev,
+    True: replay.convert_rev,
     False: stupidmod.convert_rev,
 }