Merge lp:~ian-clatworthy/bzr/eol-st-ci-fix into lp:~bzr/bzr/trunk-old

Proposed by Ian Clatworthy
Status: Merged
Merged at revision: not available
Proposed branch: lp:~ian-clatworthy/bzr/eol-st-ci-fix
Merge into: lp:~bzr/bzr/trunk-old
Diff against target: 555 lines
To merge this branch: bzr merge lp:~ian-clatworthy/bzr/eol-st-ci-fix
Reviewer Review Type Date Requested Status
John A Meinel Approve
Review via email: mp+7269@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Ian Clatworthy (ian-clatworthy) wrote :

This patch fixes some very bad bugs in content filtering. There are actually 2 separate issues fixed:

1. Branching from a non-content-filtered tree to a content-filtered one would produce an incorrect dirstate because the optimisation to reuse the source dirstate wasn't being disabled

2. Status detection was relying on size matching to determine equivalence when that assumption doesn't hold in the presence of content filtering.

Both code changes are quite straightforward, though the latter one requires fixes in both Python and Pyrex code. Testing all of this is far more complex though. I've added a heap more units tests and I'm comfortable that status on content filtered trees now has pretty good coverage.

I'm yet to add detailed tests for the branch/commit issue, because the monkey-patching approach used to test status isn't at the right level to work for branch. I have tested it manually though and one of the bug reporters (Frits) has confirmed that the code fixes works correctly for him. Given the related code change - switching off an optional optimisation - is very straight forward, I think this patch can land as is for 1.16rc1.

FWIW, my plan is to add those additional tests soon. (Some pending changes to the monkey-patching used here to fix some other bugs will make that easier, but they aren't ready to go, and this really needs to be landed for content filtering to be unbroken for users.)

Revision history for this message
John A Meinel (jameinel) wrote :
Download full text (4.9 KiB)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ian Clatworthy wrote:
> Ian Clatworthy has proposed merging lp:~ian-clatworthy/bzr/eol-st-ci-fix into lp:bzr.
>
> Requested reviews:
> bzr-core (bzr-core)
>
> This patch fixes some very bad bugs in content filtering. There are actually 2 separate issues fixed:
>
> 1. Branching from a non-content-filtered tree to a content-filtered one would produce an incorrect dirstate because the optimisation to reuse the source dirstate wasn't being disabled
>
> 2. Status detection was relying on size matching to determine equivalence when that assumption doesn't hold in the presence of content filtering.
>
> Both code changes are quite straightforward, though the latter one requires fixes in both Python and Pyrex code. Testing all of this is far more complex though. I've added a heap more units tests and I'm comfortable that status on content filtered trees now has pretty good coverage.
>
> I'm yet to add detailed tests for the branch/commit issue, because the monkey-patching approach used to test status isn't at the right level to work for branch. I have tested it manually though and one of the bug reporters (Frits) has confirmed that the code fixes works correctly for him. Given the related code change - switching off an optional optimisation - is very straight forward, I think this patch can land as is for 1.16rc1.
>
> FWIW, my plan is to add those additional tests soon. (Some pending changes to the monkey-patching used here to fix some other bugs will make that easier, but they aren't ready to go, and this really needs to be landed for content filtering to be unbroken for users.)
>

+ # Check the sha. We can't just rely on the size as
+ # content filtering may mean differ sizes actually
+ # map to the same content
+ if link_or_sha1 is None:

^- It would be nice if this was only done when there were actually
filters in play, rather than disabling the size check universally.

This change will cause us to sha1 the content of everything we are
committing 2 times. Once during iter_changes to make sure it really has
changed, and then once during the insert-into-repository code to record
its final sha, and ensure that it really has changed.

Note that ideally we would have an iter_changes that could provide
"maybe changed" and *never* sha content. Perhaps we can address that
when we re-visit dirstate. (So I'm saying it is probably ok for now, but
it is not the way we want to leave the code in the long run.)

...

                 # Note: do NOT move this logic up higher - using the
basis from
                 # the accelerator tree is still desirable because that
can save
                 # a minute or more of processing on large trees!
+ # The original tree may not have the same content filters
+ # applied so we can't safely build the inventory delta from
+ # the source tree.
                 if wt.supports_content_filtering():
                     accelerator_tree = None
+ delta_from_tree = False
+ else:
+ delta...

Read more...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2009-06-11 00:47:41 +0000
+++ NEWS 2009-06-11 04:35:14 +0000
@@ -81,6 +81,9 @@
81 the fetched revisions, not in the stacked-on ancestry.81 the fetched revisions, not in the stacked-on ancestry.
82 (John Arbash Meinel)82 (John Arbash Meinel)
8383
84* Fix status and commit to work with content filtered trees, addressing
85 numerous bad bugs with line-ending support. (Ian Clatworthy, #362030)
86
84* Fix problem of "directory not empty" when contending for a lock over87* Fix problem of "directory not empty" when contending for a lock over
85 sftp. (Martin Pool, #340352)88 sftp. (Martin Pool, #340352)
8689
8790
=== modified file 'bzrlib/_dirstate_helpers_c.pyx'
--- bzrlib/_dirstate_helpers_c.pyx 2009-03-23 14:59:43 +0000
+++ bzrlib/_dirstate_helpers_c.pyx 2009-06-11 04:35:14 +0000
@@ -1140,19 +1140,17 @@
1140 if source_minikind != c'f':1140 if source_minikind != c'f':
1141 content_change = 11141 content_change = 1
1142 else:1142 else:
1143 # If the size is the same, check the sha:1143 # Check the sha. We can't just rely on the size as
1144 if target_details[2] == source_details[2]:1144 # content filtering may mean differ sizes actually
1145 if link_or_sha1 is None:1145 # map to the same content
1146 # Stat cache miss:1146 if link_or_sha1 is None:
1147 statvalue, link_or_sha1 = \1147 # Stat cache miss:
1148 self.state._sha1_provider.stat_and_sha1(1148 statvalue, link_or_sha1 = \
1149 path_info[4])1149 self.state._sha1_provider.stat_and_sha1(
1150 self.state._observed_sha1(entry, link_or_sha1,1150 path_info[4])
1151 statvalue)1151 self.state._observed_sha1(entry, link_or_sha1,
1152 content_change = (link_or_sha1 != source_details[1])1152 statvalue)
1153 else:1153 content_change = (link_or_sha1 != source_details[1])
1154 # Size changed, so must be different
1155 content_change = 1
1156 # Target details is updated at update_entry time1154 # Target details is updated at update_entry time
1157 if self.use_filesystem_for_exec:1155 if self.use_filesystem_for_exec:
1158 # We don't need S_ISREG here, because we are sure1156 # We don't need S_ISREG here, because we are sure
11591157
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2009-06-10 03:56:49 +0000
+++ bzrlib/builtins.py 2009-06-11 04:35:14 +0000
@@ -1112,6 +1112,9 @@
11121112
1113 accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(1113 accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1114 from_location)1114 from_location)
1115 if (accelerator_tree is not None and
1116 accelerator_tree.supports_content_filtering()):
1117 accelerator_tree = None
1115 revision = _get_one_revision('branch', revision)1118 revision = _get_one_revision('branch', revision)
1116 br_from.lock_read()1119 br_from.lock_read()
1117 try:1120 try:
11181121
=== modified file 'bzrlib/dirstate.py'
--- bzrlib/dirstate.py 2009-05-06 05:36:28 +0000
+++ bzrlib/dirstate.py 2009-06-11 04:35:14 +0000
@@ -3062,19 +3062,17 @@
3062 if source_minikind != 'f':3062 if source_minikind != 'f':
3063 content_change = True3063 content_change = True
3064 else:3064 else:
3065 # If the size is the same, check the sha:3065 # Check the sha. We can't just rely on the size as
3066 if target_details[2] == source_details[2]:3066 # content filtering may mean differ sizes actually
3067 if link_or_sha1 is None:3067 # map to the same content
3068 # Stat cache miss:3068 if link_or_sha1 is None:
3069 statvalue, link_or_sha1 = \3069 # Stat cache miss:
3070 self.state._sha1_provider.stat_and_sha1(3070 statvalue, link_or_sha1 = \
3071 path_info[4])3071 self.state._sha1_provider.stat_and_sha1(
3072 self.state._observed_sha1(entry, link_or_sha1,3072 path_info[4])
3073 statvalue)3073 self.state._observed_sha1(entry, link_or_sha1,
3074 content_change = (link_or_sha1 != source_details[1])3074 statvalue)
3075 else:3075 content_change = (link_or_sha1 != source_details[1])
3076 # Size changed, so must be different
3077 content_change = True
3078 # Target details is updated at update_entry time3076 # Target details is updated at update_entry time
3079 if self.use_filesystem_for_exec:3077 if self.use_filesystem_for_exec:
3080 # We don't need S_ISREG here, because we are sure3078 # We don't need S_ISREG here, because we are sure
30813079
=== modified file 'bzrlib/tests/workingtree_implementations/test_content_filters.py'
--- bzrlib/tests/workingtree_implementations/test_content_filters.py 2009-03-23 14:59:43 +0000
+++ bzrlib/tests/workingtree_implementations/test_content_filters.py 2009-06-11 04:35:14 +0000
@@ -17,6 +17,7 @@
17"""Tests for content filtering conformance"""17"""Tests for content filtering conformance"""
1818
19from bzrlib.filters import ContentFilter19from bzrlib.filters import ContentFilter
20from bzrlib.workingtree import WorkingTree
20from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree21from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
2122
2223
@@ -44,8 +45,8 @@
4445
45class TestWorkingTreeWithContentFilters(TestCaseWithWorkingTree):46class TestWorkingTreeWithContentFilters(TestCaseWithWorkingTree):
4647
47 def create_cf_tree(self, txt_reader, txt_writer):48 def create_cf_tree(self, txt_reader, txt_writer, dir='.'):
48 tree = self.make_branch_and_tree('.')49 tree = self.make_branch_and_tree(dir)
49 def _content_filter_stack(path=None, file_id=None):50 def _content_filter_stack(path=None, file_id=None):
50 if path.endswith('.txt'):51 if path.endswith('.txt'):
51 return [ContentFilter(txt_reader, txt_writer)]52 return [ContentFilter(txt_reader, txt_writer)]
@@ -53,8 +54,8 @@
53 return []54 return []
54 tree._content_filter_stack = _content_filter_stack55 tree._content_filter_stack = _content_filter_stack
55 self.build_tree_contents([56 self.build_tree_contents([
56 ('file1.txt', 'Foo Txt'),57 (dir + '/file1.txt', 'Foo Txt'),
57 ('file2.bin', 'Foo Bin')])58 (dir + '/file2.bin', 'Foo Bin')])
58 tree.add(['file1.txt', 'file2.bin'])59 tree.add(['file1.txt', 'file2.bin'])
59 tree.commit('commit raw content')60 tree.commit('commit raw content')
60 txt_fileid = tree.path2id('file1.txt')61 txt_fileid = tree.path2id('file1.txt')
@@ -104,3 +105,57 @@
104 filtered=False).read())105 filtered=False).read())
105 self.assertEqual('Foo Bin', tree.get_file(bin_fileid,106 self.assertEqual('Foo Bin', tree.get_file(bin_fileid,
106 filtered=False).read())107 filtered=False).read())
108
109 def test_branch_source_filtered_target_not(self):
110 # Create a source branch with content filtering
111 source, txt_fileid, bin_fileid = self.create_cf_tree(
112 txt_reader=_uppercase, txt_writer=_lowercase, dir='source')
113 if not source.supports_content_filtering():
114 return
115 self.assertFileEqual("Foo Txt", 'source/file1.txt')
116 basis = source.basis_tree()
117 basis.lock_read()
118 self.addCleanup(basis.unlock)
119 self.assertEqual("FOO TXT", basis.get_file_text(txt_fileid))
120
121 # Now branch it
122 self.run_bzr('branch source target')
123 target = WorkingTree.open('target')
124 # Even though the content in source and target are different
125 # due to different filters, iter_changes should be clean
126 self.assertFileEqual("FOO TXT", 'target/file1.txt')
127 changes = target.changes_from(source.basis_tree())
128 self.assertFalse(changes.has_changed())
129
130 def test_branch_source_not_filtered_target_is(self):
131 # Create a source branch with content filtering
132 source, txt_fileid, bin_fileid = self.create_cf_tree(
133 txt_reader=None, txt_writer=None, dir='source')
134 if not source.supports_content_filtering():
135 return
136 self.assertFileEqual("Foo Txt", 'source/file1.txt')
137 basis = source.basis_tree()
138 basis.lock_read()
139 self.addCleanup(basis.unlock)
140 self.assertEqual("Foo Txt", basis.get_file_text(txt_fileid))
141
142 # Patch in a custom, symmetric content filter stack
143 self.real_content_filter_stack = WorkingTree._content_filter_stack
144 def restore_real_content_filter_stack():
145 WorkingTree._content_filter_stack = self.real_content_filter_stack
146 self.addCleanup(restore_real_content_filter_stack)
147 def _content_filter_stack(tree, path=None, file_id=None):
148 if path.endswith('.txt'):
149 return [ContentFilter(_swapcase, _swapcase)]
150 else:
151 return []
152 WorkingTree._content_filter_stack = _content_filter_stack
153
154 # Now branch it
155 self.run_bzr('branch source target')
156 target = WorkingTree.open('target')
157 # Even though the content in source and target are different
158 # due to different filters, iter_changes should be clean
159 self.assertFileEqual("fOO tXT", 'target/file1.txt')
160 changes = target.changes_from(source.basis_tree())
161 self.assertFalse(changes.has_changed())
107162
=== modified file 'bzrlib/tests/workingtree_implementations/test_eol_conversion.py'
--- bzrlib/tests/workingtree_implementations/test_eol_conversion.py 2009-04-02 04:12:11 +0000
+++ bzrlib/tests/workingtree_implementations/test_eol_conversion.py 2009-06-11 04:35:14 +0000
@@ -17,8 +17,9 @@
17"""Tests for eol conversion."""17"""Tests for eol conversion."""
1818
19import sys19import sys
20from cStringIO import StringIO
2021
21from bzrlib import rules22from bzrlib import rules, status
22from bzrlib.tests import TestSkipped23from bzrlib.tests import TestSkipped
23from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree24from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
24from bzrlib.workingtree import WorkingTree25from bzrlib.workingtree import WorkingTree
@@ -29,6 +30,13 @@
29_sample_text_on_win = """hello\r\nworld\r\n"""30_sample_text_on_win = """hello\r\nworld\r\n"""
30_sample_text_on_unix = """hello\nworld\n"""31_sample_text_on_unix = """hello\nworld\n"""
31_sample_binary = """hello\nworld\r\n\x00"""32_sample_binary = """hello\nworld\r\n\x00"""
33_sample_clean_lf = _sample_text_on_unix
34_sample_clean_crlf = _sample_text_on_win
35
36
37# Lists of formats for each storage policy
38_LF_IN_REPO = ['native', 'lf', 'crlf']
39_CRLF_IN_REPO = [ '%s-with-crlf-in-repo' % (f,) for f in _LF_IN_REPO]
3240
3341
34class TestEolConversion(TestCaseWithWorkingTree):42class TestEolConversion(TestCaseWithWorkingTree):
@@ -74,8 +82,11 @@
74 return t, basis82 return t, basis
7583
76 def assertNewContentForSetting(self, wt, eol, expected_unix,84 def assertNewContentForSetting(self, wt, eol, expected_unix,
77 expected_win=None):85 expected_win, roundtrip):
78 """Clone a working tree and check the convenience content."""86 """Clone a working tree and check the convenience content.
87
88 If roundtrip is True, status and commit should see no changes.
89 """
79 if expected_win is None:90 if expected_win is None:
80 expected_win = expected_unix91 expected_win = expected_unix
81 self.patch_rules_searcher(eol)92 self.patch_rules_searcher(eol)
@@ -86,100 +97,241 @@
86 self.assertEqual(expected_win, content)97 self.assertEqual(expected_win, content)
87 else:98 else:
88 self.assertEqual(expected_unix, content)99 self.assertEqual(expected_unix, content)
100 # Confirm that status thinks nothing has changed if the text roundtrips
101 if roundtrip:
102 status_io = StringIO()
103 status.show_tree_status(wt2, to_file=status_io)
104 self.assertEqual('', status_io.getvalue())
89105
90 def assertContent(self, wt, basis, expected_raw, expected_unix,106 def assertContent(self, wt, basis, expected_raw, expected_unix,
91 expected_win):107 expected_win, roundtrip_to=None):
92 """Check the committed content and content in cloned trees."""108 """Check the committed content and content in cloned trees.
109
110 :param roundtrip_to: the set of formats (excluding exact) we
111 can round-trip to or None for all
112 """
93 basis_content = basis.get_file('file1-id').read()113 basis_content = basis.get_file('file1-id').read()
94 self.assertEqual(expected_raw, basis_content)114 self.assertEqual(expected_raw, basis_content)
95 self.assertNewContentForSetting(wt, None, expected_raw)115
116 # No setting and exact should always roundtrip
117 self.assertNewContentForSetting(wt, None,
118 expected_raw, expected_raw, roundtrip=True)
119 self.assertNewContentForSetting(wt, 'exact',
120 expected_raw, expected_raw, roundtrip=True)
121
122 # Roundtripping is otherwise dependent on whether the original
123 # text is clean - mixed line endings will prevent it. It also
124 # depends on whether the format in the repository is being changed.
125 if roundtrip_to is None:
126 roundtrip_to = _LF_IN_REPO + _CRLF_IN_REPO
96 self.assertNewContentForSetting(wt, 'native',127 self.assertNewContentForSetting(wt, 'native',
97 expected_unix, expected_win)128 expected_unix, expected_win, 'native' in roundtrip_to)
98 self.assertNewContentForSetting(wt, 'lf',129 self.assertNewContentForSetting(wt, 'lf',
99 expected_unix, expected_unix)130 expected_unix, expected_unix, 'lf' in roundtrip_to)
100 self.assertNewContentForSetting(wt, 'crlf',131 self.assertNewContentForSetting(wt, 'crlf',
101 expected_win, expected_win)132 expected_win, expected_win, 'crlf' in roundtrip_to)
102 self.assertNewContentForSetting(wt, 'native-with-crlf-in-repo',133 self.assertNewContentForSetting(wt, 'native-with-crlf-in-repo',
103 expected_unix, expected_win)134 expected_unix, expected_win,
135 'native-with-crlf-in-repo' in roundtrip_to)
104 self.assertNewContentForSetting(wt, 'lf-with-crlf-in-repo',136 self.assertNewContentForSetting(wt, 'lf-with-crlf-in-repo',
105 expected_unix, expected_unix)137 expected_unix, expected_unix,
138 'lf-with-crlf-in-repo' in roundtrip_to)
106 self.assertNewContentForSetting(wt, 'crlf-with-crlf-in-repo',139 self.assertNewContentForSetting(wt, 'crlf-with-crlf-in-repo',
107 expected_win, expected_win)140 expected_win, expected_win,
108 self.assertNewContentForSetting(wt, 'exact', expected_raw)141 'crlf-with-crlf-in-repo' in roundtrip_to)
109142
110 def test_eol_no_rules(self):143 # Test binary files. These always roundtrip.
111 wt, basis = self.prepare_tree(_sample_text)144
112 self.assertContent(wt, basis, _sample_text,145 def test_eol_no_rules_binary(self):
113 _sample_text_on_unix, _sample_text_on_win)146 wt, basis = self.prepare_tree(_sample_binary)
114147 self.assertContent(wt, basis, _sample_binary, _sample_binary,
115 def test_eol_native(self):148 _sample_binary)
116 wt, basis = self.prepare_tree(_sample_text, eol='native')149
117 self.assertContent(wt, basis, _sample_text_on_unix,150 def test_eol_exact_binary(self):
118 _sample_text_on_unix, _sample_text_on_win)151 wt, basis = self.prepare_tree(_sample_binary, eol='exact')
152 self.assertContent(wt, basis, _sample_binary, _sample_binary,
153 _sample_binary)
119154
120 def test_eol_native_binary(self):155 def test_eol_native_binary(self):
121 wt, basis = self.prepare_tree(_sample_binary, eol='native')156 wt, basis = self.prepare_tree(_sample_binary, eol='native')
122 self.assertContent(wt, basis, _sample_binary, _sample_binary,157 self.assertContent(wt, basis, _sample_binary, _sample_binary,
123 _sample_binary)158 _sample_binary)
124159
125 def test_eol_lf(self):
126 wt, basis = self.prepare_tree(_sample_text, eol='lf')
127 self.assertContent(wt, basis, _sample_text_on_unix,
128 _sample_text_on_unix, _sample_text_on_win)
129
130 def test_eol_lf_binary(self):160 def test_eol_lf_binary(self):
131 wt, basis = self.prepare_tree(_sample_binary, eol='lf')161 wt, basis = self.prepare_tree(_sample_binary, eol='lf')
132 self.assertContent(wt, basis, _sample_binary, _sample_binary,162 self.assertContent(wt, basis, _sample_binary, _sample_binary,
133 _sample_binary)163 _sample_binary)
134164
135 def test_eol_crlf(self):
136 wt, basis = self.prepare_tree(_sample_text, eol='crlf')
137 self.assertContent(wt, basis, _sample_text_on_unix,
138 _sample_text_on_unix, _sample_text_on_win)
139
140 def test_eol_crlf_binary(self):165 def test_eol_crlf_binary(self):
141 wt, basis = self.prepare_tree(_sample_binary, eol='crlf')166 wt, basis = self.prepare_tree(_sample_binary, eol='crlf')
142 self.assertContent(wt, basis, _sample_binary, _sample_binary,167 self.assertContent(wt, basis, _sample_binary, _sample_binary,
143 _sample_binary)168 _sample_binary)
144169
145 def test_eol_native_with_crlf_in_repo(self):
146 wt, basis = self.prepare_tree(_sample_text,
147 eol='native-with-crlf-in-repo')
148 self.assertContent(wt, basis, _sample_text_on_win,
149 _sample_text_on_unix, _sample_text_on_win)
150
151 def test_eol_native_with_crlf_in_repo_binary(self):170 def test_eol_native_with_crlf_in_repo_binary(self):
152 wt, basis = self.prepare_tree(_sample_binary,171 wt, basis = self.prepare_tree(_sample_binary,
153 eol='native-with-crlf-in-repo')172 eol='native-with-crlf-in-repo')
154 self.assertContent(wt, basis, _sample_binary, _sample_binary,173 self.assertContent(wt, basis, _sample_binary, _sample_binary,
155 _sample_binary)174 _sample_binary)
156175
157 def test_eol_lf_with_crlf_in_repo(self):
158 wt, basis = self.prepare_tree(_sample_text, eol='lf-with-crlf-in-repo')
159 self.assertContent(wt, basis, _sample_text_on_win,
160 _sample_text_on_unix, _sample_text_on_win)
161
162 def test_eol_lf_with_crlf_in_repo_binary(self):176 def test_eol_lf_with_crlf_in_repo_binary(self):
163 wt, basis = self.prepare_tree(_sample_binary, eol='lf-with-crlf-in-repo')177 wt, basis = self.prepare_tree(_sample_binary,
178 eol='lf-with-crlf-in-repo')
164 self.assertContent(wt, basis, _sample_binary, _sample_binary,179 self.assertContent(wt, basis, _sample_binary, _sample_binary,
165 _sample_binary)180 _sample_binary)
166181
167 def test_eol_crlf_with_crlf_in_repo(self):
168 wt, basis = self.prepare_tree(_sample_text, eol='crlf-with-crlf-in-repo')
169 self.assertContent(wt, basis, _sample_text_on_win,
170 _sample_text_on_unix, _sample_text_on_win)
171
172 def test_eol_crlf_with_crlf_in_repo_binary(self):182 def test_eol_crlf_with_crlf_in_repo_binary(self):
173 wt, basis = self.prepare_tree(_sample_binary, eol='crlf-with-crlf-in-repo')183 wt, basis = self.prepare_tree(_sample_binary,
184 eol='crlf-with-crlf-in-repo')
174 self.assertContent(wt, basis, _sample_binary, _sample_binary,185 self.assertContent(wt, basis, _sample_binary, _sample_binary,
175 _sample_binary)186 _sample_binary)
176187
177 def test_eol_exact(self):188 # Test text with mixed line endings ("dirty text").
189 # This doesn't roundtrip so status always thinks something has changed.
190
191 def test_eol_no_rules_dirty(self):
192 wt, basis = self.prepare_tree(_sample_text)
193 self.assertContent(wt, basis, _sample_text,
194 _sample_text_on_unix, _sample_text_on_win, roundtrip_to=[])
195
196 def test_eol_exact_dirty(self):
178 wt, basis = self.prepare_tree(_sample_text, eol='exact')197 wt, basis = self.prepare_tree(_sample_text, eol='exact')
179 self.assertContent(wt, basis, _sample_text,198 self.assertContent(wt, basis, _sample_text,
180 _sample_text_on_unix, _sample_text_on_win)199 _sample_text_on_unix, _sample_text_on_win, roundtrip_to=[])
181200
182 def test_eol_exact_binary(self):201 def test_eol_native_dirty(self):
183 wt, basis = self.prepare_tree(_sample_binary, eol='exact')202 wt, basis = self.prepare_tree(_sample_text, eol='native')
184 self.assertContent(wt, basis, _sample_binary, _sample_binary,203 self.assertContent(wt, basis, _sample_text_on_unix,
185 _sample_binary)204 _sample_text_on_unix, _sample_text_on_win, roundtrip_to=[])
205
206 def test_eol_lf_dirty(self):
207 wt, basis = self.prepare_tree(_sample_text, eol='lf')
208 self.assertContent(wt, basis, _sample_text_on_unix,
209 _sample_text_on_unix, _sample_text_on_win, roundtrip_to=[])
210
211 def test_eol_crlf_dirty(self):
212 wt, basis = self.prepare_tree(_sample_text, eol='crlf')
213 self.assertContent(wt, basis, _sample_text_on_unix,
214 _sample_text_on_unix, _sample_text_on_win, roundtrip_to=[])
215
216 def test_eol_native_with_crlf_in_repo_dirty(self):
217 wt, basis = self.prepare_tree(_sample_text,
218 eol='native-with-crlf-in-repo')
219 self.assertContent(wt, basis, _sample_text_on_win,
220 _sample_text_on_unix, _sample_text_on_win, roundtrip_to=[])
221
222 def test_eol_lf_with_crlf_in_repo_dirty(self):
223 wt, basis = self.prepare_tree(_sample_text,
224 eol='lf-with-crlf-in-repo')
225 self.assertContent(wt, basis, _sample_text_on_win,
226 _sample_text_on_unix, _sample_text_on_win, roundtrip_to=[])
227
228 def test_eol_crlf_with_crlf_in_repo_dirty(self):
229 wt, basis = self.prepare_tree(_sample_text,
230 eol='crlf-with-crlf-in-repo')
231 self.assertContent(wt, basis, _sample_text_on_win,
232 _sample_text_on_unix, _sample_text_on_win, roundtrip_to=[])
233
234 # Test text with clean line endings, either always lf or always crlf.
235 # This selectively roundtrips (based on what's stored in the repo).
236
237 def test_eol_no_rules_clean_lf(self):
238 wt, basis = self.prepare_tree(_sample_clean_lf)
239 self.assertContent(wt, basis, _sample_clean_lf,
240 _sample_text_on_unix, _sample_text_on_win,
241 roundtrip_to=_LF_IN_REPO)
242
243 def test_eol_no_rules_clean_crlf(self):
244 wt, basis = self.prepare_tree(_sample_clean_crlf)
245 self.assertContent(wt, basis, _sample_clean_crlf,
246 _sample_text_on_unix, _sample_text_on_win,
247 roundtrip_to=_CRLF_IN_REPO)
248
249 def test_eol_exact_clean_lf(self):
250 wt, basis = self.prepare_tree(_sample_clean_lf, eol='exact')
251 self.assertContent(wt, basis, _sample_clean_lf,
252 _sample_text_on_unix, _sample_text_on_win,
253 roundtrip_to=_LF_IN_REPO)
254
255 def test_eol_exact_clean_crlf(self):
256 wt, basis = self.prepare_tree(_sample_clean_crlf, eol='exact')
257 self.assertContent(wt, basis, _sample_clean_crlf,
258 _sample_text_on_unix, _sample_text_on_win,
259 roundtrip_to=_CRLF_IN_REPO)
260
261 def test_eol_native_clean_lf(self):
262 wt, basis = self.prepare_tree(_sample_clean_lf, eol='native')
263 self.assertContent(wt, basis, _sample_text_on_unix,
264 _sample_text_on_unix, _sample_text_on_win,
265 roundtrip_to=_LF_IN_REPO)
266
267 def test_eol_native_clean_crlf(self):
268 wt, basis = self.prepare_tree(_sample_clean_crlf, eol='native')
269 self.assertContent(wt, basis, _sample_text_on_unix,
270 _sample_text_on_unix, _sample_text_on_win,
271 roundtrip_to=_LF_IN_REPO)
272
273 def test_eol_lf_clean_lf(self):
274 wt, basis = self.prepare_tree(_sample_clean_lf, eol='lf')
275 self.assertContent(wt, basis, _sample_text_on_unix,
276 _sample_text_on_unix, _sample_text_on_win,
277 roundtrip_to=_LF_IN_REPO)
278
279 def test_eol_lf_clean_crlf(self):
280 wt, basis = self.prepare_tree(_sample_clean_crlf, eol='lf')
281 self.assertContent(wt, basis, _sample_text_on_unix,
282 _sample_text_on_unix, _sample_text_on_win,
283 roundtrip_to=_LF_IN_REPO)
284
285 def test_eol_crlf_clean_lf(self):
286 wt, basis = self.prepare_tree(_sample_clean_lf, eol='crlf')
287 self.assertContent(wt, basis, _sample_text_on_unix,
288 _sample_text_on_unix, _sample_text_on_win,
289 roundtrip_to=_LF_IN_REPO)
290
291 def test_eol_crlf_clean_crlf(self):
292 wt, basis = self.prepare_tree(_sample_clean_crlf, eol='crlf')
293 self.assertContent(wt, basis, _sample_text_on_unix,
294 _sample_text_on_unix, _sample_text_on_win,
295 roundtrip_to=_LF_IN_REPO)
296
297 def test_eol_native_with_crlf_in_repo_clean_lf(self):
298 wt, basis = self.prepare_tree(_sample_clean_lf,
299 eol='native-with-crlf-in-repo')
300 self.assertContent(wt, basis, _sample_text_on_win,
301 _sample_text_on_unix, _sample_text_on_win,
302 roundtrip_to=_CRLF_IN_REPO)
303
304 def test_eol_native_with_crlf_in_repo_clean_crlf(self):
305 wt, basis = self.prepare_tree(_sample_clean_crlf,
306 eol='native-with-crlf-in-repo')
307 self.assertContent(wt, basis, _sample_text_on_win,
308 _sample_text_on_unix, _sample_text_on_win,
309 roundtrip_to=_CRLF_IN_REPO)
310
311 def test_eol_lf_with_crlf_in_repo_clean_lf(self):
312 wt, basis = self.prepare_tree(_sample_clean_lf,
313 eol='lf-with-crlf-in-repo')
314 self.assertContent(wt, basis, _sample_text_on_win,
315 _sample_text_on_unix, _sample_text_on_win,
316 roundtrip_to=_CRLF_IN_REPO)
317
318 def test_eol_lf_with_crlf_in_repo_clean_crlf(self):
319 wt, basis = self.prepare_tree(_sample_clean_crlf,
320 eol='lf-with-crlf-in-repo')
321 self.assertContent(wt, basis, _sample_text_on_win,
322 _sample_text_on_unix, _sample_text_on_win,
323 roundtrip_to=_CRLF_IN_REPO)
324
325 def test_eol_crlf_with_crlf_in_repo_clean_lf(self):
326 wt, basis = self.prepare_tree(_sample_clean_lf,
327 eol='crlf-with-crlf-in-repo')
328 self.assertContent(wt, basis, _sample_text_on_win,
329 _sample_text_on_unix, _sample_text_on_win,
330 roundtrip_to=_CRLF_IN_REPO)
331
332 def test_eol_crlf_with_crlf_in_repo_clean_crlf(self):
333 wt, basis = self.prepare_tree(_sample_clean_crlf,
334 eol='crlf-with-crlf-in-repo')
335 self.assertContent(wt, basis, _sample_text_on_win,
336 _sample_text_on_unix, _sample_text_on_win,
337 roundtrip_to=_CRLF_IN_REPO)
186338
=== modified file 'bzrlib/workingtree_4.py'
--- bzrlib/workingtree_4.py 2009-06-10 03:56:49 +0000
+++ bzrlib/workingtree_4.py 2009-06-11 04:35:14 +0000
@@ -1440,13 +1440,20 @@
1440 # Note: do NOT move this logic up higher - using the basis from1440 # Note: do NOT move this logic up higher - using the basis from
1441 # the accelerator tree is still desirable because that can save1441 # the accelerator tree is still desirable because that can save
1442 # a minute or more of processing on large trees!1442 # a minute or more of processing on large trees!
1443 # The original tree may not have the same content filters
1444 # applied so we can't safely build the inventory delta from
1445 # the source tree.
1443 if wt.supports_content_filtering():1446 if wt.supports_content_filtering():
1444 accelerator_tree = None1447 accelerator_tree = None
1448 delta_from_tree = False
1449 else:
1450 delta_from_tree = True
1445 # delta_from_tree is safe even for DirStateRevisionTrees,1451 # delta_from_tree is safe even for DirStateRevisionTrees,
1446 # because wt4.apply_inventory_delta does not mutate the input1452 # because wt4.apply_inventory_delta does not mutate the input
1447 # inventory entries.1453 # inventory entries.
1448 transform.build_tree(basis, wt, accelerator_tree,1454 transform.build_tree(basis, wt, accelerator_tree,
1449 hardlink=hardlink, delta_from_tree=True)1455 hardlink=hardlink,
1456 delta_from_tree=delta_from_tree)
1450 finally:1457 finally:
1451 basis.unlock()1458 basis.unlock()
1452 finally:1459 finally: