Merge lp:~jelmer/brz/extract-paths2ids into lp:brz

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/extract-paths2ids
Merge into: lp:brz
Prerequisite: lp:~jelmer/brz/find-previous-paths
Diff against target: 1340 lines (+329/-267)
20 files modified
breezy/builtins.py (+13/-14)
breezy/bzr/inventory.py (+2/-2)
breezy/bzr/inventorytree.py (+138/-12)
breezy/bzr/workingtree.py (+4/-7)
breezy/filter_tree.py (+2/-2)
breezy/merge.py (+15/-31)
breezy/rename_map.py (+9/-1)
breezy/tests/per_intertree/test_compare.py (+3/-1)
breezy/tests/per_tree/test_inv.py (+10/-4)
breezy/tests/per_tree/test_test_trees.py (+2/-8)
breezy/tests/per_workingtree/test_inv.py (+1/-1)
breezy/tests/per_workingtree/test_nested_specifics.py (+1/-1)
breezy/tests/per_workingtree/test_paths2ids.py (+37/-0)
breezy/tests/test_merge.py (+5/-9)
breezy/tests/test_merge_core.py (+5/-5)
breezy/tests/test_shelf.py (+2/-2)
breezy/tests/test_transform.py (+5/-4)
breezy/tests/test_workingtree.py (+2/-1)
breezy/transform.py (+22/-22)
breezy/tree.py (+51/-140)
To merge this branch: bzr merge lp:~jelmer/brz/extract-paths2ids
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+341923@code.launchpad.net

Commit message

Add a new Tree.find_related_paths_across_trees() method and pass files rather than file ids in various places.

Description of the change

Add a new Tree.find_related_paths_across_trees() method.

This finds a set of paths in a specific tree that are related to a set of specified paths in trees to look up in.

This allows further eliminating the use of file ids in public APIs:

* Tree.paths2ids can now be InventoryTree-specific.
* Merge now only takes a list of interesting files rather than a list of interesting file ids or interesting files.
* Tree.iter_entries_by_dir() now takes a list of specific_files

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Thanks, all looks sensible.

review: Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :

Running landing tests failed
https://ci.breezy-vcs.org/job/brz-dev/31/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/builtins.py'
2--- breezy/builtins.py 2018-03-24 00:39:56 +0000
3+++ breezy/builtins.py 2018-03-24 01:23:20 +0000
4@@ -53,6 +53,7 @@
5 symbol_versioning,
6 timestamp,
7 transport,
8+ tree as _mod_tree,
9 ui,
10 urlutils,
11 views,
12@@ -960,11 +961,11 @@
13
14 self.add_cleanup(tree.lock_read().unlock)
15 if file_list is not None:
16- file_ids = tree.paths2ids(file_list, trees=extra_trees,
17- require_versioned=True)
18+ paths = tree.find_related_paths_across_trees(
19+ file_list, extra_trees, require_versioned=True)
20 # find_ids_across_trees may include some paths that don't
21 # exist in 'tree'.
22- entries = tree.iter_entries_by_dir(specific_file_ids=file_ids)
23+ entries = tree.iter_entries_by_dir(specific_files=paths)
24 else:
25 entries = tree.iter_entries_by_dir()
26
27@@ -4775,29 +4776,27 @@
28 " merges. Not cherrypicking or"
29 " multi-merges."))
30 repository = tree.branch.repository
31- interesting_ids = None
32+ interesting_files = None
33 new_conflicts = []
34 conflicts = tree.conflicts()
35 if file_list is not None:
36- interesting_ids = set()
37+ interesting_files = set()
38 for filename in file_list:
39- file_id = tree.path2id(filename)
40- if file_id is None:
41+ if not tree.is_versioned(filename):
42 raise errors.NotVersionedError(filename)
43- interesting_ids.add(file_id)
44- if tree.kind(filename, file_id) != "directory":
45+ interesting_files.add(filename)
46+ if tree.kind(filename) != "directory":
47 continue
48
49- # FIXME: Support nested trees
50- for name, ie in tree.root_inventory.iter_entries(file_id):
51- interesting_ids.add(ie.file_id)
52+ for path, ie in tree.iter_entries_by_dir(specific_files=[filename]):
53+ interesting_files.add(path)
54 new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
55 else:
56 # Remerge only supports resolving contents conflicts
57 allowed_conflicts = ('text conflict', 'contents conflict')
58 restore_files = [c.path for c in conflicts
59 if c.typestring in allowed_conflicts]
60- _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
61+ _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_files)
62 tree.set_conflicts(ConflictList(new_conflicts))
63 if file_list is not None:
64 restore_files = file_list
65@@ -4814,7 +4813,7 @@
66 tree.set_parent_ids(parents[:1])
67 try:
68 merger = _mod_merge.Merger.from_revision_ids(tree, parents[1])
69- merger.interesting_ids = interesting_ids
70+ merger.interesting_files = interesting_files
71 merger.merge_type = merge_type
72 merger.show_base = show_base
73 merger.reprocess = reprocess
74
75=== modified file 'breezy/bzr/inventory.py'
76--- breezy/bzr/inventory.py 2018-03-10 15:17:30 +0000
77+++ breezy/bzr/inventory.py 2018-03-24 01:23:20 +0000
78@@ -720,12 +720,12 @@
79
80 def _preload_cache(self):
81 """Populate any caches, we are about to access all items.
82-
83+
84 The default implementation does nothing, because CommonInventory doesn't
85 have a cache.
86 """
87 pass
88-
89+
90 def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None,
91 yield_parents=False):
92 """Iterate over the entries in a directory first order.
93
94=== modified file 'breezy/bzr/inventorytree.py'
95--- breezy/bzr/inventorytree.py 2018-03-24 00:16:27 +0000
96+++ breezy/bzr/inventorytree.py 2018-03-24 01:23:20 +0000
97@@ -170,6 +170,53 @@
98 file_id = file_id[0]
99 return self.root_inventory, file_id
100
101+ def find_related_paths_across_trees(self, paths, trees=[],
102+ require_versioned=True):
103+ """Find related paths in tree corresponding to specified filenames in any
104+ of `lookup_trees`.
105+
106+ All matches in all trees will be used, and all children of matched
107+ directories will be used.
108+
109+ :param paths: The filenames to find related paths for (if None, returns
110+ None)
111+ :param trees: The trees to find file_ids within
112+ :param require_versioned: if true, all specified filenames must occur in
113+ at least one tree.
114+ :return: a set of paths for the specified filenames and their children
115+ in `tree`
116+ """
117+ if paths is None:
118+ return None;
119+ file_ids = self.paths2ids(
120+ paths, trees, require_versioned=require_versioned)
121+ ret = set()
122+ for file_id in file_ids:
123+ try:
124+ ret.add(self.id2path(file_id))
125+ except errors.NoSuchId:
126+ pass
127+ return ret
128+
129+ def paths2ids(self, paths, trees=[], require_versioned=True):
130+ """Return all the ids that can be reached by walking from paths.
131+
132+ Each path is looked up in this tree and any extras provided in
133+ trees, and this is repeated recursively: the children in an extra tree
134+ of a directory that has been renamed under a provided path in this tree
135+ are all returned, even if none exist under a provided path in this
136+ tree, and vice versa.
137+
138+ :param paths: An iterable of paths to start converting to ids from.
139+ Alternatively, if paths is None, no ids should be calculated and None
140+ will be returned. This is offered to make calling the api unconditional
141+ for code that *might* take a list of files.
142+ :param trees: Additional trees to consider.
143+ :param require_versioned: If False, do not raise NotVersionedError if
144+ an element of paths is not versioned in this tree and all of trees.
145+ """
146+ return find_ids_across_trees(paths, [self] + list(trees), require_versioned)
147+
148 def path2id(self, path):
149 """Return the id for path in this tree."""
150 with self.lock_read():
151@@ -222,7 +269,7 @@
152 # are not versioned.
153 return set((p for p in paths if self.path2id(p) is None))
154
155- def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
156+ def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
157 """Walk the tree in 'by_dir' order.
158
159 This will yield each entry in the tree as a (path, entry) tuple.
160@@ -231,20 +278,20 @@
161 See Tree.iter_entries_by_dir for details.
162
163 :param yield_parents: If True, yield the parents from the root leading
164- down to specific_file_ids that have been requested. This has no
165- impact if specific_file_ids is None.
166+ down to specific_files that have been requested. This has no
167+ impact if specific_files is None.
168 """
169 with self.lock_read():
170- if specific_file_ids is None:
171- inventory_file_ids = None
172- else:
173+ if specific_files is not None:
174 inventory_file_ids = []
175- for tree_file_id in specific_file_ids:
176- inventory, inv_file_id = self._unpack_file_id(tree_file_id)
177+ for path in specific_files:
178+ inventory, inv_file_id = self._path2inv_file_id(path)
179 if not inventory is self.root_inventory: # for now
180 raise AssertionError("%r != %r" % (
181 inventory, self.root_inventory))
182 inventory_file_ids.append(inv_file_id)
183+ else:
184+ inventory_file_ids = None
185 # FIXME: Handle nested trees
186 return self.root_inventory.iter_entries_by_dir(
187 specific_file_ids=inventory_file_ids, yield_parents=yield_parents)
188@@ -319,6 +366,84 @@
189 return last_revision
190
191
192+def find_ids_across_trees(filenames, trees, require_versioned=True):
193+ """Find the ids corresponding to specified filenames.
194+
195+ All matches in all trees will be used, and all children of matched
196+ directories will be used.
197+
198+ :param filenames: The filenames to find file_ids for (if None, returns
199+ None)
200+ :param trees: The trees to find file_ids within
201+ :param require_versioned: if true, all specified filenames must occur in
202+ at least one tree.
203+ :return: a set of file ids for the specified filenames and their children.
204+ """
205+ if not filenames:
206+ return None
207+ specified_path_ids = _find_ids_across_trees(filenames, trees,
208+ require_versioned)
209+ return _find_children_across_trees(specified_path_ids, trees)
210+
211+
212+def _find_ids_across_trees(filenames, trees, require_versioned):
213+ """Find the ids corresponding to specified filenames.
214+
215+ All matches in all trees will be used, but subdirectories are not scanned.
216+
217+ :param filenames: The filenames to find file_ids for
218+ :param trees: The trees to find file_ids within
219+ :param require_versioned: if true, all specified filenames must occur in
220+ at least one tree.
221+ :return: a set of file ids for the specified filenames
222+ """
223+ not_versioned = []
224+ interesting_ids = set()
225+ for tree_path in filenames:
226+ not_found = True
227+ for tree in trees:
228+ file_id = tree.path2id(tree_path)
229+ if file_id is not None:
230+ interesting_ids.add(file_id)
231+ not_found = False
232+ if not_found:
233+ not_versioned.append(tree_path)
234+ if len(not_versioned) > 0 and require_versioned:
235+ raise errors.PathsNotVersionedError(not_versioned)
236+ return interesting_ids
237+
238+
239+def _find_children_across_trees(specified_ids, trees):
240+ """Return a set including specified ids and their children.
241+
242+ All matches in all trees will be used.
243+
244+ :param trees: The trees to find file_ids within
245+ :return: a set containing all specified ids and their children
246+ """
247+ interesting_ids = set(specified_ids)
248+ pending = interesting_ids
249+ # now handle children of interesting ids
250+ # we loop so that we handle all children of each id in both trees
251+ while len(pending) > 0:
252+ new_pending = set()
253+ for file_id in pending:
254+ for tree in trees:
255+ try:
256+ path = tree.id2path(file_id)
257+ except errors.NoSuchId:
258+ continue
259+ try:
260+ for child in tree.iter_child_entries(path, file_id):
261+ if child.file_id not in interesting_ids:
262+ new_pending.add(child.file_id)
263+ except errors.NotADirectory:
264+ pass
265+ interesting_ids.update(new_pending)
266+ pending = new_pending
267+ return interesting_ids
268+
269+
270 class MutableInventoryTree(MutableTree, InventoryTree):
271
272 def apply_inventory_delta(self, changes):
273@@ -442,10 +567,11 @@
274 return entry[3]
275 # Find a 'best fit' match if the filesystem is case-insensitive
276 inv_path = self.tree._fix_case_of_inventory_path(inv_path)
277- file_id = self.tree.path2id(inv_path)
278- if file_id is not None:
279- return self.tree.iter_entries_by_dir([file_id]).next()[1]
280- return None
281+ try:
282+ return self.tree.iter_entries_by_dir(
283+ specific_files=[inv_path]).next()[1]
284+ except StopIteration:
285+ return None
286
287 def _convert_to_directory(self, this_ie, inv_path):
288 """Convert an entry to a directory.
289
290=== modified file 'breezy/bzr/workingtree.py'
291--- breezy/bzr/workingtree.py 2018-03-04 19:11:21 +0000
292+++ breezy/bzr/workingtree.py 2018-03-24 01:23:20 +0000
293@@ -652,10 +652,7 @@
294 file_ids are in a WorkingTree if they are in the working inventory
295 and the working file exists.
296 """
297- ret = set()
298- for path, ie in self.iter_entries_by_dir():
299- ret.add(ie.file_id)
300- return ret
301+ return {ie.file_id for path, ie in self.iter_entries_by_dir()}
302
303 def all_versioned_paths(self):
304 return {path for path, ie in self.iter_entries_by_dir()}
305@@ -1699,13 +1696,13 @@
306 blocked_parent_ids.add(ie.file_id)
307 yield path, ie
308
309- def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
310+ def iter_entries_by_dir(self, specific_files=None,
311+ yield_parents=False):
312 """See Tree.iter_entries_by_dir()"""
313 # The only trick here is that if we supports_tree_reference then we
314 # need to detect if a directory becomes a tree-reference.
315 iterator = super(WorkingTree, self).iter_entries_by_dir(
316- specific_file_ids=specific_file_ids,
317- yield_parents=yield_parents)
318+ specific_files=specific_files, yield_parents=yield_parents)
319 if not self.supports_tree_reference():
320 return iterator
321 else:
322
323=== modified file 'breezy/filter_tree.py'
324--- breezy/filter_tree.py 2018-03-23 23:32:28 +0000
325+++ breezy/filter_tree.py 2018-03-24 01:23:20 +0000
326@@ -58,14 +58,14 @@
327 def is_executable(self, path, file_id=None):
328 return self.backing_tree.is_executable(path, file_id)
329
330- def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=None):
331+ def iter_entries_by_dir(self, specific_files=None, yield_parents=None):
332 # NB: This simply returns the parent tree's entries; the length may be
333 # wrong but it can't easily be calculated without filtering the whole
334 # text. Currently all callers cope with this; perhaps they should be
335 # updated to a narrower interface that only provides things guaranteed
336 # cheaply available across all trees. -- mbp 20110705
337 return self.backing_tree.iter_entries_by_dir(
338- specific_file_ids=specific_file_ids,
339+ specific_files=specific_files,
340 yield_parents=yield_parents)
341
342 def lock_read(self):
343
344=== modified file 'breezy/merge.py'
345--- breezy/merge.py 2018-03-24 00:39:56 +0000
346+++ breezy/merge.py 2018-03-24 01:23:20 +0000
347@@ -56,12 +56,13 @@
348 # TODO: Report back as changes are merged in
349
350
351-def transform_tree(from_tree, to_tree, interesting_ids=None):
352+def transform_tree(from_tree, to_tree, interesting_files=None):
353 from_tree.lock_tree_write()
354 operation = cleanup.OperationWithCleanups(merge_inner)
355 operation.add_cleanup(from_tree.unlock)
356 operation.run_simple(from_tree.branch, to_tree, from_tree,
357- ignore_zero=True, interesting_ids=interesting_ids, this_tree=from_tree)
358+ ignore_zero=True, this_tree=from_tree,
359+ interesting_files=interesting_files)
360
361
362 class MergeHooks(hooks.Hooks):
363@@ -295,7 +296,6 @@
364 self.base_tree = base_tree
365 self.ignore_zero = False
366 self.backup_files = False
367- self.interesting_ids = None
368 self.interesting_files = None
369 self.show_base = False
370 self.reprocess = False
371@@ -610,7 +610,6 @@
372 def make_merger(self):
373 kwargs = {'working_tree': self.this_tree, 'this_tree': self.this_tree,
374 'other_tree': self.other_tree,
375- 'interesting_ids': self.interesting_ids,
376 'interesting_files': self.interesting_files,
377 'this_branch': self.this_branch,
378 'other_branch': self.other_branch,
379@@ -722,7 +721,7 @@
380 requires_file_merge_plan = False
381
382 def __init__(self, working_tree, this_tree, base_tree, other_tree,
383- interesting_ids=None, reprocess=False, show_base=False,
384+ reprocess=False, show_base=False,
385 change_reporter=None, interesting_files=None, do_merge=True,
386 cherrypick=False, lca_trees=None, this_branch=None,
387 other_branch=None):
388@@ -735,30 +734,22 @@
389 :param this_branch: The branch associated with this_tree. Defaults to
390 this_tree.branch if not supplied.
391 :param other_branch: The branch associated with other_tree, if any.
392- :param interesting_ids: The file_ids of files that should be
393- participate in the merge. May not be combined with
394- interesting_files.
395 :param: reprocess If True, perform conflict-reduction processing.
396 :param show_base: If True, show the base revision in text conflicts.
397 (incompatible with reprocess)
398 :param change_reporter: An object that should report changes made
399 :param interesting_files: The tree-relative paths of files that should
400 participate in the merge. If these paths refer to directories,
401- the contents of those directories will also be included. May not
402- be combined with interesting_ids. If neither interesting_files nor
403- interesting_ids is specified, all files may participate in the
404+ the contents of those directories will also be included. If not
405+ specified, all files may participate in the
406 merge.
407 :param lca_trees: Can be set to a dictionary of {revision_id:rev_tree}
408 if the ancestry was found to include a criss-cross merge.
409 Otherwise should be None.
410 """
411 object.__init__(self)
412- if interesting_files is not None and interesting_ids is not None:
413- raise ValueError(
414- 'specify either interesting_ids or interesting_files')
415 if this_branch is None:
416 this_branch = this_tree.branch
417- self.interesting_ids = interesting_ids
418 self.interesting_files = interesting_files
419 self.working_tree = working_tree
420 self.this_tree = this_tree
421@@ -874,14 +865,13 @@
422 iterator = self.other_tree.iter_changes(self.base_tree,
423 specific_files=self.interesting_files,
424 extra_trees=[self.this_tree])
425+ this_interesting_files = self.this_tree.find_related_paths_across_trees(
426+ self.interesting_files, trees=[self.other_tree])
427 this_entries = dict((e.file_id, e) for p, e in
428 self.this_tree.iter_entries_by_dir(
429- self.interesting_ids))
430+ specific_files=this_interesting_files))
431 for (file_id, paths, changed, versioned, parents, names, kind,
432 executable) in iterator:
433- if (self.interesting_ids is not None and
434- file_id not in self.interesting_ids):
435- continue
436 entry = this_entries.get(file_id)
437 if entry is not None:
438 this_name = entry.name
439@@ -919,10 +909,10 @@
440 lookup_trees = [self.this_tree, self.base_tree]
441 lookup_trees.extend(self._lca_trees)
442 # I think we should include the lca trees as well
443- interesting_ids = self.other_tree.paths2ids(self.interesting_files,
444- lookup_trees)
445+ interesting_files = self.other_tree.find_related_paths_across_trees(
446+ self.interesting_files, lookup_trees)
447 else:
448- interesting_ids = self.interesting_ids
449+ interesting_files = None
450 result = []
451 walker = _mod_tree.MultiWalker(self.other_tree, self._lca_trees)
452
453@@ -932,7 +922,7 @@
454 # Is this modified at all from any of the other trees?
455 if other_ie is None:
456 other_ie = _none_entry
457- if interesting_ids is not None and file_id not in interesting_ids:
458+ if interesting_files is not None and path not in interesting_files:
459 continue
460
461 # If other_revision is found in any of the lcas, that means this
462@@ -1815,7 +1805,7 @@
463 self.merge_type = Merge3Merger
464 self.show_base = False
465 self.reprocess = False
466- self.interesting_ids = None
467+ self.interesting_files = None
468 self.merge_type = _MergeTypeParameterizer(MergeIntoMergeType,
469 target_subdir=self._target_subdir,
470 source_subpath=self._source_subpath)
471@@ -1926,7 +1916,6 @@
472 def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
473 backup_files=False,
474 merge_type=Merge3Merger,
475- interesting_ids=None,
476 show_base=False,
477 reprocess=False,
478 other_rev_id=None,
479@@ -1947,13 +1936,8 @@
480 change_reporter=change_reporter)
481 merger.backup_files = backup_files
482 merger.merge_type = merge_type
483- merger.interesting_ids = interesting_ids
484 merger.ignore_zero = ignore_zero
485- if interesting_files:
486- if interesting_ids:
487- raise ValueError('Only supply interesting_ids'
488- ' or interesting_files')
489- merger.interesting_files = interesting_files
490+ merger.interesting_files = interesting_files
491 merger.show_base = show_base
492 merger.reprocess = reprocess
493 merger.other_rev_id = other_rev_id
494
495=== modified file 'breezy/rename_map.py'
496--- breezy/rename_map.py 2018-03-12 01:24:43 +0000
497+++ breezy/rename_map.py 2018-03-24 01:23:20 +0000
498@@ -26,6 +26,7 @@
499 from .sixish import (
500 BytesIO,
501 viewitems,
502+ viewvalues,
503 )
504 from .ui import ui_factory
505
506@@ -237,7 +238,14 @@
507 def _make_inventory_delta(self, matches):
508 delta = []
509 file_id_matches = dict((f, p) for p, f in viewitems(matches))
510- for old_path, entry in self.tree.iter_entries_by_dir(file_id_matches):
511+ file_id_query = []
512+ for f in viewvalues(matches):
513+ try:
514+ file_id_query.append(self.tree.id2path(f))
515+ except errors.NoSuchId:
516+ pass
517+ for old_path, entry in self.tree.iter_entries_by_dir(
518+ specific_files=file_id_query):
519 new_path = file_id_matches[entry.file_id]
520 parent_path, new_name = osutils.split(new_path)
521 parent_id = matches.get(parent_path)
522
523=== modified file 'breezy/tests/per_intertree/test_compare.py'
524--- breezy/tests/per_intertree/test_compare.py 2017-11-21 20:37:41 +0000
525+++ breezy/tests/per_intertree/test_compare.py 2018-03-24 01:23:20 +0000
526@@ -557,7 +557,9 @@
527
528 @staticmethod
529 def get_path_entry(tree, file_id):
530- iterator = tree.iter_entries_by_dir(specific_file_ids=[file_id])
531+ with tree.lock_read():
532+ path = tree.id2path(file_id)
533+ iterator = tree.iter_entries_by_dir(specific_files=[path])
534 try:
535 return next(iterator)
536 except StopIteration:
537
538=== modified file 'breezy/tests/per_tree/test_inv.py'
539--- breezy/tests/per_tree/test_inv.py 2017-08-01 01:44:41 +0000
540+++ breezy/tests/per_tree/test_inv.py 2018-03-24 01:23:20 +0000
541@@ -33,8 +33,8 @@
542 )
543
544
545-def get_entry(tree, file_id):
546- return tree.iter_entries_by_dir([file_id]).next()[1]
547+def get_entry(tree, path):
548+ return tree.iter_entries_by_dir(specific_files=[path]).next()[1]
549
550
551 class TestInventoryWithSymlinks(per_tree.TestCaseWithTree):
552@@ -52,7 +52,7 @@
553 raise TestSkipped(
554 'symlinks not accurately represented in working trees and'
555 ' preview trees')
556- entry = get_entry(self.tree, self.tree.path2id('symlink'))
557+ entry = get_entry(self.tree, 'symlink')
558 self.assertEqual(entry.symlink_target, 'link-target')
559
560 def test_symlink_target_tree(self):
561@@ -64,7 +64,7 @@
562 self.assertIs(None, self.tree.get_file_size('symlink'))
563
564 def test_symlink(self):
565- entry = get_entry(self.tree, self.tree.path2id('symlink'))
566+ entry = get_entry(self.tree, 'symlink')
567 self.assertEqual(entry.kind, 'symlink')
568 self.assertEqual(None, entry.text_size)
569
570@@ -76,6 +76,9 @@
571 self.build_tree(['tree/dir/', 'tree/dir/file'])
572 work_tree.add(['dir', 'dir/file'])
573 tree = self._convert_tree(work_tree)
574+ if not isinstance(tree, InventoryTree):
575+ raise tests.TestNotApplicable(
576+ "test not applicable on non-inventory tests")
577 tree.lock_read()
578 self.addCleanup(tree.unlock)
579 self.assertEqual({tree.path2id('dir'), tree.path2id('dir/file')},
580@@ -88,6 +91,9 @@
581 work_tree.commit('commit old state')
582 work_tree.remove('file')
583 tree = self._convert_tree(work_tree)
584+ if not isinstance(tree, InventoryTree):
585+ raise tests.TestNotApplicable(
586+ "test not applicable on non-inventory tests")
587 tree.lock_read()
588 self.addCleanup(tree.unlock)
589 self.assertEqual(set([]), tree.paths2ids(['file'],
590
591=== modified file 'breezy/tests/per_tree/test_test_trees.py'
592--- breezy/tests/per_tree/test_test_trees.py 2018-02-26 13:36:15 +0000
593+++ breezy/tests/per_tree/test_test_trees.py 2018-03-24 01:23:20 +0000
594@@ -228,11 +228,8 @@
595 (u'ba\N{Euro Sign}r/ba\N{Euro Sign}z',
596 baz_id, bar_id, revision_id),
597 ]
598- tree.lock_read()
599- try:
600+ with tree.lock_read():
601 path_entries = list(tree.iter_entries_by_dir())
602- finally:
603- tree.unlock()
604
605 for expected, (path, ie) in zip(path_and_ids, path_entries):
606 self.assertEqual(expected[0], path) # Paths should match
607@@ -276,11 +273,8 @@
608 (u'ba\N{Euro Sign}r/qu\N{Euro Sign}x',
609 qux_id, bar_id, revision_id_2),
610 ]
611- tree.lock_read()
612- try:
613+ with tree.lock_read():
614 path_entries = list(tree.iter_entries_by_dir())
615- finally:
616- tree.unlock()
617
618 for (epath, efid, eparent, erev), (path, ie) in zip(path_and_ids,
619 path_entries):
620
621=== modified file 'breezy/tests/per_workingtree/test_inv.py'
622--- breezy/tests/per_workingtree/test_inv.py 2018-02-15 19:38:33 +0000
623+++ breezy/tests/per_workingtree/test_inv.py 2018-03-24 01:23:20 +0000
624@@ -178,6 +178,6 @@
625 # wt.current_dirstate()'s idea about what files are where.
626 ie = base.inventory['subdir-id']
627 self.assertEqual('directory', ie.kind)
628- path, ie = next(base.iter_entries_by_dir(['subdir-id']))
629+ path, ie = next(base.iter_entries_by_dir(specific_files=['subdir']))
630 self.assertEqual('subdir', path)
631 self.assertEqual('tree-reference', ie.kind)
632
633=== modified file 'breezy/tests/per_workingtree/test_nested_specifics.py'
634--- breezy/tests/per_workingtree/test_nested_specifics.py 2018-02-16 19:38:39 +0000
635+++ breezy/tests/per_workingtree/test_nested_specifics.py 2018-03-24 01:23:20 +0000
636@@ -79,5 +79,5 @@
637
638 def test_iter_entries_by_dir_autodetects_subtree(self):
639 tree = self.prepare_with_subtree()
640- path, ie = next(tree.iter_entries_by_dir(['subtree-id']))
641+ path, ie = next(tree.iter_entries_by_dir(specific_files=['subtree']))
642 self.assertEqual('tree-reference', ie.kind)
643
644=== modified file 'breezy/tests/per_workingtree/test_paths2ids.py'
645--- breezy/tests/per_workingtree/test_paths2ids.py 2018-02-03 13:39:29 +0000
646+++ breezy/tests/per_workingtree/test_paths2ids.py 2018-03-24 01:23:20 +0000
647@@ -22,6 +22,7 @@
648 """
649
650 from breezy import errors
651+from breezy.bzr.inventorytree import InventoryTree
652 from breezy.tests import (
653 features,
654 TestNotApplicable,
655@@ -62,16 +63,30 @@
656
657 def test_paths_none_result_none(self):
658 tree = self.make_branch_and_tree('tree')
659+ if not isinstance(tree, InventoryTree):
660+ raise TestNotApplicable(
661+ "test not applicable on non-inventory tests")
662+
663 tree.lock_read()
664 self.assertEqual(None, tree.paths2ids(None))
665 tree.unlock()
666
667 def test_find_single_root(self):
668 tree = self.make_branch_and_tree('tree')
669+ if not isinstance(tree, InventoryTree):
670+ raise TestNotApplicable(
671+ "test not applicable on non-inventory tests")
672+
673+
674 self.assertExpectedIds([tree.path2id('')], tree, [''])
675
676 def test_find_tree_and_clone_roots(self):
677 tree = self.make_branch_and_tree('tree')
678+ if not isinstance(tree, InventoryTree):
679+ raise TestNotApplicable(
680+ "test not applicable on non-inventory tests")
681+
682+
683 clone = tree.controldir.clone('clone').open_workingtree()
684 clone.lock_tree_write()
685 clone_root_id = 'new-id'
686@@ -136,6 +151,11 @@
687 new-child because its under dir in new.
688 """
689 tree = self.make_branch_and_tree('tree')
690+ if not isinstance(tree, InventoryTree):
691+ raise TestNotApplicable(
692+ "test not applicable on non-inventory tests")
693+
694+
695 self.build_tree(
696 ['tree/dir/', 'tree/dir/child-moves', 'tree/dir/child-stays',
697 'tree/dir/child-goes'])
698@@ -161,6 +181,11 @@
699
700 def test_unversioned_one_tree(self):
701 tree = self.make_branch_and_tree('tree')
702+ if not isinstance(tree, InventoryTree):
703+ raise TestNotApplicable(
704+ "test not applicable on non-inventory tests")
705+
706+
707 self.build_tree(['tree/unversioned'])
708 self.assertExpectedIds([], tree, ['unversioned'], require_versioned=False)
709 tree.lock_read()
710@@ -172,6 +197,10 @@
711 # should not raise an error: it must be unversioned in *all* trees to
712 # error.
713 tree = self.make_branch_and_tree('tree')
714+ if not isinstance(tree, InventoryTree):
715+ raise TestNotApplicable(
716+ "test not applicable on non-inventory tests")
717+
718 if not tree.supports_setting_file_ids():
719 raise TestNotApplicable('tree does not support setting file ids')
720 tree.commit('make basis')
721@@ -185,6 +214,10 @@
722 # should not raise an error: it must be unversioned in *all* trees to
723 # error.
724 tree = self.make_branch_and_tree('tree')
725+ if not isinstance(tree, InventoryTree):
726+ raise TestNotApplicable(
727+ "test not applicable on non-inventory tests")
728+
729 tree.commit('make basis')
730 basis = tree.basis_tree()
731 self.assertExpectedIds([], tree, ['unversioned'], [basis],
732@@ -201,6 +234,10 @@
733 def test_unversioned_non_ascii_one_tree(self):
734 self.requireFeature(features.UnicodeFilenameFeature)
735 tree = self.make_branch_and_tree('.')
736+ if not isinstance(tree, InventoryTree):
737+ raise TestNotApplicable(
738+ "test not applicable on non-inventory tests")
739+
740 self.build_tree([u"\xa7"])
741 self.assertExpectedIds([], tree, [u"\xa7"], require_versioned=False)
742 self.addCleanup(tree.lock_read().unlock)
743
744=== modified file 'breezy/tests/test_merge.py'
745--- breezy/tests/test_merge.py 2018-03-20 00:30:39 +0000
746+++ breezy/tests/test_merge.py 2018-03-24 01:23:20 +0000
747@@ -1265,8 +1265,7 @@
748 builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
749 return builder
750
751- def make_Merger(self, builder, other_revision_id,
752- interesting_files=None, interesting_ids=None):
753+ def make_Merger(self, builder, other_revision_id, interesting_files=None):
754 """Make a Merger object from a branch builder"""
755 mem_tree = memorytree.MemoryTree.create_on_branch(builder.get_branch())
756 mem_tree.lock_write()
757@@ -1274,8 +1273,6 @@
758 merger = _mod_merge.Merger.from_revision_ids(
759 mem_tree, other_revision_id)
760 merger.set_interesting_files(interesting_files)
761- # It seems there is no matching function for set_interesting_ids
762- merger.interesting_ids = interesting_ids
763 merger.merge_type = _mod_merge.Merge3Merger
764 return merger
765
766@@ -1400,10 +1397,9 @@
767 class TestMergerEntriesLCA(TestMergerBase):
768
769 def make_merge_obj(self, builder, other_revision_id,
770- interesting_files=None, interesting_ids=None):
771+ interesting_files=None):
772 merger = self.make_Merger(builder, other_revision_id,
773- interesting_files=interesting_files,
774- interesting_ids=interesting_ids)
775+ interesting_files=interesting_files)
776 return merger.make_merger()
777
778 def test_simple(self):
779@@ -2099,7 +2095,7 @@
780 ((False, [False, False]), False, False)),
781 ], entries)
782
783- def test_interesting_ids(self):
784+ def test_interesting_files(self):
785 # Two files modified, but we should filter one of them
786 builder = self.get_builder()
787 builder.build_snapshot(None,
788@@ -2114,7 +2110,7 @@
789 ('modify', ('b-id', 'new-content\n'))], revision_id='E-id')
790 builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
791 merge_obj = self.make_merge_obj(builder, 'E-id',
792- interesting_ids=['b-id'])
793+ interesting_files=['b'])
794 entries = list(merge_obj._entries_lca())
795 root_id = 'a-root-id'
796 self.assertEqual([('b-id', True,
797
798=== modified file 'breezy/tests/test_merge_core.py'
799--- breezy/tests/test_merge_core.py 2018-03-09 19:52:32 +0000
800+++ breezy/tests/test_merge_core.py 2018-03-24 01:23:20 +0000
801@@ -77,8 +77,8 @@
802 if option is True:
803 new_file(tt)
804
805- def merge(self, merge_type=Merge3Merger, interesting_ids=None, **kwargs):
806- merger = self.make_merger(merge_type, interesting_ids, **kwargs)
807+ def merge(self, merge_type=Merge3Merger, interesting_files=None, **kwargs):
808+ merger = self.make_merger(merge_type, interesting_files, **kwargs)
809 merger.do_merge()
810 return merger.cooked_conflicts
811
812@@ -86,7 +86,7 @@
813 merger = self.make_merger(Merge3Merger, None, this_revision_tree=True)
814 return merger.make_preview_transform()
815
816- def make_merger(self, merge_type, interesting_ids,
817+ def make_merger(self, merge_type, interesting_files,
818 this_revision_tree=False, **kwargs):
819 self.base_tt.apply()
820 self.base.commit('base commit')
821@@ -110,7 +110,7 @@
822 else:
823 this_tree = self.this
824 merger = merge_type(this_tree, self.this, self.base, other_basis,
825- interesting_ids=interesting_ids, do_merge=False,
826+ interesting_files=interesting_files, do_merge=False,
827 this_branch=self.this.branch, **kwargs)
828 return merger
829
830@@ -251,7 +251,7 @@
831 builder.change_contents("1", other="text4")
832 builder.add_file("2", builder.tree_root, "name2", "hello1", True)
833 builder.change_contents("2", other="text4")
834- builder.merge(interesting_ids=["1"])
835+ builder.merge(interesting_files=["name1"])
836 self.assertEqual(builder.this.get_file("name1").read(), "text4" )
837 self.assertEqual(builder.this.get_file("name2").read(), "hello1" )
838 builder.cleanup()
839
840=== modified file 'breezy/tests/test_shelf.py'
841--- breezy/tests/test_shelf.py 2017-12-12 01:19:18 +0000
842+++ breezy/tests/test_shelf.py 2018-03-24 01:23:20 +0000
843@@ -206,7 +206,7 @@
844
845 def check_shelve_creation(self, creator, tree):
846 self.assertRaises(StopIteration,
847- next, tree.iter_entries_by_dir(['foo-id']))
848+ next, tree.iter_entries_by_dir(specific_files=['foo']))
849 s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
850 self.assertEqual('foo-id',
851 creator.shelf_transform.final_file_id(s_trans_id))
852@@ -349,7 +349,7 @@
853 creator.shelve_creation('foo-id')
854 creator.transform()
855 self.assertRaises(StopIteration,
856- next, tree.iter_entries_by_dir(['foo-id']))
857+ next, tree.iter_entries_by_dir(specific_files=['foo']))
858 self.assertShelvedFileEqual('', creator, 'foo-id')
859 s_trans_id = creator.shelf_transform.trans_id_file_id('foo-id')
860 self.assertEqual('foo-id',
861
862=== modified file 'breezy/tests/test_transform.py'
863--- breezy/tests/test_transform.py 2018-03-24 00:39:56 +0000
864+++ breezy/tests/test_transform.py 2018-03-24 01:23:20 +0000
865@@ -2937,13 +2937,14 @@
866 self.assertFalse(preview_tree.is_versioned('old_name/child'))
867 self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
868
869- def assertMatchingIterEntries(self, tt, specific_file_ids=None):
870+ def assertMatchingIterEntries(self, tt, specific_files=None):
871 preview_tree = tt.get_preview_tree()
872 preview_result = list(preview_tree.iter_entries_by_dir(
873- specific_file_ids))
874+ specific_files=specific_files))
875 tree = tt._tree
876 tt.apply()
877- actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
878+ actual_result = list(tree.iter_entries_by_dir(
879+ specific_files=specific_files))
880 self.assertEqual(actual_result, preview_result)
881
882 def test_iter_entries_by_dir_new(self):
883@@ -2983,7 +2984,7 @@
884 self.build_tree(['tree/parent/', 'tree/parent/child'])
885 tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
886 tt = TreeTransform(tree)
887- self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
888+ self.assertMatchingIterEntries(tt, ['', 'parent/child'])
889
890 def test_symlink_content_summary(self):
891 self.requireFeature(SymlinkFeature)
892
893=== modified file 'breezy/tests/test_workingtree.py'
894--- breezy/tests/test_workingtree.py 2018-02-11 17:07:38 +0000
895+++ breezy/tests/test_workingtree.py 2018-03-24 01:23:20 +0000
896@@ -257,7 +257,8 @@
897 subtree = self.make_branch_and_tree('tree/a/b')
898 self.assertEqual([('tree-reference', 'b-id')],
899 [(ie.kind, ie.file_id)
900- for path, ie in tree.iter_entries_by_dir(['b-id'])])
901+ for path, ie in tree.iter_entries_by_dir(
902+ specific_files=['a/b'])])
903
904 def test_direct_subtree(self):
905 tree = self.make_simple_tree()
906
907=== modified file 'breezy/transform.py'
908--- breezy/transform.py 2018-03-24 00:39:56 +0000
909+++ breezy/transform.py 2018-03-24 01:23:20 +0000
910@@ -894,7 +894,7 @@
911 to_trans_ids[to_file_id] = trans_id
912 return from_trans_ids, to_trans_ids
913
914- def _from_file_data(self, from_trans_id, from_versioned, file_id):
915+ def _from_file_data(self, from_trans_id, from_versioned, from_path):
916 """Get data about a file in the from (tree) state
917
918 Return a (name, parent, kind, executable) tuple
919@@ -902,7 +902,8 @@
920 from_path = self._tree_id_paths.get(from_trans_id)
921 if from_versioned:
922 # get data from working tree if versioned
923- from_entry = self._tree.iter_entries_by_dir([file_id]).next()[1]
924+ from_entry = self._tree.iter_entries_by_dir(
925+ specific_files=[from_path]).next()[1]
926 from_name = from_entry.name
927 from_parent = from_entry.parent_id
928 else:
929@@ -971,12 +972,6 @@
930 else:
931 to_versioned = True
932
933- from_name, from_parent, from_kind, from_executable = \
934- self._from_file_data(from_trans_id, from_versioned, file_id)
935-
936- to_name, to_parent, to_kind, to_executable = \
937- self._to_file_data(to_trans_id, from_trans_id, from_executable)
938-
939 if not from_versioned:
940 from_path = None
941 else:
942@@ -985,6 +980,13 @@
943 to_path = None
944 else:
945 to_path = final_paths.get_path(to_trans_id)
946+
947+ from_name, from_parent, from_kind, from_executable = \
948+ self._from_file_data(from_trans_id, from_versioned, from_path)
949+
950+ to_name, to_parent, to_kind, to_executable = \
951+ self._to_file_data(to_trans_id, from_trans_id, from_executable)
952+
953 if from_kind != to_kind:
954 modified = True
955 elif to_kind in ('file', 'symlink') and (
956@@ -1763,9 +1765,6 @@
957 inventory_delta.append((path, None, file_id, None))
958 new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
959 new_paths)
960- entries = self._tree.iter_entries_by_dir(
961- viewvalues(new_path_file_ids))
962- old_paths = dict((e.file_id, p) for p, e in entries)
963 final_kinds = {}
964 for num, (path, trans_id) in enumerate(new_paths):
965 if (num % 10) == 0:
966@@ -1793,7 +1792,10 @@
967 new_entry = inventory.make_entry(kind,
968 self.final_name(trans_id),
969 parent_file_id, file_id)
970- old_path = old_paths.get(new_entry.file_id)
971+ try:
972+ old_path = self._tree.id2path(new_entry.file_id)
973+ except errors.NoSuchId:
974+ old_path = None
975 new_executability = self._new_executability.get(trans_id)
976 if new_executability is not None:
977 new_entry.executable = new_executability
978@@ -1944,10 +1946,8 @@
979 path = self._tree_id_paths[parent_id]
980 except KeyError:
981 return
982- file_id = self.tree_file_id(parent_id)
983- if file_id is None:
984- return
985- entry = self._tree.iter_entries_by_dir([file_id]).next()[1]
986+ entry = self._tree.iter_entries_by_dir(
987+ specific_files=[path]).next()[1]
988 children = getattr(entry, 'children', {})
989 for child in children:
990 childpath = joinpath(path, child)
991@@ -2127,14 +2127,14 @@
992 if self._transform.final_file_id(trans_id) is None:
993 yield self._final_paths._determine_path(trans_id)
994
995- def _make_inv_entries(self, ordered_entries, specific_file_ids=None,
996+ def _make_inv_entries(self, ordered_entries, specific_files=None,
997 yield_parents=False):
998 for trans_id, parent_file_id in ordered_entries:
999 file_id = self._transform.final_file_id(trans_id)
1000 if file_id is None:
1001 continue
1002- if (specific_file_ids is not None
1003- and file_id not in specific_file_ids):
1004+ if (specific_files is not None and
1005+ unicode(self._final_paths.get_path(trans_id)) not in specific_files):
1006 continue
1007 kind = self._transform.final_kind(trans_id)
1008 if kind is None:
1009@@ -2172,7 +2172,7 @@
1010 for entry, trans_id in self._make_inv_entries(todo):
1011 yield entry
1012
1013- def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
1014+ def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
1015 # This may not be a maximally efficient implementation, but it is
1016 # reasonably straightforward. An implementation that grafts the
1017 # TreeTransform changes onto the tree's iter_entries_by_dir results
1018@@ -2180,7 +2180,7 @@
1019 # position.
1020 ordered_ids = self._list_files_by_dir()
1021 for entry, trans_id in self._make_inv_entries(ordered_ids,
1022- specific_file_ids, yield_parents=yield_parents):
1023+ specific_files, yield_parents=yield_parents):
1024 yield unicode(self._final_paths.get_path(trans_id)), entry
1025
1026 def _iter_entries_for_dir(self, dir_path):
1027@@ -3081,7 +3081,7 @@
1028 if file_id is None:
1029 file_id = tt.inactive_file_id(trans_id)
1030 _, entry = next(path_tree.iter_entries_by_dir(
1031- [file_id]))
1032+ specific_files=[path_tree.id2path(file_id)]))
1033 # special-case the other tree root (move its
1034 # children to current root)
1035 if entry.parent_id is None:
1036
1037=== modified file 'breezy/tree.py'
1038--- breezy/tree.py 2018-03-24 00:39:56 +0000
1039+++ breezy/tree.py 2018-03-24 01:23:20 +0000
1040@@ -213,7 +213,7 @@
1041 """
1042 raise NotImplementedError(self.id2path)
1043
1044- def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
1045+ def iter_entries_by_dir(self, specific_files=None, yield_parents=False):
1046 """Walk the tree in 'by_dir' order.
1047
1048 This will yield each entry in the tree as a (path, entry) tuple.
1049@@ -239,8 +239,8 @@
1050 a, f, a/b, a/d, a/b/c, a/d/e, f/g
1051
1052 :param yield_parents: If True, yield the parents from the root leading
1053- down to specific_file_ids that have been requested. This has no
1054- impact if specific_file_ids is None.
1055+ down to specific_files that have been requested. This has no
1056+ impact if specific_files is None.
1057 """
1058 raise NotImplementedError(self.iter_entries_by_dir)
1059
1060@@ -501,24 +501,23 @@
1061 """
1062 return self.path2id(path) is not None
1063
1064- def paths2ids(self, paths, trees=[], require_versioned=True):
1065- """Return all the ids that can be reached by walking from paths.
1066-
1067- Each path is looked up in this tree and any extras provided in
1068- trees, and this is repeated recursively: the children in an extra tree
1069- of a directory that has been renamed under a provided path in this tree
1070- are all returned, even if none exist under a provided path in this
1071- tree, and vice versa.
1072-
1073- :param paths: An iterable of paths to start converting to ids from.
1074- Alternatively, if paths is None, no ids should be calculated and None
1075- will be returned. This is offered to make calling the api unconditional
1076- for code that *might* take a list of files.
1077- :param trees: Additional trees to consider.
1078- :param require_versioned: If False, do not raise NotVersionedError if
1079- an element of paths is not versioned in this tree and all of trees.
1080+ def find_related_paths_across_trees(self, paths, trees=[],
1081+ require_versioned=True):
1082+ """Find related paths in tree corresponding to specified filenames in any
1083+ of `lookup_trees`.
1084+
1085+ All matches in all trees will be used, and all children of matched
1086+ directories will be used.
1087+
1088+ :param paths: The filenames to find related paths for (if None, returns
1089+ None)
1090+ :param trees: The trees to find file_ids within
1091+ :param require_versioned: if true, all specified filenames must occur in
1092+ at least one tree.
1093+ :return: a set of paths for the specified filenames and their children
1094+ in `tree`
1095 """
1096- return find_ids_across_trees(paths, [self] + list(trees), require_versioned)
1097+ raise NotImplementedError(self.find_related_paths_across_trees)
1098
1099 def lock_read(self):
1100 """Lock this tree for multiple read only operations.
1101@@ -657,84 +656,6 @@
1102 return searcher
1103
1104
1105-def find_ids_across_trees(filenames, trees, require_versioned=True):
1106- """Find the ids corresponding to specified filenames.
1107-
1108- All matches in all trees will be used, and all children of matched
1109- directories will be used.
1110-
1111- :param filenames: The filenames to find file_ids for (if None, returns
1112- None)
1113- :param trees: The trees to find file_ids within
1114- :param require_versioned: if true, all specified filenames must occur in
1115- at least one tree.
1116- :return: a set of file ids for the specified filenames and their children.
1117- """
1118- if not filenames:
1119- return None
1120- specified_path_ids = _find_ids_across_trees(filenames, trees,
1121- require_versioned)
1122- return _find_children_across_trees(specified_path_ids, trees)
1123-
1124-
1125-def _find_ids_across_trees(filenames, trees, require_versioned):
1126- """Find the ids corresponding to specified filenames.
1127-
1128- All matches in all trees will be used, but subdirectories are not scanned.
1129-
1130- :param filenames: The filenames to find file_ids for
1131- :param trees: The trees to find file_ids within
1132- :param require_versioned: if true, all specified filenames must occur in
1133- at least one tree.
1134- :return: a set of file ids for the specified filenames
1135- """
1136- not_versioned = []
1137- interesting_ids = set()
1138- for tree_path in filenames:
1139- not_found = True
1140- for tree in trees:
1141- file_id = tree.path2id(tree_path)
1142- if file_id is not None:
1143- interesting_ids.add(file_id)
1144- not_found = False
1145- if not_found:
1146- not_versioned.append(tree_path)
1147- if len(not_versioned) > 0 and require_versioned:
1148- raise errors.PathsNotVersionedError(not_versioned)
1149- return interesting_ids
1150-
1151-
1152-def _find_children_across_trees(specified_ids, trees):
1153- """Return a set including specified ids and their children.
1154-
1155- All matches in all trees will be used.
1156-
1157- :param trees: The trees to find file_ids within
1158- :return: a set containing all specified ids and their children
1159- """
1160- interesting_ids = set(specified_ids)
1161- pending = interesting_ids
1162- # now handle children of interesting ids
1163- # we loop so that we handle all children of each id in both trees
1164- while len(pending) > 0:
1165- new_pending = set()
1166- for tree in trees:
1167- for file_id in pending:
1168- try:
1169- path = tree.id2path(file_id)
1170- except errors.NoSuchId:
1171- continue
1172- try:
1173- for child in tree.iter_child_entries(path, file_id):
1174- if child.file_id not in interesting_ids:
1175- new_pending.add(child.file_id)
1176- except errors.NotADirectory:
1177- pass
1178- interesting_ids.update(new_pending)
1179- pending = new_pending
1180- return interesting_ids
1181-
1182-
1183 class InterTree(InterObject):
1184 """This class represents operations taking place between two Trees.
1185
1186@@ -854,18 +775,6 @@
1187 if extra_trees is not None:
1188 trees = trees + tuple(extra_trees)
1189 with self.lock_read():
1190- # target is usually the newer tree:
1191- specific_file_ids = self.target.paths2ids(specific_files, trees,
1192- require_versioned=require_versioned)
1193- if specific_files and not specific_file_ids:
1194- # All files are unversioned, so just return an empty delta
1195- # _compare_trees would think we want a complete delta
1196- result = delta.TreeDelta()
1197- fake_entry = inventory.InventoryFile('unused', 'unused', 'unused')
1198- result.unversioned = [(path, None,
1199- self.target._comparison_data(fake_entry, path)[0]) for path in
1200- specific_files]
1201- return result
1202 return delta._compare_trees(self.source, self.target, want_unchanged,
1203 specific_files, include_root, extra_trees=extra_trees,
1204 require_versioned=require_versioned,
1205@@ -907,17 +816,23 @@
1206 output. An unversioned file is defined as one with (False, False)
1207 for the versioned pair.
1208 """
1209- lookup_trees = [self.source]
1210- if extra_trees:
1211- lookup_trees.extend(extra_trees)
1212+ if not extra_trees:
1213+ extra_trees = []
1214+ else:
1215+ extra_trees = list(extra_trees)
1216 # The ids of items we need to examine to insure delta consistency.
1217 precise_file_ids = set()
1218 changed_file_ids = []
1219 if specific_files == []:
1220- specific_file_ids = []
1221+ target_specific_files = []
1222+ source_specific_files = []
1223 else:
1224- specific_file_ids = self.target.paths2ids(specific_files,
1225- lookup_trees, require_versioned=require_versioned)
1226+ target_specific_files = self.target.find_related_paths_across_trees(
1227+ specific_files, [self.source] + extra_trees,
1228+ require_versioned=require_versioned)
1229+ source_specific_files = self.source.find_related_paths_across_trees(
1230+ specific_files, [self.target] + extra_trees,
1231+ require_versioned=require_versioned)
1232 if specific_files is not None:
1233 # reparented or added entries must have their parents included
1234 # so that valid deltas can be created. The seen_parents set
1235@@ -937,10 +852,10 @@
1236 all_unversioned = collections.deque()
1237 to_paths = {}
1238 from_entries_by_dir = list(self.source.iter_entries_by_dir(
1239- specific_file_ids=specific_file_ids))
1240+ specific_files=source_specific_files))
1241 from_data = dict((e.file_id, (p, e)) for p, e in from_entries_by_dir)
1242 to_entries_by_dir = list(self.target.iter_entries_by_dir(
1243- specific_file_ids=specific_file_ids))
1244+ specific_files=target_specific_files))
1245 num_entries = len(from_entries_by_dir) + len(to_entries_by_dir)
1246 entry_count = 0
1247 # the unversioned path lookup only occurs on real trees - where there
1248@@ -969,7 +884,7 @@
1249 if pb is not None:
1250 pb.update('comparing files', entry_count, num_entries)
1251 if changes or include_unchanged:
1252- if specific_file_ids is not None:
1253+ if specific_files is not None:
1254 new_parent_id = result[4][1]
1255 precise_file_ids.add(new_parent_id)
1256 changed_file_ids.append(result[0])
1257@@ -1017,12 +932,12 @@
1258 yield(file_id, (path, to_path), changed_content, versioned, parent,
1259 name, kind, executable)
1260 changed_file_ids = set(changed_file_ids)
1261- if specific_file_ids is not None:
1262+ if specific_files is not None:
1263 for result in self._handle_precise_ids(precise_file_ids,
1264 changed_file_ids):
1265 yield result
1266
1267- def _get_entry(self, tree, file_id):
1268+ def _get_entry(self, tree, path):
1269 """Get an inventory entry from a tree, with missing entries as None.
1270
1271 If the tree raises NotImplementedError on accessing .inventory, then
1272@@ -1032,20 +947,12 @@
1273 :param tree: The tree to lookup the entry in.
1274 :param file_id: The file_id to lookup.
1275 """
1276+ # No inventory available.
1277 try:
1278- inventory = tree.root_inventory
1279- except (AttributeError, NotImplementedError):
1280- # No inventory available.
1281- try:
1282- iterator = tree.iter_entries_by_dir(specific_file_ids=[file_id])
1283- return iterator.next()[1]
1284- except StopIteration:
1285- return None
1286- else:
1287- try:
1288- return inventory[file_id]
1289- except errors.NoSuchId:
1290- return None
1291+ iterator = tree.iter_entries_by_dir(specific_files=[path])
1292+ return iterator.next()[1]
1293+ except StopIteration:
1294+ return None
1295
1296 def _handle_precise_ids(self, precise_file_ids, changed_file_ids,
1297 discarded_changes=None):
1298@@ -1093,22 +1000,26 @@
1299 # Examine file_id
1300 if discarded_changes:
1301 result = discarded_changes.get(file_id)
1302- old_entry = None
1303+ source_entry = None
1304 else:
1305 result = None
1306 if result is None:
1307- old_entry = self._get_entry(self.source, file_id)
1308- new_entry = self._get_entry(self.target, file_id)
1309 try:
1310 source_path = self.source.id2path(file_id)
1311 except errors.NoSuchId:
1312 source_path = None
1313+ source_entry = None
1314+ else:
1315+ source_entry = self._get_entry(self.source, source_path)
1316 try:
1317 target_path = self.target.id2path(file_id)
1318 except errors.NoSuchId:
1319 target_path = None
1320+ target_entry = None
1321+ else:
1322+ target_entry = self._get_entry(self.target, target_path)
1323 result, changes = self._changes_from_entries(
1324- old_entry, new_entry, source_path, target_path)
1325+ source_entry, target_entry, source_path, target_path)
1326 else:
1327 changes = True
1328 # Get this parents parent to examine.
1329@@ -1119,9 +1030,9 @@
1330 result[6][1] != 'directory'):
1331 # This stopped being a directory, the old children have
1332 # to be included.
1333- if old_entry is None:
1334+ if source_entry is None:
1335 # Reusing a discarded change.
1336- old_entry = self._get_entry(self.source, file_id)
1337+ source_entry = self._get_entry(self.source, result[1][0])
1338 precise_file_ids.update(
1339 child.file_id
1340 for child in self.source.iter_child_entries(result[1][0]))

Subscribers

People subscribed via source and target branches