Merge lp:~mbp/bzr/415508-content-filtering into lp:bzr/1.18
- 415508-content-filtering
- Merge into 1.18
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | not available | ||||
Proposed branch: | lp:~mbp/bzr/415508-content-filtering | ||||
Merge into: | lp:bzr/1.18 | ||||
Diff against target: | 345 lines | ||||
To merge this branch: | bzr merge lp:~mbp/bzr/415508-content-filtering | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ian Clatworthy (community) | Approve | ||
John A Meinel (community) | Approve | ||
Review via email: mp+10640@code.launchpad.net |
Commit message
Description of the change
Martin Pool (mbp) wrote : | # |
John A Meinel (jameinel) wrote : | # |
I think this patch is good.
I don't see a test added that would catch the bug that we discovered. Namely, that we *don't* add a new node for texts that have a content filter, but which have not otherwise changed.
I would like to see something along those lines, but I'm not going to block this patch on it, because I think the bug we have right now is pretty huge and needs fixing.
Martin Pool (mbp) wrote : | # |
I need an additional test change, like this, for the path_content_
=== modified file 'bzrlib/
--- bzrlib/
+++ bzrlib/
@@ -1,4 +1,4 @@
-# Copyright (C) 2007 Canonical Ltd
+# Copyright (C) 2007, 2009 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -34,6 +34,18 @@
return result
+ def check_content_
+ # if the tree supports content filters, then it's allowed to leave out
+ # the size because it might be difficult to compute. otherwise, it
+ # must be present and correct
+ returned_size = summary[1]
+ if returned_size == expected_size or (
+ tree.supports_
+ and returned_size is None):
+ pass
+ else:
+ self.fail("invalid size in summary: %r" % (returned_size,))
+
def test_symlink_
tree = self.make_
@@ -76,8 +88,7 @@
summary = self._convert_
- # size must be known
- self.assertEqua
+ self.check_
# executable
# may have hash,
@@ -91,8 +102,7 @@
summary = self._convert_
- # size must be known
- self.assertEqua
+ self.check_
# not executable
if osutils.
Ian Clatworthy (ian-clatworthy) wrote : | # |
The code and tests look good to me. The docstring and code in _path_content_
The NEWS items needs a trivial tweak: 'of file' -> 'of a file'.
Preview Diff
1 | === modified file 'NEWS' | |||
2 | --- NEWS 2009-08-20 08:38:09 +0000 | |||
3 | +++ NEWS 2009-08-26 06:35:38 +0000 | |||
4 | @@ -7,6 +7,24 @@ | |||
5 | 7 | :depth: 1 | 7 | :depth: 1 |
6 | 8 | 8 | ||
7 | 9 | 9 | ||
8 | 10 | bzr 1.18.1 NOT RELEASED YET | ||
9 | 11 | ########################### | ||
10 | 12 | |||
11 | 13 | Bug Fixes | ||
12 | 14 | ********* | ||
13 | 15 | |||
14 | 16 | * Fixed a problem where using content filtering and especially end-of-line | ||
15 | 17 | conversion will commit too many copies a file. | ||
16 | 18 | (Martin Pool, #415508) | ||
17 | 19 | |||
18 | 20 | API Changes | ||
19 | 21 | *********** | ||
20 | 22 | |||
21 | 23 | * ``Tree.path_content_summary`` may return a size of None, when called on | ||
22 | 24 | a tree with content filtering where the size of the canonical form | ||
23 | 25 | cannot be cheaply determined. (Martin Pool) | ||
24 | 26 | |||
25 | 27 | |||
26 | 10 | bzr 1.18 | 28 | bzr 1.18 |
27 | 11 | ######## | 29 | ######## |
28 | 12 | 30 | ||
29 | 13 | 31 | ||
30 | === modified file 'bzrlib/commit.py' | |||
31 | --- bzrlib/commit.py 2009-07-15 05:54:37 +0000 | |||
32 | +++ bzrlib/commit.py 2009-08-26 06:35:39 +0000 | |||
33 | @@ -802,10 +802,11 @@ | |||
34 | 802 | # _update_builder_with_changes. | 802 | # _update_builder_with_changes. |
35 | 803 | continue | 803 | continue |
36 | 804 | content_summary = self.work_tree.path_content_summary(path) | 804 | content_summary = self.work_tree.path_content_summary(path) |
37 | 805 | kind = content_summary[0] | ||
38 | 805 | # Note that when a filter of specific files is given, we must only | 806 | # Note that when a filter of specific files is given, we must only |
39 | 806 | # skip/record deleted files matching that filter. | 807 | # skip/record deleted files matching that filter. |
40 | 807 | if not specific_files or is_inside_any(specific_files, path): | 808 | if not specific_files or is_inside_any(specific_files, path): |
42 | 808 | if content_summary[0] == 'missing': | 809 | if kind == 'missing': |
43 | 809 | if not deleted_paths: | 810 | if not deleted_paths: |
44 | 810 | # path won't have been split yet. | 811 | # path won't have been split yet. |
45 | 811 | path_segments = splitpath(path) | 812 | path_segments = splitpath(path) |
46 | @@ -818,23 +819,20 @@ | |||
47 | 818 | continue | 819 | continue |
48 | 819 | # TODO: have the builder do the nested commit just-in-time IF and | 820 | # TODO: have the builder do the nested commit just-in-time IF and |
49 | 820 | # only if needed. | 821 | # only if needed. |
51 | 821 | if content_summary[0] == 'tree-reference': | 822 | if kind == 'tree-reference': |
52 | 822 | # enforce repository nested tree policy. | 823 | # enforce repository nested tree policy. |
53 | 823 | if (not self.work_tree.supports_tree_reference() or | 824 | if (not self.work_tree.supports_tree_reference() or |
54 | 824 | # repository does not support it either. | 825 | # repository does not support it either. |
55 | 825 | not self.branch.repository._format.supports_tree_reference): | 826 | not self.branch.repository._format.supports_tree_reference): |
61 | 826 | content_summary = ('directory',) + content_summary[1:] | 827 | kind = 'directory' |
62 | 827 | kind = content_summary[0] | 828 | content_summary = (kind, None, None, None) |
63 | 828 | # TODO: specific_files filtering before nested tree processing | 829 | elif self.recursive == 'down': |
59 | 829 | if kind == 'tree-reference': | ||
60 | 830 | if self.recursive == 'down': | ||
64 | 831 | nested_revision_id = self._commit_nested_tree( | 830 | nested_revision_id = self._commit_nested_tree( |
65 | 832 | file_id, path) | 831 | file_id, path) |
68 | 833 | content_summary = content_summary[:3] + ( | 832 | content_summary = (kind, None, None, nested_revision_id) |
67 | 834 | nested_revision_id,) | ||
69 | 835 | else: | 833 | else: |
72 | 836 | content_summary = content_summary[:3] + ( | 834 | nested_revision_id = self.work_tree.get_reference_revision(file_id) |
73 | 837 | self.work_tree.get_reference_revision(file_id),) | 835 | content_summary = (kind, None, None, nested_revision_id) |
74 | 838 | 836 | ||
75 | 839 | # Record an entry for this item | 837 | # Record an entry for this item |
76 | 840 | # Note: I don't particularly want to have the existing_ie | 838 | # Note: I don't particularly want to have the existing_ie |
77 | 841 | 839 | ||
78 | === modified file 'bzrlib/repository.py' | |||
79 | --- bzrlib/repository.py 2009-08-06 02:23:37 +0000 | |||
80 | +++ bzrlib/repository.py 2009-08-26 06:35:39 +0000 | |||
81 | @@ -464,9 +464,9 @@ | |||
82 | 464 | if content_summary[2] is None: | 464 | if content_summary[2] is None: |
83 | 465 | raise ValueError("Files must not have executable = None") | 465 | raise ValueError("Files must not have executable = None") |
84 | 466 | if not store: | 466 | if not store: |
88 | 467 | if (# if the file length changed we have to store: | 467 | # We can't trust a check of the file length because of content |
89 | 468 | parent_entry.text_size != content_summary[1] or | 468 | # filtering... |
90 | 469 | # if the exec bit has changed we have to store: | 469 | if (# if the exec bit has changed we have to store: |
91 | 470 | parent_entry.executable != content_summary[2]): | 470 | parent_entry.executable != content_summary[2]): |
92 | 471 | store = True | 471 | store = True |
93 | 472 | elif parent_entry.text_sha1 == content_summary[3]: | 472 | elif parent_entry.text_sha1 == content_summary[3]: |
94 | @@ -539,6 +539,9 @@ | |||
95 | 539 | ie.revision = parent_entry.revision | 539 | ie.revision = parent_entry.revision |
96 | 540 | return self._get_delta(ie, basis_inv, path), False, None | 540 | return self._get_delta(ie, basis_inv, path), False, None |
97 | 541 | ie.reference_revision = content_summary[3] | 541 | ie.reference_revision = content_summary[3] |
98 | 542 | if ie.reference_revision is None: | ||
99 | 543 | raise AssertionError("invalid content_summary for nested tree: %r" | ||
100 | 544 | % (content_summary,)) | ||
101 | 542 | self._add_text_to_weave(ie.file_id, '', heads, None) | 545 | self._add_text_to_weave(ie.file_id, '', heads, None) |
102 | 543 | else: | 546 | else: |
103 | 544 | raise NotImplementedError('unknown kind') | 547 | raise NotImplementedError('unknown kind') |
104 | 545 | 548 | ||
105 | === modified file 'bzrlib/tests/per_tree/test_path_content_summary.py' | |||
106 | --- bzrlib/tests/per_tree/test_path_content_summary.py 2009-07-10 07:14:02 +0000 | |||
107 | +++ bzrlib/tests/per_tree/test_path_content_summary.py 2009-08-26 06:35:39 +0000 | |||
108 | @@ -1,4 +1,4 @@ | |||
110 | 1 | # Copyright (C) 2007 Canonical Ltd | 1 | # Copyright (C) 2007, 2009 Canonical Ltd |
111 | 2 | # | 2 | # |
112 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
113 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
114 | @@ -34,6 +34,18 @@ | |||
115 | 34 | self.addCleanup(result.unlock) | 34 | self.addCleanup(result.unlock) |
116 | 35 | return result | 35 | return result |
117 | 36 | 36 | ||
118 | 37 | def check_content_summary_size(self, tree, summary, expected_size): | ||
119 | 38 | # if the tree supports content filters, then it's allowed to leave out | ||
120 | 39 | # the size because it might be difficult to compute. otherwise, it | ||
121 | 40 | # must be present and correct | ||
122 | 41 | returned_size = summary[1] | ||
123 | 42 | if returned_size == expected_size or ( | ||
124 | 43 | tree.supports_content_filtering() | ||
125 | 44 | and returned_size is None): | ||
126 | 45 | pass | ||
127 | 46 | else: | ||
128 | 47 | self.fail("invalid size in summary: %r" % (returned_size,)) | ||
129 | 48 | |||
130 | 37 | def test_symlink_content_summary(self): | 49 | def test_symlink_content_summary(self): |
131 | 38 | self.requireFeature(tests.SymlinkFeature) | 50 | self.requireFeature(tests.SymlinkFeature) |
132 | 39 | tree = self.make_branch_and_tree('tree') | 51 | tree = self.make_branch_and_tree('tree') |
133 | @@ -76,8 +88,7 @@ | |||
134 | 76 | summary = self._convert_tree(tree).path_content_summary('path') | 88 | summary = self._convert_tree(tree).path_content_summary('path') |
135 | 77 | self.assertEqual(4, len(summary)) | 89 | self.assertEqual(4, len(summary)) |
136 | 78 | self.assertEqual('file', summary[0]) | 90 | self.assertEqual('file', summary[0]) |
139 | 79 | # size must be known | 91 | self.check_content_summary_size(tree, summary, 22) |
138 | 80 | self.assertEqual(22, summary[1]) | ||
140 | 81 | # executable | 92 | # executable |
141 | 82 | self.assertEqual(True, summary[2]) | 93 | self.assertEqual(True, summary[2]) |
142 | 83 | # may have hash, | 94 | # may have hash, |
143 | @@ -91,8 +102,7 @@ | |||
144 | 91 | summary = self._convert_tree(tree).path_content_summary('path') | 102 | summary = self._convert_tree(tree).path_content_summary('path') |
145 | 92 | self.assertEqual(4, len(summary)) | 103 | self.assertEqual(4, len(summary)) |
146 | 93 | self.assertEqual('file', summary[0]) | 104 | self.assertEqual('file', summary[0]) |
149 | 94 | # size must be known | 105 | self.check_content_summary_size(tree, summary, 22) |
148 | 95 | self.assertEqual(22, summary[1]) | ||
150 | 96 | # not executable | 106 | # not executable |
151 | 97 | if osutils.supports_executable: | 107 | if osutils.supports_executable: |
152 | 98 | self.assertEqual(False, summary[2]) | 108 | self.assertEqual(False, summary[2]) |
153 | 99 | 109 | ||
154 | === modified file 'bzrlib/tests/per_workingtree/test_content_filters.py' | |||
155 | --- bzrlib/tests/per_workingtree/test_content_filters.py 2009-07-10 07:14:02 +0000 | |||
156 | +++ bzrlib/tests/per_workingtree/test_content_filters.py 2009-08-26 06:35:39 +0000 | |||
157 | @@ -43,6 +43,25 @@ | |||
158 | 43 | return _converter_helper(chunks, 'lower') | 43 | return _converter_helper(chunks, 'lower') |
159 | 44 | 44 | ||
160 | 45 | 45 | ||
161 | 46 | _trailer_string = '\nend string\n' | ||
162 | 47 | |||
163 | 48 | |||
164 | 49 | def _append_text(chunks, context=None): | ||
165 | 50 | """A content filter that appends a string to the end of the file. | ||
166 | 51 | |||
167 | 52 | This tests filters that change the length.""" | ||
168 | 53 | return chunks + [_trailer_string] | ||
169 | 54 | |||
170 | 55 | |||
171 | 56 | def _remove_appended_text(chunks, context=None): | ||
172 | 57 | """Remove the appended text.""" | ||
173 | 58 | |||
174 | 59 | text = ''.join(chunks) | ||
175 | 60 | if text.endswith(_trailer_string): | ||
176 | 61 | text = text[:-len(_trailer_string)] | ||
177 | 62 | return [text] | ||
178 | 63 | |||
179 | 64 | |||
180 | 46 | class TestWorkingTreeWithContentFilters(TestCaseWithWorkingTree): | 65 | class TestWorkingTreeWithContentFilters(TestCaseWithWorkingTree): |
181 | 47 | 66 | ||
182 | 48 | def create_cf_tree(self, txt_reader, txt_writer, dir='.'): | 67 | def create_cf_tree(self, txt_reader, txt_writer, dir='.'): |
183 | @@ -159,3 +178,34 @@ | |||
184 | 159 | self.assertFileEqual("fOO tXT", 'target/file1.txt') | 178 | self.assertFileEqual("fOO tXT", 'target/file1.txt') |
185 | 160 | changes = target.changes_from(source.basis_tree()) | 179 | changes = target.changes_from(source.basis_tree()) |
186 | 161 | self.assertFalse(changes.has_changed()) | 180 | self.assertFalse(changes.has_changed()) |
187 | 181 | |||
188 | 182 | def test_path_content_summary(self): | ||
189 | 183 | """path_content_summary should always talk about the canonical form.""" | ||
190 | 184 | # see https://bugs.edge.launchpad.net/bzr/+bug/415508 | ||
191 | 185 | # | ||
192 | 186 | # set up a tree where the canonical form has a string added to the | ||
193 | 187 | # end | ||
194 | 188 | source, txt_fileid, bin_fileid = self.create_cf_tree( | ||
195 | 189 | txt_reader=_append_text, | ||
196 | 190 | txt_writer=_remove_appended_text, | ||
197 | 191 | dir='source') | ||
198 | 192 | if not source.supports_content_filtering(): | ||
199 | 193 | return | ||
200 | 194 | source.lock_read() | ||
201 | 195 | self.addCleanup(source.unlock) | ||
202 | 196 | |||
203 | 197 | expected_canonical_form = 'Foo Txt\nend string\n' | ||
204 | 198 | self.assertEquals(source.get_file(txt_fileid, filtered=True).read(), | ||
205 | 199 | expected_canonical_form) | ||
206 | 200 | self.assertEquals(source.get_file(txt_fileid, filtered=False).read(), | ||
207 | 201 | 'Foo Txt') | ||
208 | 202 | |||
209 | 203 | # results are: kind, size, executable, sha1_or_link_target | ||
210 | 204 | result = source.path_content_summary('file1.txt') | ||
211 | 205 | |||
212 | 206 | self.assertEquals(result, | ||
213 | 207 | ('file', None, False, None)) | ||
214 | 208 | |||
215 | 209 | # we could give back the length of the canonical form, but in general | ||
216 | 210 | # that will be expensive to compute, so it's acceptable to just return | ||
217 | 211 | # None. | ||
218 | 162 | 212 | ||
219 | === modified file 'bzrlib/tree.py' | |||
220 | --- bzrlib/tree.py 2009-07-17 06:04:35 +0000 | |||
221 | +++ bzrlib/tree.py 2009-08-26 06:35:39 +0000 | |||
222 | @@ -218,10 +218,14 @@ | |||
223 | 218 | def path_content_summary(self, path): | 218 | def path_content_summary(self, path): |
224 | 219 | """Get a summary of the information about path. | 219 | """Get a summary of the information about path. |
225 | 220 | 220 | ||
226 | 221 | All the attributes returned are for the canonical form, not the | ||
227 | 222 | convenient form (if content filters are in use.) | ||
228 | 223 | |||
229 | 221 | :param path: A relative path within the tree. | 224 | :param path: A relative path within the tree. |
230 | 222 | :return: A tuple containing kind, size, exec, sha1-or-link. | 225 | :return: A tuple containing kind, size, exec, sha1-or-link. |
231 | 223 | Kind is always present (see tree.kind()). | 226 | Kind is always present (see tree.kind()). |
233 | 224 | size is present if kind is file, None otherwise. | 227 | size is present if kind is file and the size of the |
234 | 228 | canonical form can be cheaply determined, None otherwise. | ||
235 | 225 | exec is None unless kind is file and the platform supports the 'x' | 229 | exec is None unless kind is file and the platform supports the 'x' |
236 | 226 | bit. | 230 | bit. |
237 | 227 | sha1-or-link is the link target if kind is symlink, or the sha1 if | 231 | sha1-or-link is the link target if kind is symlink, or the sha1 if |
238 | 228 | 232 | ||
239 | === modified file 'bzrlib/workingtree.py' | |||
240 | --- bzrlib/workingtree.py 2009-08-04 04:36:34 +0000 | |||
241 | +++ bzrlib/workingtree.py 2009-08-26 06:35:39 +0000 | |||
242 | @@ -613,6 +613,8 @@ | |||
243 | 613 | 613 | ||
244 | 614 | def get_file_size(self, file_id): | 614 | def get_file_size(self, file_id): |
245 | 615 | """See Tree.get_file_size""" | 615 | """See Tree.get_file_size""" |
246 | 616 | # XXX: this returns the on-disk size; it should probably return the | ||
247 | 617 | # canonical size | ||
248 | 616 | try: | 618 | try: |
249 | 617 | return os.path.getsize(self.id2abspath(file_id)) | 619 | return os.path.getsize(self.id2abspath(file_id)) |
250 | 618 | except OSError, e: | 620 | except OSError, e: |
251 | @@ -749,11 +751,7 @@ | |||
252 | 749 | raise | 751 | raise |
253 | 750 | kind = _mapper(stat_result.st_mode) | 752 | kind = _mapper(stat_result.st_mode) |
254 | 751 | if kind == 'file': | 753 | if kind == 'file': |
260 | 752 | size = stat_result.st_size | 754 | return self._file_content_summary(path, stat_result) |
256 | 753 | # try for a stat cache lookup | ||
257 | 754 | executable = self._is_executable_from_path_and_stat(path, stat_result) | ||
258 | 755 | return (kind, size, executable, self._sha_from_stat( | ||
259 | 756 | path, stat_result)) | ||
261 | 757 | elif kind == 'directory': | 755 | elif kind == 'directory': |
262 | 758 | # perhaps it looks like a plain directory, but it's really a | 756 | # perhaps it looks like a plain directory, but it's really a |
263 | 759 | # reference. | 757 | # reference. |
264 | @@ -766,6 +764,13 @@ | |||
265 | 766 | else: | 764 | else: |
266 | 767 | return (kind, None, None, None) | 765 | return (kind, None, None, None) |
267 | 768 | 766 | ||
268 | 767 | def _file_content_summary(self, path, stat_result): | ||
269 | 768 | size = stat_result.st_size | ||
270 | 769 | executable = self._is_executable_from_path_and_stat(path, stat_result) | ||
271 | 770 | # try for a stat cache lookup | ||
272 | 771 | return ('file', size, executable, self._sha_from_stat( | ||
273 | 772 | path, stat_result)) | ||
274 | 773 | |||
275 | 769 | def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost): | 774 | def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost): |
276 | 770 | """Common ghost checking functionality from set_parent_*. | 775 | """Common ghost checking functionality from set_parent_*. |
277 | 771 | 776 | ||
278 | 772 | 777 | ||
279 | === modified file 'bzrlib/workingtree_4.py' | |||
280 | --- bzrlib/workingtree_4.py 2009-08-04 11:25:23 +0000 | |||
281 | +++ bzrlib/workingtree_4.py 2009-08-26 06:35:39 +0000 | |||
282 | @@ -1300,6 +1300,32 @@ | |||
283 | 1300 | return statvalue, sha1 | 1300 | return statvalue, sha1 |
284 | 1301 | 1301 | ||
285 | 1302 | 1302 | ||
286 | 1303 | class ContentFilteringDirStateWorkingTree(DirStateWorkingTree): | ||
287 | 1304 | """Dirstate working tree that supports content filtering. | ||
288 | 1305 | |||
289 | 1306 | The dirstate holds the hash and size of the canonical form of the file, | ||
290 | 1307 | and most methods must return that. | ||
291 | 1308 | """ | ||
292 | 1309 | |||
293 | 1310 | def _file_content_summary(self, path, stat_result): | ||
294 | 1311 | # This is to support the somewhat obsolete path_content_summary method | ||
295 | 1312 | # with content filtering: see | ||
296 | 1313 | # <https://bugs.edge.launchpad.net/bzr/+bug/415508>. | ||
297 | 1314 | # | ||
298 | 1315 | # If the dirstate cache is up to date and knows the hash and size, | ||
299 | 1316 | # return that. | ||
300 | 1317 | # Otherwise if there are no content filters, return the on-disk size | ||
301 | 1318 | # and leave the hash blank. | ||
302 | 1319 | # Otherwise, read and filter the on-disk file and use its size and | ||
303 | 1320 | # hash. | ||
304 | 1321 | # | ||
305 | 1322 | # The dirstate doesn't store the size of the canonical form so we | ||
306 | 1323 | # can't trust it for content-filtered trees. We just return None. | ||
307 | 1324 | dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result) | ||
308 | 1325 | executable = self._is_executable_from_path_and_stat(path, stat_result) | ||
309 | 1326 | return ('file', None, executable, dirstate_sha1) | ||
310 | 1327 | |||
311 | 1328 | |||
312 | 1303 | class WorkingTree4(DirStateWorkingTree): | 1329 | class WorkingTree4(DirStateWorkingTree): |
313 | 1304 | """This is the Format 4 working tree. | 1330 | """This is the Format 4 working tree. |
314 | 1305 | 1331 | ||
315 | @@ -1313,7 +1339,7 @@ | |||
316 | 1313 | """ | 1339 | """ |
317 | 1314 | 1340 | ||
318 | 1315 | 1341 | ||
320 | 1316 | class WorkingTree5(DirStateWorkingTree): | 1342 | class WorkingTree5(ContentFilteringDirStateWorkingTree): |
321 | 1317 | """This is the Format 5 working tree. | 1343 | """This is the Format 5 working tree. |
322 | 1318 | 1344 | ||
323 | 1319 | This differs from WorkingTree4 by: | 1345 | This differs from WorkingTree4 by: |
324 | @@ -1323,7 +1349,7 @@ | |||
325 | 1323 | """ | 1349 | """ |
326 | 1324 | 1350 | ||
327 | 1325 | 1351 | ||
329 | 1326 | class WorkingTree6(DirStateWorkingTree): | 1352 | class WorkingTree6(ContentFilteringDirStateWorkingTree): |
330 | 1327 | """This is the Format 6 working tree. | 1353 | """This is the Format 6 working tree. |
331 | 1328 | 1354 | ||
332 | 1329 | This differs from WorkingTree5 by: | 1355 | This differs from WorkingTree5 by: |
333 | @@ -1550,7 +1576,11 @@ | |||
334 | 1550 | 1576 | ||
335 | 1551 | 1577 | ||
336 | 1552 | class DirStateRevisionTree(Tree): | 1578 | class DirStateRevisionTree(Tree): |
338 | 1553 | """A revision tree pulling the inventory from a dirstate.""" | 1579 | """A revision tree pulling the inventory from a dirstate. |
339 | 1580 | |||
340 | 1581 | Note that this is one of the historical (ie revision) trees cached in the | ||
341 | 1582 | dirstate for easy access, not the workingtree. | ||
342 | 1583 | """ | ||
343 | 1554 | 1584 | ||
344 | 1555 | def __init__(self, dirstate, revision_id, repository): | 1585 | def __init__(self, dirstate, revision_id, repository): |
345 | 1556 | self._dirstate = dirstate | 1586 | self._dirstate = dirstate |
fix for https:/ /bugs.edge. launchpad. net/bzr/ +bug/415508:
* when getting the contents_summary on a file, don't return the size because it's not needed and it's not cheap to compute for content-filtered trees