Merge lp:~jelmer/brz/preview-tree into lp:brz/3.1
- preview-tree
- Merge into 3.1
Proposed by
Jelmer Vernooij
Status: | Merged |
---|---|
Approved by: | Jelmer Vernooij |
Approved revision: | no longer in the source branch. |
Merge reported by: | The Breezy Bot |
Merged at revision: | not available |
Proposed branch: | lp:~jelmer/brz/preview-tree |
Merge into: | lp:brz/3.1 |
Diff against target: |
724 lines (+228/-215) 5 files modified
breezy/bzr/transform.py (+23/-190) breezy/tests/per_tree/test_path_content_summary.py (+3/-3) breezy/tests/per_tree/test_symlinks.py (+2/-2) breezy/tests/per_workingtree/test_transform.py (+18/-20) breezy/transform.py (+182/-0) |
To merge this branch: | bzr merge lp:~jelmer/brz/preview-tree |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email: mp+387003@code.launchpad.net |
Commit message
Factor out common PreviewTree functions.
Description of the change
Factor out common PreviewTree functions.
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'breezy/bzr/transform.py' |
2 | --- breezy/bzr/transform.py 2020-07-06 01:43:04 +0000 |
3 | +++ breezy/bzr/transform.py 2020-07-07 22:38:02 +0000 |
4 | @@ -51,6 +51,7 @@ |
5 | ImmortalLimbo, |
6 | ReusingTransform, |
7 | MalformedTransform, |
8 | + PreviewTree, |
9 | ) |
10 | from ..tree import TreeChange |
11 | from . import ( |
12 | @@ -1655,7 +1656,7 @@ |
13 | The tree is a snapshot, and altering the TreeTransform will invalidate |
14 | it. |
15 | """ |
16 | - return _PreviewTree(self) |
17 | + return InventoryPreviewTree(self) |
18 | |
19 | def _inventory_altered(self): |
20 | """Determine which trans_ids need new Inventory entries. |
21 | @@ -1807,22 +1808,17 @@ |
22 | raise NotImplementedError(self.new_orphan) |
23 | |
24 | |
25 | -class _PreviewTree(inventorytree.InventoryTree): |
26 | +class InventoryPreviewTree(PreviewTree, inventorytree.InventoryTree): |
27 | """Partial implementation of Tree to support show_diff_trees""" |
28 | |
29 | def __init__(self, transform): |
30 | - self._transform = transform |
31 | + PreviewTree.__init__(self, transform) |
32 | self._final_paths = FinalPaths(transform) |
33 | - self.__by_parent = None |
34 | - self._parent_ids = [] |
35 | - self._all_children_cache = {} |
36 | - self._path2trans_id_cache = {} |
37 | - self._final_name_cache = {} |
38 | self._iter_changes_cache = { |
39 | c.file_id: c for c in self._transform.iter_changes()} |
40 | |
41 | def supports_tree_reference(self): |
42 | - # TODO(jelmer): Support tree references in _PreviewTree. |
43 | + # TODO(jelmer): Support tree references in PreviewTree. |
44 | # return self._transform._tree.supports_tree_reference() |
45 | return False |
46 | |
47 | @@ -1831,19 +1827,6 @@ |
48 | changes = self._iter_changes_cache.get(file_id) |
49 | return (changes is not None and changes.changed_content) |
50 | |
51 | - def _get_repository(self): |
52 | - repo = getattr(self._transform._tree, '_repository', None) |
53 | - if repo is None: |
54 | - repo = self._transform._tree.branch.repository |
55 | - return repo |
56 | - |
57 | - def _iter_parent_trees(self): |
58 | - for revision_id in self.get_parent_ids(): |
59 | - try: |
60 | - yield self.revision_tree(revision_id) |
61 | - except errors.NoSuchRevisionInTree: |
62 | - yield self._get_repository().revision_tree(revision_id) |
63 | - |
64 | def _get_file_revision(self, path, file_id, vf, tree_revision): |
65 | parent_keys = [ |
66 | (file_id, t.get_file_revision(t.id2path(file_id))) |
67 | @@ -1860,12 +1843,6 @@ |
68 | name = self._transform._limbo_name(trans_id) |
69 | return os.lstat(name) |
70 | |
71 | - @property |
72 | - def _by_parent(self): |
73 | - if self.__by_parent is None: |
74 | - self.__by_parent = self._transform.by_parent() |
75 | - return self.__by_parent |
76 | - |
77 | def _comparison_data(self, entry, path): |
78 | kind, size, executable, link_or_sha1 = self.path_content_summary(path) |
79 | if kind == 'missing': |
80 | @@ -1876,20 +1853,10 @@ |
81 | executable = self.is_executable(path) |
82 | return kind, executable, None |
83 | |
84 | - def is_locked(self): |
85 | - return False |
86 | - |
87 | - def lock_read(self): |
88 | - # Perhaps in theory, this should lock the TreeTransform? |
89 | - return lock.LogicalLockResult(self.unlock) |
90 | - |
91 | - def unlock(self): |
92 | - pass |
93 | - |
94 | @property |
95 | def root_inventory(self): |
96 | """This Tree does not use inventory as its backing data.""" |
97 | - raise NotImplementedError(_PreviewTree.root_inventory) |
98 | + raise NotImplementedError(PreviewTree.root_inventory) |
99 | |
100 | def all_file_ids(self): |
101 | tree_ids = set(self._transform._tree.all_file_ids()) |
102 | @@ -1911,28 +1878,6 @@ |
103 | |
104 | return tree_paths |
105 | |
106 | - def _path2trans_id(self, path): |
107 | - # We must not use None here, because that is a valid value to store. |
108 | - trans_id = self._path2trans_id_cache.get(path, object) |
109 | - if trans_id is not object: |
110 | - return trans_id |
111 | - segments = osutils.splitpath(path) |
112 | - cur_parent = self._transform.root |
113 | - for cur_segment in segments: |
114 | - for child in self._all_children(cur_parent): |
115 | - final_name = self._final_name_cache.get(child) |
116 | - if final_name is None: |
117 | - final_name = self._transform.final_name(child) |
118 | - self._final_name_cache[child] = final_name |
119 | - if final_name == cur_segment: |
120 | - cur_parent = child |
121 | - break |
122 | - else: |
123 | - self._path2trans_id_cache[path] = None |
124 | - return None |
125 | - self._path2trans_id_cache[path] = cur_parent |
126 | - return cur_parent |
127 | - |
128 | def path2id(self, path): |
129 | if isinstance(path, list): |
130 | if path == []: |
131 | @@ -1947,17 +1892,6 @@ |
132 | except NoFinalPath: |
133 | raise errors.NoSuchId(self, file_id) |
134 | |
135 | - def _all_children(self, trans_id): |
136 | - children = self._all_children_cache.get(trans_id) |
137 | - if children is not None: |
138 | - return children |
139 | - children = set(self._transform.iter_tree_children(trans_id)) |
140 | - # children in the _new_parent set are provided by _by_parent. |
141 | - children.difference_update(self._transform._new_parent) |
142 | - children.update(self._by_parent.get(trans_id, [])) |
143 | - self._all_children_cache[trans_id] = children |
144 | - return children |
145 | - |
146 | def extras(self): |
147 | possible_extras = set(self._transform.trans_id_tree_path(p) for p |
148 | in self._transform._tree.extras()) |
149 | @@ -2069,21 +2003,6 @@ |
150 | for path, entry in entries: |
151 | yield path, 'V', entry.kind, entry |
152 | |
153 | - def kind(self, path): |
154 | - trans_id = self._path2trans_id(path) |
155 | - if trans_id is None: |
156 | - raise errors.NoSuchFile(path) |
157 | - return self._transform.final_kind(trans_id) |
158 | - |
159 | - def stored_kind(self, path): |
160 | - trans_id = self._path2trans_id(path) |
161 | - if trans_id is None: |
162 | - raise errors.NoSuchFile(path) |
163 | - try: |
164 | - return self._transform._new_contents[trans_id] |
165 | - except KeyError: |
166 | - return self._transform._tree.stored_kind(path) |
167 | - |
168 | def get_file_mtime(self, path): |
169 | """See Tree.get_file_mtime""" |
170 | file_id = self.path2id(path) |
171 | @@ -2095,77 +2014,6 @@ |
172 | trans_id = self._path2trans_id(path) |
173 | return self._stat_limbo_file(trans_id).st_mtime |
174 | |
175 | - def get_file_size(self, path): |
176 | - """See Tree.get_file_size""" |
177 | - trans_id = self._path2trans_id(path) |
178 | - if trans_id is None: |
179 | - raise errors.NoSuchFile(path) |
180 | - kind = self._transform.final_kind(trans_id) |
181 | - if kind != 'file': |
182 | - return None |
183 | - if trans_id in self._transform._new_contents: |
184 | - return self._stat_limbo_file(trans_id).st_size |
185 | - if self.kind(path) == 'file': |
186 | - return self._transform._tree.get_file_size(path) |
187 | - else: |
188 | - return None |
189 | - |
190 | - def get_file_verifier(self, path, stat_value=None): |
191 | - trans_id = self._path2trans_id(path) |
192 | - if trans_id is None: |
193 | - raise errors.NoSuchFile(path) |
194 | - kind = self._transform._new_contents.get(trans_id) |
195 | - if kind is None: |
196 | - return self._transform._tree.get_file_verifier(path) |
197 | - if kind == 'file': |
198 | - with self.get_file(path) as fileobj: |
199 | - return ("SHA1", osutils.sha_file(fileobj)) |
200 | - |
201 | - def get_file_sha1(self, path, stat_value=None): |
202 | - trans_id = self._path2trans_id(path) |
203 | - if trans_id is None: |
204 | - raise errors.NoSuchFile(path) |
205 | - kind = self._transform._new_contents.get(trans_id) |
206 | - if kind is None: |
207 | - return self._transform._tree.get_file_sha1(path) |
208 | - if kind == 'file': |
209 | - with self.get_file(path) as fileobj: |
210 | - return osutils.sha_file(fileobj) |
211 | - |
212 | - def get_reference_revision(self, path): |
213 | - trans_id = self._path2trans_id(path) |
214 | - if trans_id is None: |
215 | - raise errors.NoSuchFile(path) |
216 | - reference_revision = self._transform._new_reference_revision.get(trans_id) |
217 | - if reference_revision is None: |
218 | - return self._transform._tree.get_reference_revision(path) |
219 | - return reference_revision |
220 | - |
221 | - def is_executable(self, path): |
222 | - trans_id = self._path2trans_id(path) |
223 | - if trans_id is None: |
224 | - return False |
225 | - try: |
226 | - return self._transform._new_executability[trans_id] |
227 | - except KeyError: |
228 | - try: |
229 | - return self._transform._tree.is_executable(path) |
230 | - except OSError as e: |
231 | - if e.errno == errno.ENOENT: |
232 | - return False |
233 | - raise |
234 | - except errors.NoSuchFile: |
235 | - return False |
236 | - |
237 | - def has_filename(self, path): |
238 | - trans_id = self._path2trans_id(path) |
239 | - if trans_id in self._transform._new_contents: |
240 | - return True |
241 | - elif trans_id in self._transform._removed_contents: |
242 | - return False |
243 | - else: |
244 | - return self._transform._tree.has_filename(path) |
245 | - |
246 | def path_content_summary(self, path): |
247 | trans_id = self._path2trans_id(path) |
248 | tt = self._transform |
249 | @@ -2219,18 +2067,6 @@ |
250 | raise ValueError('want_unversioned is not supported') |
251 | return self._transform.iter_changes() |
252 | |
253 | - def get_file(self, path): |
254 | - """See Tree.get_file""" |
255 | - file_id = self.path2id(path) |
256 | - if not self._content_change(file_id): |
257 | - return self._transform._tree.get_file(path) |
258 | - trans_id = self._path2trans_id(path) |
259 | - name = self._transform._limbo_name(trans_id) |
260 | - return open(name, 'rb') |
261 | - |
262 | - def get_file_with_stat(self, path): |
263 | - return self.get_file(path), None |
264 | - |
265 | def annotate_iter(self, path, |
266 | default_revision=_mod_revision.CURRENT_REVISION): |
267 | file_id = self.path2id(path) |
268 | @@ -2263,15 +2099,6 @@ |
269 | self.get_file(path).readlines(), |
270 | default_revision) |
271 | |
272 | - def get_symlink_target(self, path): |
273 | - """See Tree.get_symlink_target""" |
274 | - file_id = self.path2id(path) |
275 | - if not self._content_change(file_id): |
276 | - return self._transform._tree.get_symlink_target(path) |
277 | - trans_id = self._path2trans_id(path) |
278 | - name = self._transform._limbo_name(trans_id) |
279 | - return osutils.readlink(name) |
280 | - |
281 | def walkdirs(self, prefix=''): |
282 | pending = [self._transform.root] |
283 | while len(pending) > 0: |
284 | @@ -2302,14 +2129,20 @@ |
285 | pending.extend(sorted(subdirs, key=self._final_paths.get_path, |
286 | reverse=True)) |
287 | |
288 | - def get_parent_ids(self): |
289 | - return self._parent_ids |
290 | - |
291 | - def set_parent_ids(self, parent_ids): |
292 | - self._parent_ids = parent_ids |
293 | - |
294 | - def get_revision_tree(self, revision_id): |
295 | - return self._transform._tree.get_revision_tree(revision_id) |
296 | - |
297 | - |
298 | - |
299 | + def get_symlink_target(self, path): |
300 | + """See Tree.get_symlink_target""" |
301 | + file_id = self.path2id(path) |
302 | + if not self._content_change(file_id): |
303 | + return self._transform._tree.get_symlink_target(path) |
304 | + trans_id = self._path2trans_id(path) |
305 | + name = self._transform._limbo_name(trans_id) |
306 | + return osutils.readlink(name) |
307 | + |
308 | + def get_file(self, path): |
309 | + """See Tree.get_file""" |
310 | + file_id = self.path2id(path) |
311 | + if not self._content_change(file_id): |
312 | + return self._transform._tree.get_file(path) |
313 | + trans_id = self._path2trans_id(path) |
314 | + name = self._transform._limbo_name(trans_id) |
315 | + return open(name, 'rb') |
316 | |
317 | === modified file 'breezy/tests/per_tree/test_path_content_summary.py' |
318 | --- breezy/tests/per_tree/test_path_content_summary.py 2020-07-04 12:29:00 +0000 |
319 | +++ breezy/tests/per_tree/test_path_content_summary.py 2020-07-07 22:38:02 +0000 |
320 | @@ -23,8 +23,8 @@ |
321 | tests, |
322 | ) |
323 | |
324 | -from breezy.bzr.transform import ( |
325 | - _PreviewTree, |
326 | +from breezy.transform import ( |
327 | + PreviewTree, |
328 | ) |
329 | from breezy.tests import ( |
330 | features, |
331 | @@ -114,7 +114,7 @@ |
332 | self.assertEqual('missing', summary[0]) |
333 | self.assertIs(None, summary[2]) |
334 | self.assertIs(None, summary[3]) |
335 | - elif isinstance(tree, _PreviewTree): |
336 | + elif isinstance(tree, PreviewTree): |
337 | self.expectFailure('PreviewTree returns "missing" for unversioned' |
338 | 'files', self.assertEqual, 'file', summary[0]) |
339 | self.assertEqual('file', summary[0]) |
340 | |
341 | === modified file 'breezy/tests/per_tree/test_symlinks.py' |
342 | --- breezy/tests/per_tree/test_symlinks.py 2020-06-30 20:39:13 +0000 |
343 | +++ breezy/tests/per_tree/test_symlinks.py 2020-07-07 22:38:02 +0000 |
344 | @@ -27,7 +27,7 @@ |
345 | ) |
346 | from breezy.mutabletree import MutableTree |
347 | from breezy.tests import TestSkipped |
348 | -from breezy.bzr.transform import _PreviewTree |
349 | +from breezy.transform import PreviewTree |
350 | from breezy.tests import ( |
351 | features, |
352 | ) |
353 | @@ -55,7 +55,7 @@ |
354 | self.addCleanup(self.tree.unlock) |
355 | |
356 | def test_symlink_target(self): |
357 | - if isinstance(self.tree, (MutableTree, _PreviewTree)): |
358 | + if isinstance(self.tree, (MutableTree, PreviewTree)): |
359 | raise TestSkipped( |
360 | 'symlinks not accurately represented in working trees and' |
361 | ' preview trees') |
362 | |
363 | === modified file 'breezy/tests/per_workingtree/test_transform.py' |
364 | --- breezy/tests/per_workingtree/test_transform.py 2020-07-06 01:43:04 +0000 |
365 | +++ breezy/tests/per_workingtree/test_transform.py 2020-07-07 22:38:02 +0000 |
366 | @@ -1140,7 +1140,7 @@ |
367 | self.assertEqual([bar1_abspath], stat_paths) |
368 | |
369 | def test_iter_changes(self): |
370 | - self.wt.set_root_id(b'eert_toor') |
371 | + root_id = self.wt.path2id('') |
372 | transform, root = self.transform() |
373 | transform.new_file('old', root, [b'blah'], b'id-1', True) |
374 | transform.apply() |
375 | @@ -1150,13 +1150,13 @@ |
376 | old = transform.trans_id_tree_path('old') |
377 | transform.unversion_file(old) |
378 | self.assertEqual([(b'id-1', ('old', None), False, (True, False), |
379 | - (b'eert_toor', b'eert_toor'), |
380 | + (root_id, root_id), |
381 | ('old', 'old'), ('file', 'file'), |
382 | (True, True), False)], |
383 | list(transform.iter_changes())) |
384 | transform.new_directory('new', root, b'id-1') |
385 | self.assertEqual([(b'id-1', ('old', 'new'), True, (True, True), |
386 | - (b'eert_toor', b'eert_toor'), ('old', 'new'), |
387 | + (root_id, root_id), ('old', 'new'), |
388 | ('file', 'directory'), |
389 | (True, False), False)], |
390 | list(transform.iter_changes())) |
391 | @@ -1164,7 +1164,7 @@ |
392 | transform.finalize() |
393 | |
394 | def test_iter_changes_new(self): |
395 | - self.wt.set_root_id(b'eert_toor') |
396 | + root_id = self.wt.path2id('') |
397 | transform, root = self.transform() |
398 | transform.new_file('old', root, [b'blah']) |
399 | transform.apply() |
400 | @@ -1173,7 +1173,7 @@ |
401 | old = transform.trans_id_tree_path('old') |
402 | transform.version_file(old, file_id=b'id-1') |
403 | self.assertEqual([(b'id-1', (None, 'old'), False, (False, True), |
404 | - (b'eert_toor', b'eert_toor'), |
405 | + (root_id, root_id), |
406 | ('old', 'old'), ('file', 'file'), |
407 | (False, False), False)], |
408 | list(transform.iter_changes())) |
409 | @@ -1181,7 +1181,7 @@ |
410 | transform.finalize() |
411 | |
412 | def test_iter_changes_modifications(self): |
413 | - self.wt.set_root_id(b'eert_toor') |
414 | + root_id = self.wt.path2id('') |
415 | transform, root = self.transform() |
416 | transform.new_file('old', root, [b'blah'], b'id-1') |
417 | transform.new_file('new', root, [b'blah']) |
418 | @@ -1197,7 +1197,7 @@ |
419 | # content deletion |
420 | transform.delete_contents(old) |
421 | self.assertEqual([(b'id-1', ('old', 'old'), True, (True, True), |
422 | - (b'eert_toor', b'eert_toor'), |
423 | + (root_id, root_id), |
424 | ('old', 'old'), ('file', None), |
425 | (False, False), False)], |
426 | list(transform.iter_changes())) |
427 | @@ -1205,13 +1205,13 @@ |
428 | # content change |
429 | transform.create_file([b'blah'], old) |
430 | self.assertEqual([(b'id-1', ('old', 'old'), True, (True, True), |
431 | - (b'eert_toor', b'eert_toor'), |
432 | + (root_id, root_id), |
433 | ('old', 'old'), ('file', 'file'), |
434 | (False, False), False)], |
435 | list(transform.iter_changes())) |
436 | transform.cancel_deletion(old) |
437 | self.assertEqual([(b'id-1', ('old', 'old'), True, (True, True), |
438 | - (b'eert_toor', b'eert_toor'), |
439 | + (root_id, root_id), |
440 | ('old', 'old'), ('file', 'file'), |
441 | (False, False), False)], |
442 | list(transform.iter_changes())) |
443 | @@ -1223,7 +1223,7 @@ |
444 | transform.version_file(new, file_id=b'id-1') |
445 | transform.adjust_path('old', root, new) |
446 | self.assertEqual([(b'id-1', ('old', 'old'), True, (True, True), |
447 | - (b'eert_toor', b'eert_toor'), |
448 | + (root_id, root_id), |
449 | ('old', 'old'), ('file', 'file'), |
450 | (False, False), False)], |
451 | list(transform.iter_changes())) |
452 | @@ -1234,7 +1234,7 @@ |
453 | self.assertEqual([], list(transform.iter_changes())) |
454 | transform.set_executability(True, old) |
455 | self.assertEqual([(b'id-1', ('old', 'old'), False, (True, True), |
456 | - (b'eert_toor', b'eert_toor'), |
457 | + (root_id, root_id), |
458 | ('old', 'old'), ('file', 'file'), |
459 | (False, True), False)], |
460 | list(transform.iter_changes())) |
461 | @@ -1245,7 +1245,7 @@ |
462 | transform.adjust_path('new', root, old) |
463 | transform._new_parent = {} |
464 | self.assertEqual([(b'id-1', ('old', 'new'), False, (True, True), |
465 | - (b'eert_toor', b'eert_toor'), |
466 | + (root_id, root_id), |
467 | ('old', 'new'), ('file', 'file'), |
468 | (False, False), False)], |
469 | list(transform.iter_changes())) |
470 | @@ -1256,8 +1256,7 @@ |
471 | transform.adjust_path('new', subdir, old) |
472 | transform._new_name = {} |
473 | self.assertEqual([(b'id-1', ('old', 'subdir/old'), False, |
474 | - (True, True), (b'eert_toor', |
475 | - b'subdir-id'), ('old', 'old'), |
476 | + (True, True), (root_id, b'subdir-id'), ('old', 'old'), |
477 | ('file', 'file'), (False, False), False)], |
478 | list(transform.iter_changes())) |
479 | transform._new_path = {} |
480 | @@ -1266,7 +1265,7 @@ |
481 | transform.finalize() |
482 | |
483 | def test_iter_changes_modified_bleed(self): |
484 | - self.wt.set_root_id(b'eert_toor') |
485 | + root_id = self.wt.path2id('') |
486 | """Modified flag should not bleed from one change to another""" |
487 | # unfortunately, we have no guarantee that file1 (which is modified) |
488 | # will be applied before file2. And if it's applied after file2, it |
489 | @@ -1283,10 +1282,10 @@ |
490 | transform.trans_id_file_id(b'id-2')) |
491 | self.assertEqual( |
492 | [(b'id-1', (u'file1', u'file1'), True, (True, True), |
493 | - (b'eert_toor', b'eert_toor'), ('file1', u'file1'), |
494 | + (root_id, root_id), ('file1', u'file1'), |
495 | ('file', None), (False, False), False), |
496 | (b'id-2', (u'file2', u'file2'), False, (True, True), |
497 | - (b'eert_toor', b'eert_toor'), ('file2', u'file2'), |
498 | + (root_id, root_id), ('file2', u'file2'), |
499 | ('file', 'file'), (False, True), False)], |
500 | list(transform.iter_changes())) |
501 | finally: |
502 | @@ -1294,7 +1293,7 @@ |
503 | |
504 | def test_iter_changes_move_missing(self): |
505 | """Test moving ids with no files around""" |
506 | - self.wt.set_root_id(b'toor_eert') |
507 | + root_id = self.wt.path2id('') |
508 | # Need two steps because versioning a non-existant file is a conflict. |
509 | transform, root = self.transform() |
510 | transform.new_directory('floater', root, b'floater-id') |
511 | @@ -1308,7 +1307,7 @@ |
512 | transform.adjust_path('flitter', root, floater) |
513 | self.assertEqual([(b'floater-id', ('floater', 'flitter'), False, |
514 | (True, True), |
515 | - (b'toor_eert', b'toor_eert'), |
516 | + (root_id, root_id), |
517 | ('floater', 'flitter'), |
518 | (None, None), (False, False), False)], |
519 | list(transform.iter_changes())) |
520 | @@ -1317,7 +1316,6 @@ |
521 | |
522 | def test_iter_changes_pointless(self): |
523 | """Ensure that no-ops are not treated as modifications""" |
524 | - self.wt.set_root_id(b'eert_toor') |
525 | transform, root = self.transform() |
526 | transform.new_file('old', root, [b'blah'], b'id-1') |
527 | transform.new_directory('subdir', root, b'subdir-id') |
528 | |
529 | === modified file 'breezy/transform.py' |
530 | --- breezy/transform.py 2020-07-06 01:43:04 +0000 |
531 | +++ breezy/transform.py 2020-07-07 22:38:02 +0000 |
532 | @@ -26,6 +26,7 @@ |
533 | controldir, |
534 | errors, |
535 | lazy_import, |
536 | + lock, |
537 | osutils, |
538 | registry, |
539 | trace, |
540 | @@ -1350,3 +1351,184 @@ |
541 | tt.delete_contents(trans_id) |
542 | tt.create_hardlink(source_tree.abspath(change.path[0]), trans_id) |
543 | tt.apply() |
544 | + |
545 | + |
546 | +class PreviewTree(object): |
547 | + """Preview tree.""" |
548 | + |
549 | + def __init__(self, transform): |
550 | + self._transform = transform |
551 | + self._parent_ids = [] |
552 | + self.__by_parent = None |
553 | + self._path2trans_id_cache = {} |
554 | + self._all_children_cache = {} |
555 | + self._final_name_cache = {} |
556 | + |
557 | + @property |
558 | + def _by_parent(self): |
559 | + if self.__by_parent is None: |
560 | + self.__by_parent = self._transform.by_parent() |
561 | + return self.__by_parent |
562 | + |
563 | + def get_parent_ids(self): |
564 | + return self._parent_ids |
565 | + |
566 | + def set_parent_ids(self, parent_ids): |
567 | + self._parent_ids = parent_ids |
568 | + |
569 | + def get_revision_tree(self, revision_id): |
570 | + return self._transform._tree.get_revision_tree(revision_id) |
571 | + |
572 | + def is_locked(self): |
573 | + return False |
574 | + |
575 | + def lock_read(self): |
576 | + # Perhaps in theory, this should lock the TreeTransform? |
577 | + return lock.LogicalLockResult(self.unlock) |
578 | + |
579 | + def unlock(self): |
580 | + pass |
581 | + |
582 | + def _path2trans_id(self, path): |
583 | + # We must not use None here, because that is a valid value to store. |
584 | + trans_id = self._path2trans_id_cache.get(path, object) |
585 | + if trans_id is not object: |
586 | + return trans_id |
587 | + segments = osutils.splitpath(path) |
588 | + cur_parent = self._transform.root |
589 | + for cur_segment in segments: |
590 | + for child in self._all_children(cur_parent): |
591 | + final_name = self._final_name_cache.get(child) |
592 | + if final_name is None: |
593 | + final_name = self._transform.final_name(child) |
594 | + self._final_name_cache[child] = final_name |
595 | + if final_name == cur_segment: |
596 | + cur_parent = child |
597 | + break |
598 | + else: |
599 | + self._path2trans_id_cache[path] = None |
600 | + return None |
601 | + self._path2trans_id_cache[path] = cur_parent |
602 | + return cur_parent |
603 | + |
604 | + def _all_children(self, trans_id): |
605 | + children = self._all_children_cache.get(trans_id) |
606 | + if children is not None: |
607 | + return children |
608 | + children = set(self._transform.iter_tree_children(trans_id)) |
609 | + # children in the _new_parent set are provided by _by_parent. |
610 | + children.difference_update(self._transform._new_parent) |
611 | + children.update(self._by_parent.get(trans_id, [])) |
612 | + self._all_children_cache[trans_id] = children |
613 | + return children |
614 | + |
615 | + def get_file_with_stat(self, path): |
616 | + return self.get_file(path), None |
617 | + |
618 | + def is_executable(self, path): |
619 | + trans_id = self._path2trans_id(path) |
620 | + if trans_id is None: |
621 | + return False |
622 | + try: |
623 | + return self._transform._new_executability[trans_id] |
624 | + except KeyError: |
625 | + try: |
626 | + return self._transform._tree.is_executable(path) |
627 | + except OSError as e: |
628 | + if e.errno == errno.ENOENT: |
629 | + return False |
630 | + raise |
631 | + except errors.NoSuchFile: |
632 | + return False |
633 | + |
634 | + def has_filename(self, path): |
635 | + trans_id = self._path2trans_id(path) |
636 | + if trans_id in self._transform._new_contents: |
637 | + return True |
638 | + elif trans_id in self._transform._removed_contents: |
639 | + return False |
640 | + else: |
641 | + return self._transform._tree.has_filename(path) |
642 | + |
643 | + def get_file_sha1(self, path, stat_value=None): |
644 | + trans_id = self._path2trans_id(path) |
645 | + if trans_id is None: |
646 | + raise errors.NoSuchFile(path) |
647 | + kind = self._transform._new_contents.get(trans_id) |
648 | + if kind is None: |
649 | + return self._transform._tree.get_file_sha1(path) |
650 | + if kind == 'file': |
651 | + with self.get_file(path) as fileobj: |
652 | + return osutils.sha_file(fileobj) |
653 | + |
654 | + def get_file_verifier(self, path, stat_value=None): |
655 | + trans_id = self._path2trans_id(path) |
656 | + if trans_id is None: |
657 | + raise errors.NoSuchFile(path) |
658 | + kind = self._transform._new_contents.get(trans_id) |
659 | + if kind is None: |
660 | + return self._transform._tree.get_file_verifier(path) |
661 | + if kind == 'file': |
662 | + with self.get_file(path) as fileobj: |
663 | + return ("SHA1", osutils.sha_file(fileobj)) |
664 | + |
665 | + def kind(self, path): |
666 | + trans_id = self._path2trans_id(path) |
667 | + if trans_id is None: |
668 | + raise errors.NoSuchFile(path) |
669 | + return self._transform.final_kind(trans_id) |
670 | + |
671 | + def stored_kind(self, path): |
672 | + trans_id = self._path2trans_id(path) |
673 | + if trans_id is None: |
674 | + raise errors.NoSuchFile(path) |
675 | + try: |
676 | + return self._transform._new_contents[trans_id] |
677 | + except KeyError: |
678 | + return self._transform._tree.stored_kind(path) |
679 | + |
680 | + def _get_repository(self): |
681 | + repo = getattr(self._transform._tree, '_repository', None) |
682 | + if repo is None: |
683 | + repo = self._transform._tree.branch.repository |
684 | + return repo |
685 | + |
686 | + def _iter_parent_trees(self): |
687 | + for revision_id in self.get_parent_ids(): |
688 | + try: |
689 | + yield self.revision_tree(revision_id) |
690 | + except errors.NoSuchRevisionInTree: |
691 | + yield self._get_repository().revision_tree(revision_id) |
692 | + |
693 | + def get_file_size(self, path): |
694 | + """See Tree.get_file_size""" |
695 | + trans_id = self._path2trans_id(path) |
696 | + if trans_id is None: |
697 | + raise errors.NoSuchFile(path) |
698 | + kind = self._transform.final_kind(trans_id) |
699 | + if kind != 'file': |
700 | + return None |
701 | + if trans_id in self._transform._new_contents: |
702 | + return self._stat_limbo_file(trans_id).st_size |
703 | + if self.kind(path) == 'file': |
704 | + return self._transform._tree.get_file_size(path) |
705 | + else: |
706 | + return None |
707 | + |
708 | + def get_reference_revision(self, path): |
709 | + trans_id = self._path2trans_id(path) |
710 | + if trans_id is None: |
711 | + raise errors.NoSuchFile(path) |
712 | + reference_revision = self._transform._new_reference_revision.get(trans_id) |
713 | + if reference_revision is None: |
714 | + return self._transform._tree.get_reference_revision(path) |
715 | + return reference_revision |
716 | + |
717 | + def tree_kind(self, trans_id): |
718 | + path = self._tree_id_paths.get(trans_id) |
719 | + if path is None: |
720 | + return None |
721 | + kind = self._tree.path_content_summary(path)[0] |
722 | + if kind == 'missing': |
723 | + kind = None |
724 | + return kind |