Rev 4706: (igc) fix content filtering after pull, merge, switch (#385879) in file:///home/pqm/archives/thelove/bzr/2.0/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Fri Nov 20 02:53:53 GMT 2009
At file:///home/pqm/archives/thelove/bzr/2.0/
------------------------------------------------------------
revno: 4706 [merge]
revision-id: pqm at pqm.ubuntu.com-20091120025352-c0rtr4c3fgx1d876
parent: pqm at pqm.ubuntu.com-20091118053528-bibgd6qfczykm4ia
parent: ian.clatworthy at canonical.com-20091120013936-nkpsxwmiqveuu4rv
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: 2.0
timestamp: Fri 2009-11-20 02:53:52 +0000
message:
(igc) fix content filtering after pull, merge, switch (#385879)
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/merge.py merge.py-20050513021216-953b65a438527106
bzrlib/tests/per_workingtree/test_content_filters.py test_content_filters-20080424071441-8navsrmrfdxpn90a-1
bzrlib/transform.py transform.py-20060105172343-dd99e54394d91687
=== modified file 'NEWS'
--- a/NEWS 2009-11-03 15:54:22 +0000
+++ b/NEWS 2009-11-20 01:39:36 +0000
@@ -19,6 +19,10 @@
Bug Fixes
*********
+
+* Content filters are now applied correctly after pull, merge and switch.
+ (Ian Clatworthy, #385879)
+
* Improve "Binary files differ" hunk handling. (Aaron Bentley, #436325)
Improvements
=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py 2009-09-11 21:19:32 +0000
+++ b/bzrlib/merge.py 2009-10-27 09:45:35 +0000
@@ -1162,8 +1162,22 @@
self.tt.delete_contents(trans_id)
if file_id in self.other_tree:
# OTHER changed the file
+ wt = self.this_tree
+ if wt.supports_content_filtering():
+ # We get the path from the working tree if it exists.
+ # That fails though when OTHER is adding a file, so
+ # we fall back to the other tree to find the path if
+ # it doesn't exist locally.
+ try:
+ filter_tree_path = wt.id2path(file_id)
+ except errors.NoSuchId:
+ filter_tree_path = self.other_tree.id2path(file_id)
+ else:
+ # Skip the id2path lookup for older formats
+ filter_tree_path = None
create_from_tree(self.tt, trans_id,
- self.other_tree, file_id)
+ self.other_tree, file_id,
+ filter_tree_path=filter_tree_path)
if not file_in_this:
self.tt.version_file(file_id, trans_id)
return "modified"
@@ -1256,12 +1270,26 @@
('THIS', self.this_tree, this_lines)]
if not no_base:
data.append(('BASE', self.base_tree, base_lines))
+
+ # We need to use the actual path in the working tree of the file here,
+ # ignoring the conflict suffixes
+ wt = self.this_tree
+ if wt.supports_content_filtering():
+ try:
+ filter_tree_path = wt.id2path(file_id)
+ except errors.NoSuchId:
+ # file has been deleted
+ filter_tree_path = None
+ else:
+ # Skip the id2path lookup for older formats
+ filter_tree_path = None
+
versioned = False
file_group = []
for suffix, tree, lines in data:
if file_id in tree:
trans_id = self._conflict_file(name, parent_id, tree, file_id,
- suffix, lines)
+ suffix, lines, filter_tree_path)
file_group.append(trans_id)
if set_version and not versioned:
self.tt.version_file(file_id, trans_id)
@@ -1269,11 +1297,12 @@
return file_group
def _conflict_file(self, name, parent_id, tree, file_id, suffix,
- lines=None):
+ lines=None, filter_tree_path=None):
"""Emit a single conflict file."""
name = name + '.' + suffix
trans_id = self.tt.create_path(name, parent_id)
- create_from_tree(self.tt, trans_id, tree, file_id, lines)
+ create_from_tree(self.tt, trans_id, tree, file_id, lines,
+ filter_tree_path)
return trans_id
def merge_executable(self, file_id, file_status):
=== modified file 'bzrlib/tests/per_workingtree/test_content_filters.py'
--- a/bzrlib/tests/per_workingtree/test_content_filters.py 2009-10-28 01:16:10 +0000
+++ b/bzrlib/tests/per_workingtree/test_content_filters.py 2009-10-28 23:34:13 +0000
@@ -18,7 +18,9 @@
import os
+from bzrlib.bzrdir import BzrDir
from bzrlib.filters import ContentFilter
+from bzrlib.switch import switch
from bzrlib.workingtree import WorkingTree
from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
@@ -83,6 +85,38 @@
bin_fileid = tree.path2id('file2.bin')
return tree, txt_fileid, bin_fileid
+ def create_cf_tree_with_two_revisions(self, txt_reader, txt_writer,
+ dir='.'):
+ tree = self.make_branch_and_tree(dir)
+ def _content_filter_stack(path=None, file_id=None):
+ if path.endswith('.txt'):
+ return [ContentFilter(txt_reader, txt_writer)]
+ else:
+ return []
+ tree._content_filter_stack = _content_filter_stack
+ self.build_tree_contents([
+ (dir + '/file1.txt', 'Foo Txt'),
+ (dir + '/file2.bin', 'Foo Bin'),
+ (dir + '/file3.txt', 'Bar Txt'),
+ ])
+ tree.add(['file1.txt', 'file2.bin', 'file3.txt'])
+ tree.commit('commit raw content')
+ fileid_1 = tree.path2id('file1.txt')
+ fileid_2 = tree.path2id('file2.bin')
+ fileid_3 = tree.path2id('file3.txt')
+ # Commit another revision with various changes. We make sure
+ # the change includes a modification, an addition and a deletion.
+ # Renames are more complex and need a separate set of tests later.
+ self.build_tree_contents([
+ (dir + '/file1.txt', 'Foo ROCKS!'),
+ (dir + '/file4.txt', 'Hello World'),
+ ])
+ tree.add(['file4.txt'])
+ tree.remove(['file3.txt'], keep_files=False)
+ tree.commit("change, add and rename stuff")
+ fileid_4 = tree.path2id('file4.txt')
+ return tree, fileid_1, fileid_2, fileid_3, fileid_4
+
def patch_in_content_filter(self):
# Patch in a custom, symmetric content filter stack. It's pretty gross
# that we need to monkey-patch a class method to do this, bit it's
@@ -222,6 +256,89 @@
# that will be expensive to compute, so it's acceptable to just return
# None.
+ def test_content_filtering_applied_on_pull(self):
+ # Create a source branch with two revisions
+ source, fileid_1, fileid_2, fileid_3, fileid_4 = \
+ self.create_cf_tree_with_two_revisions(txt_reader=None,
+ txt_writer=None, dir='source')
+ if not source.supports_content_filtering():
+ return
+ self.assertFileEqual("Foo ROCKS!", 'source/file1.txt')
+ self.assert_basis_content("Foo ROCKS!", source, fileid_1)
+
+ # Now patch in content filtering and branch from revision 1
+ self.patch_in_content_filter()
+ self.run_bzr('branch -r1 source target')
+ target = WorkingTree.open('target')
+ self.assert_basis_content("Foo Txt", target, fileid_1)
+ self.assertFileEqual("fOO tXT", 'target/file1.txt')
+ self.assert_basis_content("Foo Bin", target, fileid_2)
+ self.assertFileEqual("Foo Bin", 'target/file2.bin')
+ self.assert_basis_content("Bar Txt", target, fileid_3)
+ self.assertFileEqual("bAR tXT", 'target/file3.txt')
+
+ # Pull the latter change and check the target tree is updated
+ self.run_bzr('pull -d target')
+ self.assert_basis_content("Foo ROCKS!", target, fileid_1)
+ self.assertFileEqual("fOO rocks!", 'target/file1.txt')
+ self.assert_basis_content("Foo Bin", target, fileid_2)
+ self.assert_basis_content("Hello World", target, fileid_4)
+ self.assertFileEqual("hELLO wORLD", 'target/file4.txt')
+
+ def test_content_filtering_applied_on_merge(self):
+ # Create a source branch with two revisions
+ source, fileid_1, fileid_2, fileid_3, fileid_4 = \
+ self.create_cf_tree_with_two_revisions(txt_reader=None,
+ txt_writer=None, dir='source')
+ if not source.supports_content_filtering():
+ return
+ self.assert_basis_content("Foo ROCKS!", source, fileid_1)
+ self.assertFileEqual("Foo ROCKS!", 'source/file1.txt')
+ self.assert_basis_content("Foo Bin", source, fileid_2)
+ self.assert_basis_content("Hello World", source, fileid_4)
+ self.assertFileEqual("Hello World", 'source/file4.txt')
+
+ # Now patch in content filtering and branch from revision 1
+ self.patch_in_content_filter()
+ self.run_bzr('branch -r1 source target')
+ target = WorkingTree.open('target')
+ self.assert_basis_content("Foo Txt", target, fileid_1)
+ self.assertFileEqual("fOO tXT", 'target/file1.txt')
+ self.assertFileEqual("Foo Bin", 'target/file2.bin')
+ self.assertFileEqual("bAR tXT", 'target/file3.txt')
+
+ # Merge the latter change and check the target tree is updated
+ self.run_bzr('merge -d target source')
+ self.assertFileEqual("fOO rocks!", 'target/file1.txt')
+ self.assertFileEqual("hELLO wORLD", 'target/file4.txt')
+
+ # Commit the merge and check the right content is stored
+ target.commit("merge file1.txt changes from source")
+ self.assert_basis_content("Foo ROCKS!", target, fileid_1)
+ self.assert_basis_content("Hello World", target, fileid_4)
+
+ def test_content_filtering_applied_on_switch(self):
+ # Create a source branch with two revisions
+ source, fileid_1, fileid_2, fileid_3, fileid_4 = \
+ self.create_cf_tree_with_two_revisions(txt_reader=None,
+ txt_writer=None, dir='branch-a')
+ if not source.supports_content_filtering():
+ return
+
+ # Now patch in content filtering and branch from revision 1
+ self.patch_in_content_filter()
+ self.run_bzr('branch -r1 branch-a branch-b')
+
+ # Now create a lightweight checkout referring to branch-b
+ self.run_bzr('checkout --lightweight branch-b checkout')
+ self.assertFileEqual("fOO tXT", 'checkout/file1.txt')
+
+ # Switch it to branch-b and check the tree is updated
+ checkout_control_dir = BzrDir.open_containing('checkout')[0]
+ switch(checkout_control_dir, source.branch)
+ self.assertFileEqual("fOO rocks!", 'checkout/file1.txt')
+ self.assertFileEqual("hELLO wORLD", 'checkout/file4.txt')
+
def test_content_filtering_applied_on_revert_delete(self):
# Create a source branch with content filtering
source, txt_fileid, bin_fileid = self.create_cf_tree(
=== modified file 'bzrlib/transform.py'
--- a/bzrlib/transform.py 2009-10-27 12:06:56 +0000
+++ b/bzrlib/transform.py 2009-10-28 07:34:55 +0000
@@ -2420,8 +2420,14 @@
tt.create_directory(trans_id)
-def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
- """Create new file contents according to tree contents."""
+def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
+ filter_tree_path=None):
+ """Create new file contents according to tree contents.
+
+ :param filter_tree_path: the tree path to use to lookup
+ content filters to apply to the bytes output in the working tree.
+ This only applies if the working tree supports content filtering.
+ """
kind = tree.kind(file_id)
if kind == 'directory':
tt.create_directory(trans_id)
@@ -2432,6 +2438,11 @@
bytes = tree_file.readlines()
finally:
tree_file.close()
+ wt = tt._tree
+ if wt.supports_content_filtering() and filter_tree_path is not None:
+ filters = wt._content_filter_stack(filter_tree_path)
+ bytes = filtered_output_bytes(bytes, filters,
+ ContentFilterContext(filter_tree_path, tree))
tt.create_file(bytes, trans_id)
elif kind == "symlink":
tt.create_symlink(tree.get_symlink_target(file_id), trans_id)
More information about the bazaar-commits
mailing list