Rev 3786: CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes. in http://people.ubuntu.com/~robertc/baz2.0/commit-iterchanges
Robert Collins
robertc at robertcollins.net
Tue Nov 18 06:39:59 GMT 2008
At http://people.ubuntu.com/~robertc/baz2.0/commit-iterchanges
------------------------------------------------------------
revno: 3786
revision-id: robertc at robertcollins.net-20081118063955-rosaxti87kco7tlk
parent: robertc at robertcollins.net-20081118042340-iwb4oeouxbkfmrxk
committer: Robert Collins <robertc at robertcollins.net>
branch nick: commit-iterchanges
timestamp: Tue 2008-11-18 17:39:55 +1100
message:
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
modified:
bzrlib/repository.py rev_storage.py-20051111201905-119e9401e46257e3
bzrlib/tests/per_repository/test_commit_builder.py test_commit_builder.py-20060606110838-76e3ra5slucqus81-1
=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py 2008-11-18 04:08:30 +0000
+++ b/bzrlib/repository.py 2008-11-18 06:39:55 +0000
@@ -510,11 +510,13 @@
self._any_changes = True
return self._get_delta(ie, basis_inv, path), True, fingerprint
- def record_iter_changes(self, tree, basis_revision_id, iter_changes,
- _entry_factory=entry_factory):
+ def record_iter_changes(self, tree, basis_tree, basis_revision_id,
+ iter_changes, _entry_factory=entry_factory):
"""Record a new tree via iter_changes.
:param tree: The tree to obtain text contents from for changed objects.
+ :param basis_tree: The basis tree this commit is being performed
+ against.
:param basis_revision_id: The revision id of the tree the iter_changes
has been generated against.
:param iter_changes: An iter_changes iterator.
@@ -526,20 +528,76 @@
# deltas between all the parent inventories. We use inventory delta's
# between the inventory objects because iter_changes masks
# last-changed-field only changes.
+ basis_inv = basis_tree.inventory
# file_id -> change map, change is fileid, paths, changed, versioneds,
# parents, names, kinds, executables
+ merged_ids = {}
+ if len(self.parents) > 1:
+ revtrees = list(self.repository.revision_trees(self.parents))
+ repo_basis = revtrees[0]
+ for revtree in revtrees[1:]:
+ for change in _make_delta(basis_tree, revtree):
+ if change[1] is None:
+ # Deleted
+ continue
+ if change[2] not in merged_ids:
+ if change[0] is not None:
+ merged_ids[change[2]] = set([change[3].revision,
+ repo_basis[change[2]].revision])
+ else:
+ merged_ids[change[2]] = set([change[3].revision])
+ else:
+ merged_ids[change[2]].add(change[3].revision)
+ else:
+ merged_ids = {}
changes= {}
for change in iter_changes:
- changes[change[0]] = change
+ changes[change[0]] = change, merged_ids.get(
+ change[0], set([basis_inv[change[0]].revision]))
+ unchanged_merged = set(merged_ids) - set(changes)
+ # changes contains tuples with the change and a set of inventory
+ # candidates for the file.
# inv delta is:
# old_path, new_path, file_id, new_inventory_entry
seen_root = False # Is the root in the basis delta?
inv_delta = self.basis_delta
modified_rev = self._new_revision_id
- for change in changes.values():
+ for change, head_candidates in changes.values():
if change[3][1]: # versioned in target.
+ kind = change[6][1]
+ heads = self._heads(change[0], head_candidates)
+ # Populate the entry
+ if change[2]:
+ # From disk.
+ if kind == 'file':
+ import pdb;pdb.set_trace()
+ elif kind == 'symlink':
+ import pdb;pdb.set_trace()
+ elif kind == 'directory':
+ # Nothing to set.
+ import pdb;pdb.set_trace()
+ pass
+ elif kind == 'tree-reference':
+ import pdb;pdb.set_trace()
+ else:
+ raise AssertionError('unknown kind %r' % kind)
+ else:
+ # From basis.
+ if kind == 'file':
+ import pdb;pdb.set_trace()
+ elif kind == 'symlink':
+ import pdb;pdb.set_trace()
+ elif kind == 'directory':
+ # Nothing to set.
+ self._add_text_to_weave(change[0], [], heads, None)
+ pass
+ elif kind == 'tree-reference':
+ import pdb;pdb.set_trace()
+ else:
+ raise AssertionError('unknown kind %r' % kind)
entry = _entry_factory[change[6][1]](
change[0], change[5][1], change[4][1])
+
entry.revision = modified_rev
else:
entry = None
=== modified file 'bzrlib/tests/per_repository/test_commit_builder.py'
--- a/bzrlib/tests/per_repository/test_commit_builder.py 2008-11-18 04:23:40 +0000
+++ b/bzrlib/tests/per_repository/test_commit_builder.py 2008-11-18 06:39:55 +0000
@@ -76,8 +76,8 @@
try:
builder = tree.branch.get_commit_builder([])
try:
- builder.record_iter_changes(tree, tree.last_revision(),
- tree.iter_changes(tree.basis_tree()))
+ builder.record_iter_changes(tree, tree.basis_tree(),
+ tree.last_revision(), tree.iter_changes(tree.basis_tree()))
builder.finish_inventory()
except:
builder.abort()
@@ -104,8 +104,8 @@
try:
builder = tree.branch.get_commit_builder([])
try:
- builder.record_iter_changes(tree, tree.last_revision(),
- tree.iter_changes(tree.basis_tree()))
+ builder.record_iter_changes(tree, tree.basis_tree(),
+ tree.last_revision(), tree.iter_changes(tree.basis_tree()))
builder.finish_inventory()
except:
builder.abort()
@@ -180,8 +180,8 @@
return
self.assertFalse(builder.random_revid)
try:
- builder.record_iter_changes(tree, tree.last_revision(),
- tree.iter_changes(tree.basis_tree()))
+ builder.record_iter_changes(tree, tree.basis_tree(),
+ tree.last_revision(), tree.iter_changes(tree.basis_tree()))
builder.finish_inventory()
except:
builder.abort()
@@ -260,7 +260,7 @@
self.addCleanup(parent_tree.unlock)
builder = tree.branch.get_commit_builder([old_revision_id])
try:
- builder.record_iter_changes(tree, old_revision_id, [])
+ builder.record_iter_changes(tree, parent_tree, old_revision_id, [])
# Regardless of repository root behaviour we should consider this a
# pointless commit.
self.assertFalse(builder.any_changes())
@@ -340,7 +340,8 @@
delete_change = ('foo-id', ('foo', None), True, (True, False),
(tree.path2id(''), None), ('foo', None), ('file', None),
(False, None))
- builder.record_iter_changes(tree, rev_id, [delete_change])
+ builder.record_iter_changes(tree, tree.basis_tree(), rev_id,
+ [delete_change])
self.assertEqual(("foo", None, "foo-id", None),
builder.basis_delta[0])
self.assertTrue(builder.any_changes())
@@ -395,7 +396,8 @@
try:
builder = tree.branch.get_commit_builder([])
try:
- builder.record_iter_changes(tree, _mod_revision.NULL_REVISION,
+ builder.record_iter_changes(tree, tree.basis_tree(),
+ _mod_revision.NULL_REVISION,
tree.iter_changes(tree.basis_tree()))
builder.finish_inventory()
rev_id = builder.commit('foo bar')
@@ -413,6 +415,9 @@
def test_root_entry_has_revision(self):
# test the root revision created and put in the basis
# has the right rev id.
+ # XXX: RBC 20081118 - this test is too big, it depends on the exact
+ # behaviour of tree methods and so on; it should be written to the
+ # commit builder interface directly.
tree = self.make_branch_and_tree('.')
rev_id = tree.commit('message')
basis_tree = tree.basis_tree()
@@ -444,10 +449,12 @@
else:
self.assertEqual(rev2, tree2.inventory.root.revision)
- def _add_commit_check_unchanged(self, tree, name):
+ def _add_commit_check_unchanged(self, tree, name, mini_commit=None):
tree.add([name], [name + 'id'])
rev1 = tree.commit('')
- rev2 = self.mini_commit(tree, name, name, False, False)
+ if mini_commit is None:
+ mini_commit = self.mini_commit
+ rev2 = mini_commit(tree, name, name, False, False)
tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
self.assertEqual(rev1, tree2.inventory[name + 'id'].revision)
@@ -462,6 +469,13 @@
self.build_tree(['dir/'])
self._add_commit_check_unchanged(tree, 'dir')
+ def test_last_modified_revision_after_commit_dir_unchanged_ric(self):
+ # committing without changing a dir does not change the last modified.
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['dir/'])
+ self._add_commit_check_unchanged(tree, 'dir',
+ mini_commit=self.mini_commit_record_iter_changes)
+
def test_last_modified_revision_after_commit_dir_contents_unchanged(self):
# committing without changing a dir does not change the last modified
# of the dir even the dirs contents are changed.
@@ -486,6 +500,13 @@
self.build_tree(['file'])
self._add_commit_check_unchanged(tree, 'file')
+ def test_last_modified_revision_after_commit_file_unchanged_ric(self):
+ # committing without changing a file does not change the last modified.
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['file'])
+ self._add_commit_check_unchanged(tree, 'file',
+ mini_commit=self.mini_commit_record_iter_changes)
+
def test_last_modified_revision_after_commit_link_unchanged(self):
# committing without changing a link does not change the last modified.
self.requireFeature(tests.SymlinkFeature)
@@ -493,12 +514,22 @@
os.symlink('target', 'link')
self._add_commit_check_unchanged(tree, 'link')
+ def test_last_modified_revision_after_commit_link_unchanged_ric(self):
+ # committing without changing a link does not change the last modified.
+ self.requireFeature(tests.SymlinkFeature)
+ tree = self.make_branch_and_tree('.')
+ os.symlink('target', 'link')
+ self._add_commit_check_unchanged(tree, 'link',
+ mini_commit=self.mini_commit_record_iter_changes)
+
def _add_commit_renamed_check_changed(self, tree, name,
- expect_fs_hash=False):
+ expect_fs_hash=False, mini_commit=None):
def rename():
tree.rename_one(name, 'new_' + name)
+ if mini_commit is None:
+ mini_commit = self.mini_commit
self._add_commit_change_check_changed(tree, name, rename,
- expect_fs_hash=expect_fs_hash)
+ expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
def test_last_modified_revision_after_rename_dir_changes(self):
# renaming a dir changes the last modified.
@@ -506,6 +537,13 @@
self.build_tree(['dir/'])
self._add_commit_renamed_check_changed(tree, 'dir')
+ def test_last_modified_revision_after_rename_dir_changes_ric(self):
+ # renaming a dir changes the last modified.
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['dir/'])
+ self._add_commit_renamed_check_changed(tree, 'dir',
+ mini_commit=self.mini_commit_record_iter_changes)
+
def test_last_modified_revision_after_rename_file_changes(self):
# renaming a file changes the last modified.
tree = self.make_branch_and_tree('.')
@@ -550,11 +588,13 @@
self._add_commit_reparent_check_changed(tree, 'link')
def _add_commit_change_check_changed(self, tree, name, changer,
- expect_fs_hash=False):
+ expect_fs_hash=False, mini_commit=None):
tree.add([name], [name + 'id'])
rev1 = tree.commit('')
changer()
- rev2 = self.mini_commit(tree, name, tree.id2path(name + 'id'),
+ if mini_commit is None:
+ mini_commit = self.mini_commit
+ rev2 = mini_commit(tree, name, tree.id2path(name + 'id'),
expect_fs_hash=expect_fs_hash)
tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
@@ -642,6 +682,65 @@
tree.unlock()
return rev2
+ def mini_commit_record_iter_changes(self, tree, name, new_name,
+ records_version=True, delta_against_basis=True, expect_fs_hash=False):
+ """Perform a miniature commit looking for record entry results.
+
+ This version uses the record_iter_changes interface.
+
+ :param tree: The tree to commit.
+ :param name: The path in the basis tree of the tree being committed.
+ :param new_name: The path in the tree being committed.
+ :param records_version: True if the commit of new_name is expected to
+ record a new version.
+ :param delta_against_basis: True of the commit of new_name is expected
+ to have a delta against the basis.
+ :param expect_fs_hash: ignored, present for compatibility with test
+ driver code for 'mini_commit'.
+ """
+ tree.lock_write()
+ try:
+ # mini manual commit here so we can check the return of
+ # record_entry_contents.
+ parent_ids = tree.get_parent_ids()
+ builder = tree.branch.get_commit_builder(parent_ids)
+ parent_tree = tree.basis_tree()
+ parent_tree.lock_read()
+ self.addCleanup(parent_tree.unlock)
+ parent_invs = [parent_tree.inventory]
+ for parent_id in parent_ids[1:]:
+ parent_invs.append(tree.branch.repository.revision_tree(
+ parent_id).inventory)
+ changes = list(tree.iter_changes(parent_tree))
+ builder.record_iter_changes(tree, parent_tree, parent_ids[0],
+ changes)
+ delta = builder.basis_delta
+ delta_dict = dict((change[2], change) for change in delta)
+ version_recorded = tree.path2id(new_name) in delta_dict
+ if records_version:
+ self.assertTrue(version_recorded)
+ else:
+ self.assertFalse(version_recorded)
+ builder.finish_inventory()
+ new_inventory = builder.revision_tree().inventory
+ file_id = tree.path2id(new_name)
+ new_entry = new_inventory[file_id]
+ if delta_against_basis:
+ expected_delta = (name, new_name, file_id, new_entry)
+ self.assertEqual(expected_delta, delta_dict[file_id])
+ else:
+ expected_delta = None
+ self.assertFalse(version_recorded)
+ rev2 = builder.commit('')
+ tree.set_parent_ids([rev2])
+ except:
+ builder.abort()
+ tree.unlock()
+ raise
+ else:
+ tree.unlock()
+ return rev2
+
def assertFileGraph(self, expected_graph, tree, tip):
# all the changes that have occured should be in the ancestry
# (closest to a public per-file graph API we have today)
More information about the bazaar-commits
mailing list