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