91
95
want_unversioned=want_unversioned,
94
def _iter_changes(self, from_tree, include_unchanged=False,
98
def iter_changes(self, from_tree, include_unchanged=False,
95
99
specific_files=None, pb=None, extra_trees=None,
96
100
require_versioned=True, want_unversioned=False):
97
101
intertree = InterTree.get(from_tree, self)
98
return intertree._iter_changes(include_unchanged, specific_files, pb,
102
return intertree.iter_changes(include_unchanged, specific_files, pb,
99
103
extra_trees, require_versioned, want_unversioned=want_unversioned)
101
105
def conflicts(self):
102
106
"""Get a list of the conflicts in the tree.
104
108
Each conflict is an instance of bzrlib.conflicts.Conflict.
110
return _mod_conflicts.ConflictList()
108
112
def extras(self):
109
113
"""For trees that can have unversioned files, return all such paths."""
112
116
def get_parent_ids(self):
113
"""Get the parent ids for this tree.
117
"""Get the parent ids for this tree.
115
119
:return: a list of parent ids. [] is returned to indicate
116
120
a tree with no parents.
117
121
:raises: BzrError if the parents are not known.
119
123
raise NotImplementedError(self.get_parent_ids)
121
125
def has_filename(self, filename):
122
126
"""True if the tree has given filename."""
123
raise NotImplementedError()
127
raise NotImplementedError(self.has_filename)
125
129
def has_id(self, file_id):
126
file_id = osutils.safe_file_id(file_id)
127
130
return self.inventory.has_id(file_id)
129
__contains__ = has_id
132
def __contains__(self, file_id):
133
return self.has_id(file_id)
131
135
def has_or_had_id(self, file_id):
132
file_id = osutils.safe_file_id(file_id)
133
136
if file_id == self.inventory.root.file_id:
135
138
return self.inventory.has_id(file_id)
169
175
def iter_entries_by_dir(self, specific_file_ids=None):
170
176
"""Walk the tree in 'by_dir' order.
172
This will yield each entry in the tree as a (path, entry) tuple. The
173
order that they are yielded is: the contents of a directory are
174
preceeded by the parent of a directory, and all the contents of a
175
directory are grouped together.
178
This will yield each entry in the tree as a (path, entry) tuple.
179
The order that they are yielded is:
181
Directories are walked in a depth-first lexicographical order,
182
however, whenever a directory is reached, all of its direct child
183
nodes are yielded in lexicographical order before yielding the
186
For example, in the tree::
196
The yield order (ignoring root) would be::
197
a, f, a/b, a/d, a/b/c, a/d/e, f/g
177
199
return self.inventory.iter_entries_by_dir(
178
200
specific_file_ids=specific_file_ids)
180
202
def iter_references(self):
181
for path, entry in self.iter_entries_by_dir():
182
if entry.kind == 'tree-reference':
183
yield path, entry.file_id
203
if self.supports_tree_reference():
204
for path, entry in self.iter_entries_by_dir():
205
if entry.kind == 'tree-reference':
206
yield path, entry.file_id
185
208
def kind(self, file_id):
186
209
raise NotImplementedError("Tree subclass %s must implement kind"
187
210
% self.__class__.__name__)
212
def stored_kind(self, file_id):
213
"""File kind stored for this file_id.
215
May not match kind on disk for working trees. Always available
216
for versioned files, even when the file itself is missing.
218
return self.kind(file_id)
220
def path_content_summary(self, path):
221
"""Get a summary of the information about path.
223
:param path: A relative path within the tree.
224
:return: A tuple containing kind, size, exec, sha1-or-link.
225
Kind is always present (see tree.kind()).
226
size is present if kind is file, None otherwise.
227
exec is None unless kind is file and the platform supports the 'x'
229
sha1-or-link is the link target if kind is symlink, or the sha1 if
230
it can be obtained without reading the file.
232
raise NotImplementedError(self.path_content_summary)
189
234
def get_reference_revision(self, file_id, path=None):
190
235
raise NotImplementedError("Tree subclass %s must implement "
191
236
"get_reference_revision"
208
253
def _get_inventory(self):
209
254
return self._inventory
211
def get_file(self, file_id):
212
"""Return a file object for the file file_id in the tree."""
256
def get_file(self, file_id, path=None):
257
"""Return a file object for the file file_id in the tree.
259
If both file_id and path are defined, it is implementation defined as
260
to which one is used.
213
262
raise NotImplementedError(self.get_file)
264
def get_file_with_stat(self, file_id, path=None):
265
"""Get a file handle and stat object for file_id.
267
The default implementation returns (self.get_file, None) for backwards
270
:param file_id: The file id to read.
271
:param path: The path of the file, if it is known.
272
:return: A tuple (file_handle, stat_value_or_None). If the tree has
273
no stat facility, or need for a stat cache feedback during commit,
274
it may return None for the second element of the tuple.
276
return (self.get_file(file_id, path), None)
278
def get_file_text(self, file_id, path=None):
279
"""Return the byte content of a file.
281
:param file_id: The file_id of the file.
282
:param path: The path of the file.
283
If both file_id and path are supplied, an implementation may use
286
my_file = self.get_file(file_id, path)
288
return my_file.read()
292
def get_file_lines(self, file_id, path=None):
293
"""Return the content of a file, as lines.
295
:param file_id: The file_id of the file.
296
:param path: The path of the file.
297
If both file_id and path are supplied, an implementation may use
300
return osutils.split_lines(self.get_file_text(file_id, path))
215
302
def get_file_mtime(self, file_id, path=None):
216
303
"""Return the modification time for a file.
235
357
raise NotImplementedError(self.get_symlink_target)
237
def annotate_iter(self, file_id):
238
"""Return an iterator of revision_id, line tuples
359
def get_canonical_inventory_paths(self, paths):
360
"""Like get_canonical_inventory_path() but works on multiple items.
362
:param paths: A sequence of paths relative to the root of the tree.
363
:return: A list of paths, with each item the corresponding input path
364
adjusted to account for existing elements that match case
367
return list(self._yield_canonical_inventory_paths(paths))
369
def get_canonical_inventory_path(self, path):
370
"""Returns the first inventory item that case-insensitively matches path.
372
If a path matches exactly, it is returned. If no path matches exactly
373
but more than one path matches case-insensitively, it is implementation
374
defined which is returned.
376
If no path matches case-insensitively, the input path is returned, but
377
with as many path entries that do exist changed to their canonical
380
If you need to resolve many names from the same tree, you should
381
use get_canonical_inventory_paths() to avoid O(N) behaviour.
383
:param path: A paths relative to the root of the tree.
384
:return: The input path adjusted to account for existing elements
385
that match case insensitively.
387
return self._yield_canonical_inventory_paths([path]).next()
389
def _yield_canonical_inventory_paths(self, paths):
391
# First, if the path as specified exists exactly, just use it.
392
if self.path2id(path) is not None:
396
cur_id = self.get_root_id()
398
bit_iter = iter(path.split("/"))
401
for child in self.iter_children(cur_id):
403
child_base = os.path.basename(self.id2path(child))
404
if child_base.lower() == lelt:
406
cur_path = osutils.pathjoin(cur_path, child_base)
409
# before a change is committed we can see this error...
412
# got to the end of this directory and no entries matched.
413
# Return what matched so far, plus the rest as specified.
414
cur_path = osutils.pathjoin(cur_path, elt, *list(bit_iter))
419
def get_root_id(self):
420
"""Return the file_id for the root of this tree."""
421
raise NotImplementedError(self.get_root_id)
423
def annotate_iter(self, file_id,
424
default_revision=_mod_revision.CURRENT_REVISION):
425
"""Return an iterator of revision_id, line tuples.
240
427
For working trees (and mutable trees in general), the special
241
428
revision_id 'current:' will be used for lines that are new in this
242
429
tree, e.g. uncommitted changes.
243
430
:param file_id: The file to produce an annotated version from
431
:param default_revision: For lines that don't match a basis, mark them
432
with this revision id. Not all implementations will make use of
245
435
raise NotImplementedError(self.annotate_iter)
437
def _get_plan_merge_data(self, file_id, other, base):
438
from bzrlib import versionedfile
439
vf = versionedfile._PlanMergeVersionedFile(file_id)
440
last_revision_a = self._get_file_revision(file_id, vf, 'this:')
441
last_revision_b = other._get_file_revision(file_id, vf, 'other:')
443
last_revision_base = None
445
last_revision_base = base._get_file_revision(file_id, vf, 'base:')
446
return vf, last_revision_a, last_revision_b, last_revision_base
448
def plan_file_merge(self, file_id, other, base=None):
449
"""Generate a merge plan based on annotations.
451
If the file contains uncommitted changes in this tree, they will be
452
attributed to the 'current:' pseudo-revision. If the file contains
453
uncommitted changes in the other tree, they will be assigned to the
454
'other:' pseudo-revision.
456
data = self._get_plan_merge_data(file_id, other, base)
457
vf, last_revision_a, last_revision_b, last_revision_base = data
458
return vf.plan_merge(last_revision_a, last_revision_b,
461
def plan_file_lca_merge(self, file_id, other, base=None):
462
"""Generate a merge plan based lca-newness.
464
If the file contains uncommitted changes in this tree, they will be
465
attributed to the 'current:' pseudo-revision. If the file contains
466
uncommitted changes in the other tree, they will be assigned to the
467
'other:' pseudo-revision.
469
data = self._get_plan_merge_data(file_id, other, base)
470
vf, last_revision_a, last_revision_b, last_revision_base = data
471
return vf.plan_lca_merge(last_revision_a, last_revision_b,
474
def _iter_parent_trees(self):
475
"""Iterate through parent trees, defaulting to Tree.revision_tree."""
476
for revision_id in self.get_parent_ids():
478
yield self.revision_tree(revision_id)
479
except errors.NoSuchRevisionInTree:
480
yield self.repository.revision_tree(revision_id)
483
def _file_revision(revision_tree, file_id):
484
"""Determine the revision associated with a file in a given tree."""
485
revision_tree.lock_read()
487
return revision_tree.inventory[file_id].revision
489
revision_tree.unlock()
491
def _get_file_revision(self, file_id, vf, tree_revision):
492
"""Ensure that file_id, tree_revision is in vf to plan the merge."""
494
if getattr(self, '_repository', None) is None:
495
last_revision = tree_revision
496
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
497
self._iter_parent_trees()]
498
vf.add_lines((file_id, last_revision), parent_keys,
499
self.get_file(file_id).readlines())
500
repo = self.branch.repository
503
last_revision = self._file_revision(self, file_id)
504
base_vf = self._repository.texts
505
if base_vf not in vf.fallback_versionedfiles:
506
vf.fallback_versionedfiles.append(base_vf)
247
509
inventory = property(_get_inventory,
248
510
doc="Inventory of this Tree")
250
512
def _check_retrieved(self, ie, f):
251
513
if not __debug__:
253
515
fp = fingerprint_file(f)
256
518
if ie.text_size is not None:
257
519
if ie.text_size != fp['size']:
258
520
raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
369
630
raise NotImplementedError(self.walkdirs)
372
class EmptyTree(Tree):
375
self._inventory = Inventory(root_id=None)
376
symbol_versioning.warn('EmptyTree is deprecated as of bzr 0.9 please'
377
' use repository.revision_tree instead.',
378
DeprecationWarning, stacklevel=2)
380
def get_parent_ids(self):
383
def get_symlink_target(self, file_id):
386
def has_filename(self, filename):
632
def supports_content_filtering(self):
389
def kind(self, file_id):
390
file_id = osutils.safe_file_id(file_id)
391
assert self._inventory[file_id].kind == "directory"
394
def list_files(self, include_root=False):
397
def __contains__(self, file_id):
398
file_id = osutils.safe_file_id(file_id)
399
return (file_id in self._inventory)
401
def get_file_sha1(self, file_id, path=None, stat_value=None):
635
def _content_filter_stack(self, path=None, file_id=None):
636
"""The stack of content filters for a path if filtering is supported.
638
Readers will be applied in first-to-last order.
639
Writers will be applied in last-to-first order.
640
Either the path or the file-id needs to be provided.
642
:param path: path relative to the root of the tree
644
:param file_id: file_id or None if unknown
645
:return: the list of filters - [] if there are none
647
filter_pref_names = filters._get_registered_names()
648
if len(filter_pref_names) == 0:
651
path = self.id2path(file_id)
652
prefs = self.iter_search_rules([path], filter_pref_names).next()
653
stk = filters._get_filter_stack_for(prefs)
654
if 'filters' in debug.debug_flags:
655
note("*** %s content-filter: %s => %r" % (path,prefs,stk))
658
def _content_filter_stack_provider(self):
659
"""A function that returns a stack of ContentFilters.
661
The function takes a path (relative to the top of the tree) and a
662
file-id as parameters.
664
:return: None if content filtering is not supported by this tree.
666
if self.supports_content_filtering():
667
return lambda path, file_id: \
668
self._content_filter_stack(path, file_id)
672
def iter_search_rules(self, path_names, pref_names=None,
673
_default_searcher=None):
674
"""Find the preferences for filenames in a tree.
676
:param path_names: an iterable of paths to find attributes for.
677
Paths are given relative to the root of the tree.
678
:param pref_names: the list of preferences to lookup - None for all
679
:param _default_searcher: private parameter to assist testing - don't use
680
:return: an iterator of tuple sequences, one per path-name.
681
See _RulesSearcher.get_items for details on the tuple sequence.
683
if _default_searcher is None:
684
_default_searcher = rules._per_user_searcher
685
searcher = self._get_rules_searcher(_default_searcher)
686
if searcher is not None:
687
if pref_names is not None:
688
for path in path_names:
689
yield searcher.get_selected_items(path, pref_names)
691
for path in path_names:
692
yield searcher.get_items(path)
695
def _get_rules_searcher(self, default_searcher):
696
"""Get the RulesSearcher for this tree given the default one."""
697
searcher = default_searcher
405
701
######################################################################
756
1054
self.source._comparison_data(from_entry, path)
757
1055
kind = (from_kind, None)
758
1056
executable = (from_executable, None)
759
changed_content = True
1057
changed_content = from_kind is not None
760
1058
# the parent's path is necessarily known at this point.
761
1059
yield(file_id, (path, to_path), changed_content, versioned, parent,
762
1060
name, kind, executable)
765
# This was deprecated before 0.12, but did not have an official warning
766
@symbol_versioning.deprecated_function(symbol_versioning.zero_twelve)
767
def RevisionTree(*args, **kwargs):
768
"""RevisionTree has moved to bzrlib.revisiontree.RevisionTree()
770
Accessing it as bzrlib.tree.RevisionTree has been deprecated as of
773
from bzrlib.revisiontree import RevisionTree as _RevisionTree
774
return _RevisionTree(*args, **kwargs)
1063
class MultiWalker(object):
1064
"""Walk multiple trees simultaneously, getting combined results."""
1066
# Note: This could be written to not assume you can do out-of-order
1067
# lookups. Instead any nodes that don't match in all trees could be
1068
# marked as 'deferred', and then returned in the final cleanup loop.
1069
# For now, I think it is "nicer" to return things as close to the
1070
# "master_tree" order as we can.
1072
def __init__(self, master_tree, other_trees):
1073
"""Create a new MultiWalker.
1075
All trees being walked must implement "iter_entries_by_dir()", such
1076
that they yield (path, object) tuples, where that object will have a
1077
'.file_id' member, that can be used to check equality.
1079
:param master_tree: All trees will be 'slaved' to the master_tree such
1080
that nodes in master_tree will be used as 'first-pass' sync points.
1081
Any nodes that aren't in master_tree will be merged in a second
1083
:param other_trees: A list of other trees to walk simultaneously.
1085
self._master_tree = master_tree
1086
self._other_trees = other_trees
1088
# Keep track of any nodes that were properly processed just out of
1089
# order, that way we don't return them at the end, we don't have to
1090
# track *all* processed file_ids, just the out-of-order ones
1091
self._out_of_order_processed = set()
1094
def _step_one(iterator):
1095
"""Step an iter_entries_by_dir iterator.
1097
:return: (has_more, path, ie)
1098
If has_more is False, path and ie will be None.
1101
path, ie = iterator.next()
1102
except StopIteration:
1103
return False, None, None
1105
return True, path, ie
1108
def _cmp_path_by_dirblock(path1, path2):
1109
"""Compare two paths based on what directory they are in.
1111
This generates a sort order, such that all children of a directory are
1112
sorted together, and grandchildren are in the same order as the
1113
children appear. But all grandchildren come after all children.
1115
:param path1: first path
1116
:param path2: the second path
1117
:return: negative number if ``path1`` comes first,
1118
0 if paths are equal
1119
and a positive number if ``path2`` sorts first
1121
# Shortcut this special case
1124
# This is stolen from _dirstate_helpers_py.py, only switching it to
1125
# Unicode objects. Consider using encode_utf8() and then using the
1126
# optimized versions, or maybe writing optimized unicode versions.
1127
if not isinstance(path1, unicode):
1128
raise TypeError("'path1' must be a unicode string, not %s: %r"
1129
% (type(path1), path1))
1130
if not isinstance(path2, unicode):
1131
raise TypeError("'path2' must be a unicode string, not %s: %r"
1132
% (type(path2), path2))
1133
return cmp(MultiWalker._path_to_key(path1),
1134
MultiWalker._path_to_key(path2))
1137
def _path_to_key(path):
1138
dirname, basename = osutils.split(path)
1139
return (dirname.split(u'/'), basename)
1141
def _lookup_by_file_id(self, extra_entries, other_tree, file_id):
1142
"""Lookup an inventory entry by file_id.
1144
This is called when an entry is missing in the normal order.
1145
Generally this is because a file was either renamed, or it was
1146
deleted/added. If the entry was found in the inventory and not in
1147
extra_entries, it will be added to self._out_of_order_processed
1149
:param extra_entries: A dictionary of {file_id: (path, ie)}. This
1150
should be filled with entries that were found before they were
1151
used. If file_id is present, it will be removed from the
1153
:param other_tree: The Tree to search, in case we didn't find the entry
1155
:param file_id: The file_id to look for
1156
:return: (path, ie) if found or (None, None) if not present.
1158
if file_id in extra_entries:
1159
return extra_entries.pop(file_id)
1160
# TODO: Is id2path better as the first call, or is
1161
# inventory[file_id] better as a first check?
1163
cur_path = other_tree.id2path(file_id)
1164
except errors.NoSuchId:
1166
if cur_path is None:
1169
self._out_of_order_processed.add(file_id)
1170
cur_ie = other_tree.inventory[file_id]
1171
return (cur_path, cur_ie)
1174
"""Match up the values in the different trees."""
1175
for result in self._walk_master_tree():
1177
self._finish_others()
1178
for result in self._walk_others():
1181
def _walk_master_tree(self):
1182
"""First pass, walk all trees in lock-step.
1184
When we are done, all nodes in the master_tree will have been
1185
processed. _other_walkers, _other_entries, and _others_extra will be
1186
set on 'self' for future processing.
1188
# This iterator has the most "inlining" done, because it tends to touch
1189
# every file in the tree, while the others only hit nodes that don't
1191
master_iterator = self._master_tree.iter_entries_by_dir()
1193
other_walkers = [other.iter_entries_by_dir()
1194
for other in self._other_trees]
1195
other_entries = [self._step_one(walker) for walker in other_walkers]
1196
# Track extra nodes in the other trees
1197
others_extra = [{} for i in xrange(len(self._other_trees))]
1199
master_has_more = True
1200
step_one = self._step_one
1201
lookup_by_file_id = self._lookup_by_file_id
1202
out_of_order_processed = self._out_of_order_processed
1204
while master_has_more:
1205
(master_has_more, path, master_ie) = step_one(master_iterator)
1206
if not master_has_more:
1209
file_id = master_ie.file_id
1211
other_values_append = other_values.append
1212
next_other_entries = []
1213
next_other_entries_append = next_other_entries.append
1214
for idx, (other_has_more, other_path, other_ie) in enumerate(other_entries):
1215
if not other_has_more:
1216
other_values_append(lookup_by_file_id(
1217
others_extra[idx], self._other_trees[idx], file_id))
1218
next_other_entries_append((False, None, None))
1219
elif file_id == other_ie.file_id:
1220
# This is the critical code path, as most of the entries
1221
# should match between most trees.
1222
other_values_append((other_path, other_ie))
1223
next_other_entries_append(step_one(other_walkers[idx]))
1225
# This walker did not match, step it until it either
1226
# matches, or we know we are past the current walker.
1227
other_walker = other_walkers[idx]
1228
other_extra = others_extra[idx]
1229
while (other_has_more and
1230
self._cmp_path_by_dirblock(other_path, path) < 0):
1231
other_file_id = other_ie.file_id
1232
if other_file_id not in out_of_order_processed:
1233
other_extra[other_file_id] = (other_path, other_ie)
1234
other_has_more, other_path, other_ie = \
1235
step_one(other_walker)
1236
if other_has_more and other_ie.file_id == file_id:
1237
# We ended up walking to this point, match and step
1239
other_values_append((other_path, other_ie))
1240
other_has_more, other_path, other_ie = \
1241
step_one(other_walker)
1243
# This record isn't in the normal order, see if it
1245
other_values_append(lookup_by_file_id(
1246
other_extra, self._other_trees[idx], file_id))
1247
next_other_entries_append((other_has_more, other_path,
1249
other_entries = next_other_entries
1251
# We've matched all the walkers, yield this datapoint
1252
yield path, file_id, master_ie, other_values
1253
self._other_walkers = other_walkers
1254
self._other_entries = other_entries
1255
self._others_extra = others_extra
1257
def _finish_others(self):
1258
"""Finish walking the other iterators, so we get all entries."""
1259
for idx, info in enumerate(self._other_entries):
1260
other_extra = self._others_extra[idx]
1261
(other_has_more, other_path, other_ie) = info
1262
while other_has_more:
1263
other_file_id = other_ie.file_id
1264
if other_file_id not in self._out_of_order_processed:
1265
other_extra[other_file_id] = (other_path, other_ie)
1266
other_has_more, other_path, other_ie = \
1267
self._step_one(self._other_walkers[idx])
1268
del self._other_entries
1270
def _walk_others(self):
1271
"""Finish up by walking all the 'deferred' nodes."""
1272
# TODO: One alternative would be to grab all possible unprocessed
1273
# file_ids, and then sort by path, and then yield them. That
1274
# might ensure better ordering, in case a caller strictly
1275
# requires parents before children.
1276
for idx, other_extra in enumerate(self._others_extra):
1277
others = sorted(other_extra.itervalues(),
1278
key=lambda x: self._path_to_key(x[0]))
1279
for other_path, other_ie in others:
1280
file_id = other_ie.file_id
1281
# We don't need to check out_of_order_processed here, because
1282
# the lookup_by_file_id will be removing anything processed
1283
# from the extras cache
1284
other_extra.pop(file_id)
1285
other_values = [(None, None) for i in xrange(idx)]
1286
other_values.append((other_path, other_ie))
1287
for alt_idx, alt_extra in enumerate(self._others_extra[idx+1:]):
1288
alt_idx = alt_idx + idx + 1
1289
alt_extra = self._others_extra[alt_idx]
1290
alt_tree = self._other_trees[alt_idx]
1291
other_values.append(self._lookup_by_file_id(
1292
alt_extra, alt_tree, file_id))
1293
yield other_path, file_id, None, other_values