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