Rev 16: Some more work on maptree. in file:///data/jelmer/bzr-rebase/trunk/

Jelmer Vernooij jelmer at samba.org
Thu Jul 12 09:22:41 BST 2007


At file:///data/jelmer/bzr-rebase/trunk/

------------------------------------------------------------
revno: 16
revision-id: jelmer at samba.org-20070709163106-2lvmxf46qy473ua2
parent: jelmer at samba.org-20070705104021-9ji91y4xizglxqev
committer: Jelmer Vernooij <jelmer at samba.org>
branch nick: trunk
timestamp: Mon 2007-07-09 17:31:06 +0100
message:
  Some more work on maptree.
modified:
  README                         readme-20070626220000-c62864tuxlldx6uc-1
  __init__.py                    __init__.py-20070626215909-fi0s39bkwxn4gcto-1
  rebase.py                      rebase.py-20070626221123-ellanmf93nw8z9r1-1
  test_rebase.py                 test_rebase.py-20070626221123-ellanmf93nw8z9r1-2
=== modified file 'README'
--- a/README	2007-07-04 20:31:54 +0000
+++ b/README	2007-07-09 16:31:06 +0000
@@ -4,11 +4,12 @@
 ==========
 How to use
 ==========
-
-Simply run ``bzr rebase``, optionally specifying an upstream branch. If no branch is specified, 
-it will use the current parent branch (which is usually what you want).
-
-In the future, I hope it can also support rebasing on top of an unrelated branch.
+Simply run ``bzr rebase``, optionally specifying an upstream branch. If no 
+branch is specified, it will use the current parent branch (which is usually 
+what you want).
+
+In the future, I hope it can also support rebasing on top of an unrelated 
+branch.
 
 ============
 How it works
@@ -46,4 +47,3 @@
          \ -> E ---> F next
 
     Would G get rebased on top of D rather than B?
-

=== modified file '__init__.py'
--- a/__init__.py	2007-07-05 10:05:56 +0000
+++ b/__init__.py	2007-07-09 16:31:06 +0000
@@ -15,7 +15,8 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 from bzrlib.commands import Command, Option, display_command, register_command
-from bzrlib.errors import BzrCommandError, UnrelatedBranches, ConflictsInTree, NoSuchFile
+from bzrlib.errors import (BzrCommandError, ConflictsInTree, NoSuchFile, 
+                           UnrelatedBranches)
 from bzrlib.trace import info
 
 class cmd_rebase(Command):
@@ -27,13 +28,14 @@
                      Option('onto', help='Different revision to replay onto')]
     
     @display_command
-    def run(self, upstream_location=None, onto=None, revision=None, merge_type=None):
+    def run(self, upstream_location=None, onto=None, revision=None, 
+            merge_type=None):
         from bzrlib.branch import Branch
         from bzrlib.revisionspec import RevisionSpec
         from bzrlib.workingtree import WorkingTree
-        from rebase import (generate_simple_plan, rebase, 
-                            rebase_plan_exists, write_rebase_plan, 
-                            read_rebase_plan, workingtree_replay, remove_rebase_plan)
+        from rebase import (generate_simple_plan, rebase, rebase_plan_exists, 
+                            read_rebase_plan, remove_rebase_plan, 
+                            workingtree_replay, write_rebase_plan)
         wt = WorkingTree.open('.')
         wt.lock_write()
         if upstream_location is None:
@@ -70,7 +72,8 @@
             if common_revid == upstream.last_revision():
                 raise BzrCommandError("Already rebased on %s" % upstream)
 
-            start_revid = wt.branch.get_rev_id(wt.branch.revision_id_to_revno(common_revid)+1)
+            start_revid = wt.branch.get_rev_id(
+                    wt.branch.revision_id_to_revno(common_revid)+1)
 
             # Create plan
             replace_map = generate_simple_plan(
@@ -122,7 +125,9 @@
     
     @display_command
     def run(self, merge_type=None):
-        from rebase import read_rebase_plan, rebase_plan_exists, workingtree_replay, rebase, remove_rebase_plan, commit_rebase, read_active_rebase_revid
+        from rebase import (commit_rebase, rebase, rebase_plan_exists, 
+                            read_rebase_plan, read_active_rebase_revid, 
+                            remove_rebase_plan, workingtree_replay)
         from bzrlib.workingtree import WorkingTree
         wt = WorkingTree.open('.')
         wt.lock_write()
@@ -141,7 +146,8 @@
                 commit_rebase(wt, oldrev, replace_map[oldrevid][0])
             try:
                 # Start executing plan from current Branch.last_revision()
-                rebase(wt.branch.repository, replace_map, workingtree_replay(wt, merge_type=merge_type))
+                rebase(wt.branch.repository, replace_map, 
+                        workingtree_replay(wt, merge_type=merge_type))
             except ConflictsInTree:
                 raise BzrCommandError("A conflict occurred replaying a commit. Resolve the conflict and run 'bzr rebase-continue' or run 'bzr rebase-abort'.")
             # Remove plan file  
@@ -151,12 +157,14 @@
 
 
 class cmd_rebase_todo(Command):
-    """Print list of revisions that still need to be replayed as part of the current rebase operation.
+    """Print list of revisions that still need to be replayed as part of the 
+    current rebase operation.
 
     """
     
     def run(self):
-        from rebase import read_rebase_plan, rebase_todo, read_active_rebase_revid
+        from rebase import (rebase_todo, read_rebase_plan, 
+                            read_active_rebase_revid)
         from bzrlib.workingtree import WorkingTree
         wt = WorkingTree.open('.')
         wt.lock_read()
@@ -164,7 +172,7 @@
             try:
                 replace_map = read_rebase_plan(wt)[1]
             except NoSuchFile:
-                raise BzrCommandError("No rebase to view")
+                raise BzrCommandError("No rebase in progress")
             currentrevid = read_active_rebase_revid(wt)
             if currentrevid is not None:
                 info("Currently replaying: %s" % currentrevid)
@@ -187,7 +195,8 @@
     suite = TestSuite()
     testmod_names = [
             'test_rebase']
-    suite.addTest(loader.loadTestsFromModuleNames(["%s.%s" % (__name__, i) for i in testmod_names]))
+    suite.addTest(loader.loadTestsFromModuleNames(
+                              ["%s.%s" % (__name__, i) for i in testmod_names]))
 
     return suite
 

=== modified file 'rebase.py'
--- a/rebase.py	2007-07-05 10:40:21 +0000
+++ b/rebase.py	2007-07-09 16:31:06 +0000
@@ -13,9 +13,10 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+"""Rebase."""
 
 from bzrlib.config import Config
-from bzrlib.errors import UnknownFormatError, NoSuchFile, BzrError
+from bzrlib.errors import BzrError, NoSuchFile, UnknownFormatError
 from bzrlib.generate_ids import gen_revision_id
 from bzrlib import osutils
 from bzrlib.revision import NULL_REVISION
@@ -254,27 +255,76 @@
     finally:
         pb.finished()
         
-    assert all(map(repository.has_revision, [replace_map[r][0] for r in replace_map]))
+    assert all(map(repository.has_revision, 
+               [replace_map[r][0] for r in replace_map]))
      
 
+def map_file_ids(repository, old_parents, new_parents):
+    ret = {}
+    for (oldp, newp) in zip(old_parents, new_parents):
+        oldinv = repository.get_revision_inventory(oldp)
+        newinv = repository.get_revision_inventory(newp)
+        for path, ie in oldinv.iter_entries():
+            if newinv.has_filename(path):
+                ret[ie.file_id] = newinv.path2id(path)
+    return ret
+
+
+class MapInventory:
+    def __init__(self, oldinv, maptree):
+        self.oldinv = oldinv
+        self.maptree = maptree
+
+    def map_ie(self, ie):
+        """Fix the references to old file ids in an inventory entry.
+
+        :param ie: Inventory entry to map
+        :return: New inventory entry
+        """
+        new_ie = ie.copy()
+        new_ie.file_id = self.maptree.new_id(new_ie.file_id)
+        new_ie.parent_id = self.maptree.new_id(new_ie.parent_id)
+        return new_ie
+
+    def __len__(self):
+        return len(self.oldinv)
+
+    def iter_entries(self):
+        for path, ie in self.oldinv.iter_entries():
+            yield path, self.map_ie(ie)
+
+
 class MapTree:
-    def __init__(self, repository, oldtree, old_parents, new_parents):
-        self.map = {}
-        for (oldp, newp) in zip(old_parents, new_parents):
-            oldinv = repository.get_revision_inventory(oldp)
-            newinv = repository.get_revision_inventory(newp)
-            for path, ie in oldinv.iter_entries():
-                if newinv.has_filename(path):
-                    self.map[ie.file_id] = newinv.path2id(path)
+    """Wrapper around a tree that translates file ids.
+    """
+    # TODO: Inventory
+    def __init__(self, oldtree, fileid_map):
+        """Create a new MapTree. 
+
+        :param oldtree: Old tree to map to.
+        :param fileid_map: Map with old -> new file ids.
+        """
         self.oldtree = oldtree
+        self.map = fileid_map
+        self.inventory = MapInventory(self.oldtree.inventory, self)
 
     def old_id(self, file_id):
+        """Look up the original file id of a file.
+
+        :param file_id: New file id
+        :return: Old file id if mapped, otherwise new file id
+        """
         for x in self.map:
             if self.map[x] == file_id:
                 return x
         return file_id
 
     def new_id(self, file_id):
+        """Look up the new file id of a file.
+
+        :param file_id: Old file id
+        :return: New file id
+        """
         try:
             return self.map[file_id]
         except KeyError:
@@ -291,6 +341,7 @@
         return self.oldtree.is_executable(self.old_id(file_id=file_id), 
                                           path=path)
 
+
 def replay_snapshot(repository, oldrevid, newrevid, new_parents):
     """Replay a commit by simply commiting the same snapshot with different parents.
 
@@ -300,7 +351,8 @@
     :param new_parents: Revision ids of the new parent revisions.
     """
     assert isinstance(new_parents, list)
-    mutter('creating copy %r of %r with new parents %r' % (newrevid, oldrevid, new_parents))
+    mutter('creating copy %r of %r with new parents %r' % 
+                               (newrevid, oldrevid, new_parents))
     oldrev = repository.get_revision(oldrevid)
 
     revprops = dict(oldrev.properties)
@@ -316,30 +368,18 @@
 
     # Check what new_ie.file_id should be
     # use old and new parent inventories to generate new_id map
-    oldtree = MapTree(repository, 
-                      repository.revision_tree(oldrevid), 
-                      oldrev.parent_ids, new_parents)
-    oldinv = repository.get_revision_inventory(oldrevid)
-    total = len(oldinv)
+    fileid_map = map_file_ids(repository, oldrev.parent_ids, new_parents)
+    oldtree = MapTree(repository.revision_tree(oldrevid), fileid_map)
+    total = len(oldtree.inventory)
     pb = ui.ui_factory.nested_progress_bar()
     i = 0
     try:
         parent_invs = map(repository.get_revision_inventory, new_parents)
         transact = repository.get_transaction()
-        for path, ie in oldinv.iter_entries():
+        for path, ie in oldtree.inventory.iter_entries():
             pb.update('upgrading file', i, total)
             i += 1
-            new_ie = ie.copy()
-            new_ie.file_id = oldtree.new_id(new_ie.file_id)
-            new_ie.parent_id = oldtree.new_id(new_ie.parent_id)
-            if new_ie.revision == oldrevid:
-                if repository.weave_store.get_weave_or_empty(new_ie.file_id, 
-                        transact).has_version(newrevid):
-                    new_ie.revision = newrevid
-                else:
-                    new_ie.revision = None
-
-            builder.record_entry_contents(new_ie, 
+            builder.record_entry_contents(ie, 
                     parent_invs,
                    path, oldtree)
     finally:
@@ -389,8 +429,9 @@
     oldtree = repository.revision_tree(oldrevid)
     basetree = repository.revision_tree(oldrev.parent_ids[0])
     if map_ids:
-        oldtree = MapTree(repository, oldtree, oldrev.parent_ids, new_parents)
-        basetree = MapTree(repository, basetree, oldrev.parent_ids, new_parents)
+        fileid_map = map_file_ids(repository, oldrev.parent_ids, new_parents)
+        oldtree = MapTree(repository, oldtree, fileid_map)
+        basetree = MapTree(repository, basetree, fileid_map)
 
     write_active_rebase_revid(wt, oldrevid)
     merge = merge_type(working_tree=wt, this_tree=wt, 
@@ -408,17 +449,28 @@
     """
     def replay(repository, oldrevid, newrevid, newparents):
         assert wt.branch.repository == repository
-        return replay_delta_workingtree(wt, oldrevid, newrevid, newparents, merge_type=merge_type)
+        return replay_delta_workingtree(wt, oldrevid, newrevid, newparents, 
+                                        merge_type=merge_type)
     return replay
 
 
 def write_active_rebase_revid(wt, revid):
+    """Write the id of the revision that is currently being rebased. 
+
+    :param wt: Working Tree that is being used for the rebase.
+    :param revid: Revision id to write
+    """
     if revid is None:
         revid = NULL_REVISION
     wt._control_files.put_utf8(REBASE_CURRENT_REVID_FILENAME, revid)
 
 
 def read_active_rebase_revid(wt):
+    """Read the id of the revision that is currently being rebased.
+
+    :param wt: Working Tree that is being used for the rebase.
+    :return: Id of the revision that is being rebased.
+    """
     try:
         text = wt._control_files.get(REBASE_CURRENT_REVID_FILENAME).read().rstrip("\n")
         if text == NULL_REVISION:
@@ -429,9 +481,11 @@
 
 
 def complete_revert(wt, newparents):
-    """Simple helper that reverts to specified new 
-    parents and makes sure none of the extra files 
-    are left around.
+    """Simple helper that reverts to specified new parents and makes sure none 
+    of the extra files are left around.
+
+    :param wt: Working tree to use for rebase
+    :param newparents: New parents of the working tree
     """
     newtree = wt.branch.repository.revision_tree(newparents[0])
     delta = wt.changes_from(newtree)

=== modified file 'test_rebase.py'
--- a/test_rebase.py	2007-07-04 21:11:44 +0000
+++ b/test_rebase.py	2007-07-09 16:31:06 +0000
@@ -13,19 +13,20 @@
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+"""Tests for the rebase code."""
 
 from bzrlib.errors import UnknownFormatError, NoSuchFile
 from bzrlib.revision import NULL_REVISION
 from bzrlib.tests import TestCase, TestCaseWithTransport
+from bzrlib.treebuilder import TreeBuilder
 
 from rebase import (marshall_rebase_plan, unmarshall_rebase_plan, 
                     replay_snapshot, generate_simple_plan,
                     generate_transpose_plan, rebase_plan_exists,
-                    REBASE_PLAN_FILENAME, write_rebase_plan,
-                    REBASE_CURRENT_REVID_FILENAME,
+                    REBASE_PLAN_FILENAME, REBASE_CURRENT_REVID_FILENAME,
                     read_rebase_plan, remove_rebase_plan, 
-                    read_active_rebase_revid, 
-                    write_active_rebase_revid)
+                    read_active_rebase_revid, write_active_rebase_revid, 
+                    write_rebase_plan, MapTree)
 
 
 class RebasePlanReadWriterTests(TestCase):
@@ -233,3 +234,15 @@
         write_active_rebase_revid(wt, None)
         self.assertIs(None, read_active_rebase_revid(wt))
 
+
+class MapTreeTests(TestCaseWithTransport):
+    def setUp(self):
+        super(MapTreeTests, self).setUp()
+
+    def test_empty_map(self):
+        tree = self.make_branch_and_memory_tree('branch') 
+        builder = TreeBuilder()
+        builder.start_tree(tree)
+        builder.build(['foo'])
+        builder.finish_tree()
+        m = MapTree(tree, {})




More information about the bazaar-commits mailing list