/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tree.py

  • Committer: Jelmer Vernooij
  • Date: 2020-09-02 16:35:18 UTC
  • mto: (7490.40.109 work)
  • mto: This revision was merged to the branch mainline in revision 7526.
  • Revision ID: jelmer@jelmer.uk-20200902163518-sy9f4unbboljphgu
Handle duplicate directories entries for git.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2011 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Tree classes, representing directory at point in time.
 
18
"""
 
19
 
 
20
from __future__ import absolute_import
 
21
 
 
22
from . import (
 
23
    errors,
 
24
    lock,
 
25
    osutils,
 
26
    revision as _mod_revision,
 
27
    trace,
 
28
    )
 
29
from .inter import InterObject
 
30
from .sixish import (
 
31
    text_type,
 
32
    viewvalues,
 
33
    )
 
34
 
 
35
 
 
36
class FileTimestampUnavailable(errors.BzrError):
 
37
 
 
38
    _fmt = "The filestamp for %(path)s is not available."
 
39
 
 
40
    internal_error = True
 
41
 
 
42
    def __init__(self, path):
 
43
        self.path = path
 
44
 
 
45
 
 
46
class MissingNestedTree(errors.BzrError):
 
47
 
 
48
    _fmt = "The nested tree for %(path)s can not be resolved."""
 
49
 
 
50
    def __init__(self, path):
 
51
        self.path = path
 
52
 
 
53
 
 
54
class TreeEntry(object):
 
55
    """An entry that implements the minimum interface used by commands.
 
56
    """
 
57
 
 
58
    __slots__ = []
 
59
 
 
60
    def __eq__(self, other):
 
61
        # yes, this is ugly, TODO: best practice __eq__ style.
 
62
        return (isinstance(other, TreeEntry)
 
63
                and other.__class__ == self.__class__)
 
64
 
 
65
    kind = None
 
66
 
 
67
    def kind_character(self):
 
68
        return "???"
 
69
 
 
70
    def is_unmodified(self, other):
 
71
        """Does this entry reference the same entry?
 
72
 
 
73
        This is mostly the same as __eq__, but returns False
 
74
        for entries without enough information (i.e. revision is None)
 
75
        """
 
76
        return False
 
77
 
 
78
 
 
79
class TreeDirectory(TreeEntry):
 
80
    """See TreeEntry. This is a directory in a working tree."""
 
81
 
 
82
    __slots__ = []
 
83
 
 
84
    kind = 'directory'
 
85
 
 
86
    def kind_character(self):
 
87
        return "/"
 
88
 
 
89
 
 
90
class TreeFile(TreeEntry):
 
91
    """See TreeEntry. This is a regular file in a working tree."""
 
92
 
 
93
    __slots__ = []
 
94
 
 
95
    kind = 'file'
 
96
 
 
97
    def kind_character(self):
 
98
        return ''
 
99
 
 
100
 
 
101
class TreeLink(TreeEntry):
 
102
    """See TreeEntry. This is a symlink in a working tree."""
 
103
 
 
104
    __slots__ = []
 
105
 
 
106
    kind = 'symlink'
 
107
 
 
108
    def kind_character(self):
 
109
        return ''
 
110
 
 
111
 
 
112
class TreeReference(TreeEntry):
 
113
    """See TreeEntry. This is a reference to a nested tree in a working tree."""
 
114
 
 
115
    __slots__ = []
 
116
 
 
117
    kind = 'tree-reference'
 
118
 
 
119
    def kind_character(self):
 
120
        return '+'
 
121
 
 
122
 
 
123
class TreeChange(object):
 
124
    """Describes the changes between the same item in two different trees."""
 
125
 
 
126
    __slots__ = ['path', 'changed_content', 'versioned',
 
127
                 'name', 'kind', 'executable', 'copied']
 
128
 
 
129
    def __init__(self, path, changed_content, versioned,
 
130
                 name, kind, executable, copied=False):
 
131
        self.path = path
 
132
        self.changed_content = changed_content
 
133
        self.versioned = versioned
 
134
        self.name = name
 
135
        self.kind = kind
 
136
        self.executable = executable
 
137
        self.copied = copied
 
138
 
 
139
    def __repr__(self):
 
140
        return "%s%r" % (self.__class__.__name__, self._as_tuple())
 
141
 
 
142
    def _as_tuple(self):
 
143
        return (self.path, self.changed_content, self.versioned,
 
144
                self.name, self.kind, self.executable, self.copied)
 
145
 
 
146
    def __eq__(self, other):
 
147
        if isinstance(other, TreeChange):
 
148
            return self._as_tuple() == other._as_tuple()
 
149
        if isinstance(other, tuple):
 
150
            return self._as_tuple() == other
 
151
        return False
 
152
 
 
153
    def __lt__(self, other):
 
154
        return self._as_tuple() < other._as_tuple()
 
155
 
 
156
    def meta_modified(self):
 
157
        if self.versioned == (True, True):
 
158
            return (self.executable[0] != self.executable[1])
 
159
        return False
 
160
 
 
161
    @property
 
162
    def renamed(self):
 
163
        return (
 
164
            not self.copied and
 
165
            None not in self.name and
 
166
            self.path[0] != self.path[1])
 
167
 
 
168
    def is_reparented(self):
 
169
        return os.path.dirname(self.path[0]) != os.path.dirname(self.path[1])
 
170
 
 
171
    def discard_new(self):
 
172
        return self.__class__(
 
173
            (self.path[0], None), self.changed_content,
 
174
            (self.versioned[0], None),
 
175
            (self.name[0], None), (self.kind[0], None),
 
176
            (self.executable[0], None),
 
177
            copied=False)
 
178
 
 
179
 
 
180
class Tree(object):
 
181
    """Abstract file tree.
 
182
 
 
183
    There are several subclasses:
 
184
 
 
185
    * `WorkingTree` exists as files on disk editable by the user.
 
186
 
 
187
    * `RevisionTree` is a tree as recorded at some point in the past.
 
188
 
 
189
    Trees can be compared, etc, regardless of whether they are working
 
190
    trees or versioned trees.
 
191
    """
 
192
 
 
193
    def supports_rename_tracking(self):
 
194
        """Whether this tree supports rename tracking.
 
195
 
 
196
        This defaults to True, but some implementations may want to override
 
197
        it.
 
198
        """
 
199
        return True
 
200
 
 
201
    def has_versioned_directories(self):
 
202
        """Whether this tree can contain explicitly versioned directories.
 
203
 
 
204
        This defaults to True, but some implementations may want to override
 
205
        it.
 
206
        """
 
207
        return True
 
208
 
 
209
    def supports_symlinks(self):
 
210
        """Does this tree support symbolic links?
 
211
        """
 
212
        return osutils.has_symlinks()
 
213
 
 
214
    def changes_from(self, other, want_unchanged=False, specific_files=None,
 
215
                     extra_trees=None, require_versioned=False, include_root=False,
 
216
                     want_unversioned=False):
 
217
        """Return a TreeDelta of the changes from other to this tree.
 
218
 
 
219
        :param other: A tree to compare with.
 
220
        :param specific_files: An optional list of file paths to restrict the
 
221
            comparison to. When mapping filenames to ids, all matches in all
 
222
            trees (including optional extra_trees) are used, and all children of
 
223
            matched directories are included.
 
224
        :param want_unchanged: An optional boolean requesting the inclusion of
 
225
            unchanged entries in the result.
 
226
        :param extra_trees: An optional list of additional trees to use when
 
227
            mapping the contents of specific_files (paths) to their identities.
 
228
        :param require_versioned: An optional boolean (defaults to False). When
 
229
            supplied and True all the 'specific_files' must be versioned, or
 
230
            a PathsNotVersionedError will be thrown.
 
231
        :param want_unversioned: Scan for unversioned paths.
 
232
 
 
233
        The comparison will be performed by an InterTree object looked up on
 
234
        self and other.
 
235
        """
 
236
        # Martin observes that Tree.changes_from returns a TreeDelta and this
 
237
        # may confuse people, because the class name of the returned object is
 
238
        # a synonym of the object referenced in the method name.
 
239
        return InterTree.get(other, self).compare(
 
240
            want_unchanged=want_unchanged,
 
241
            specific_files=specific_files,
 
242
            extra_trees=extra_trees,
 
243
            require_versioned=require_versioned,
 
244
            include_root=include_root,
 
245
            want_unversioned=want_unversioned,
 
246
            )
 
247
 
 
248
    def iter_changes(self, from_tree, include_unchanged=False,
 
249
                     specific_files=None, pb=None, extra_trees=None,
 
250
                     require_versioned=True, want_unversioned=False):
 
251
        """See InterTree.iter_changes"""
 
252
        intertree = InterTree.get(from_tree, self)
 
253
        return intertree.iter_changes(include_unchanged, specific_files, pb,
 
254
                                      extra_trees, require_versioned,
 
255
                                      want_unversioned=want_unversioned)
 
256
 
 
257
    def conflicts(self):
 
258
        """Get a list of the conflicts in the tree.
 
259
 
 
260
        Each conflict is an instance of breezy.conflicts.Conflict.
 
261
        """
 
262
        from . import conflicts as _mod_conflicts
 
263
        return _mod_conflicts.ConflictList()
 
264
 
 
265
    def extras(self):
 
266
        """For trees that can have unversioned files, return all such paths."""
 
267
        return []
 
268
 
 
269
    def get_parent_ids(self):
 
270
        """Get the parent ids for this tree.
 
271
 
 
272
        :return: a list of parent ids. [] is returned to indicate
 
273
        a tree with no parents.
 
274
        :raises: BzrError if the parents are not known.
 
275
        """
 
276
        raise NotImplementedError(self.get_parent_ids)
 
277
 
 
278
    def has_filename(self, filename):
 
279
        """True if the tree has given filename."""
 
280
        raise NotImplementedError(self.has_filename)
 
281
 
 
282
    def is_ignored(self, filename):
 
283
        """Check whether the filename is ignored by this tree.
 
284
 
 
285
        :param filename: The relative filename within the tree.
 
286
        :return: True if the filename is ignored.
 
287
        """
 
288
        return False
 
289
 
 
290
    def all_file_ids(self):
 
291
        """Iterate through all file ids, including ids for missing files."""
 
292
        raise NotImplementedError(self.all_file_ids)
 
293
 
 
294
    def all_versioned_paths(self):
 
295
        """Iterate through all paths, including paths for missing files."""
 
296
        raise NotImplementedError(self.all_versioned_paths)
 
297
 
 
298
    def id2path(self, file_id, recurse='down'):
 
299
        """Return the path for a file id.
 
300
 
 
301
        :raises NoSuchId:
 
302
        """
 
303
        raise NotImplementedError(self.id2path)
 
304
 
 
305
    def iter_entries_by_dir(self, specific_files=None, recurse_nested=False):
 
306
        """Walk the tree in 'by_dir' order.
 
307
 
 
308
        This will yield each entry in the tree as a (path, entry) tuple.
 
309
        The order that they are yielded is:
 
310
 
 
311
        Directories are walked in a depth-first lexicographical order,
 
312
        however, whenever a directory is reached, all of its direct child
 
313
        nodes are yielded in  lexicographical order before yielding the
 
314
        grandchildren.
 
315
 
 
316
        For example, in the tree::
 
317
 
 
318
           a/
 
319
             b/
 
320
               c
 
321
             d/
 
322
               e
 
323
           f/
 
324
             g
 
325
 
 
326
        The yield order (ignoring root) would be::
 
327
 
 
328
          a, f, a/b, a/d, a/b/c, a/d/e, f/g
 
329
 
 
330
        If recurse_nested is enabled then nested trees are included as if
 
331
        they were a part of the tree. If is disabled then TreeReference
 
332
        objects (without any children) are yielded.
 
333
        """
 
334
        raise NotImplementedError(self.iter_entries_by_dir)
 
335
 
 
336
    def iter_child_entries(self, path):
 
337
        """Iterate over the children of a directory or tree reference.
 
338
 
 
339
        :param path: Path of the directory
 
340
        :raise NoSuchFile: When the path does not exist
 
341
        :return: Iterator over entries in the directory
 
342
        """
 
343
        raise NotImplementedError(self.iter_child_entries)
 
344
 
 
345
    def list_files(self, include_root=False, from_dir=None, recursive=True,
 
346
                   recurse_nested=False):
 
347
        """List all files in this tree.
 
348
 
 
349
        :param include_root: Whether to include the entry for the tree root
 
350
        :param from_dir: Directory under which to list files
 
351
        :param recursive: Whether to list files recursively
 
352
        :param recurse_nested: enter nested trees
 
353
        :return: iterator over tuples of
 
354
            (path, versioned, kind, inventory entry)
 
355
        """
 
356
        raise NotImplementedError(self.list_files)
 
357
 
 
358
    def iter_references(self):
 
359
        if self.supports_tree_reference():
 
360
            for path, entry in self.iter_entries_by_dir():
 
361
                if entry.kind == 'tree-reference':
 
362
                    yield path
 
363
 
 
364
    def get_containing_nested_tree(self, path):
 
365
        """Find the nested tree that contains a path.
 
366
 
 
367
        :return: tuple with (nested tree and path inside the nested tree)
 
368
        """
 
369
        for nested_path in self.iter_references():
 
370
            nested_path += '/'
 
371
            if path.startswith(nested_path):
 
372
                nested_tree = self.get_nested_tree(nested_path)
 
373
                return nested_tree, path[len(nested_path):]
 
374
        else:
 
375
            return None, None
 
376
 
 
377
    def get_nested_tree(self, path):
 
378
        """Open the nested tree at the specified path.
 
379
 
 
380
        :param path: Path from which to resolve tree reference.
 
381
        :return: A Tree object for the nested tree
 
382
        :raise MissingNestedTree: If the nested tree can not be resolved
 
383
        """
 
384
        raise NotImplementedError(self.get_nested_tree)
 
385
 
 
386
    def kind(self, path):
 
387
        raise NotImplementedError("Tree subclass %s must implement kind"
 
388
                                  % self.__class__.__name__)
 
389
 
 
390
    def stored_kind(self, path):
 
391
        """File kind stored for this path.
 
392
 
 
393
        May not match kind on disk for working trees.  Always available
 
394
        for versioned files, even when the file itself is missing.
 
395
        """
 
396
        return self.kind(path)
 
397
 
 
398
    def path_content_summary(self, path):
 
399
        """Get a summary of the information about path.
 
400
 
 
401
        All the attributes returned are for the canonical form, not the
 
402
        convenient form (if content filters are in use.)
 
403
 
 
404
        :param path: A relative path within the tree.
 
405
        :return: A tuple containing kind, size, exec, sha1-or-link.
 
406
            Kind is always present (see tree.kind()).
 
407
            size is present if kind is file and the size of the
 
408
                canonical form can be cheaply determined, None otherwise.
 
409
            exec is None unless kind is file and the platform supports the 'x'
 
410
                bit.
 
411
            sha1-or-link is the link target if kind is symlink, or the sha1 if
 
412
                it can be obtained without reading the file.
 
413
        """
 
414
        raise NotImplementedError(self.path_content_summary)
 
415
 
 
416
    def get_reference_revision(self, path, branch=None):
 
417
        raise NotImplementedError("Tree subclass %s must implement "
 
418
                                  "get_reference_revision"
 
419
                                  % self.__class__.__name__)
 
420
 
 
421
    def _comparison_data(self, entry, path):
 
422
        """Return a tuple of kind, executable, stat_value for a file.
 
423
 
 
424
        entry may be None if there is no inventory entry for the file, but
 
425
        path must always be supplied.
 
426
 
 
427
        kind is None if there is no file present (even if an inventory id is
 
428
        present).  executable is False for non-file entries.
 
429
        """
 
430
        raise NotImplementedError(self._comparison_data)
 
431
 
 
432
    def get_file(self, path):
 
433
        """Return a file object for the file path in the tree.
 
434
        """
 
435
        raise NotImplementedError(self.get_file)
 
436
 
 
437
    def get_file_with_stat(self, path):
 
438
        """Get a file handle and stat object for path.
 
439
 
 
440
        The default implementation returns (self.get_file, None) for backwards
 
441
        compatibility.
 
442
 
 
443
        :param path: The path of the file.
 
444
        :return: A tuple (file_handle, stat_value_or_None). If the tree has
 
445
            no stat facility, or need for a stat cache feedback during commit,
 
446
            it may return None for the second element of the tuple.
 
447
        """
 
448
        return (self.get_file(path), None)
 
449
 
 
450
    def get_file_text(self, path):
 
451
        """Return the byte content of a file.
 
452
 
 
453
        :param path: The path of the file.
 
454
 
 
455
        :returns: A single byte string for the whole file.
 
456
        """
 
457
        with self.get_file(path) as my_file:
 
458
            return my_file.read()
 
459
 
 
460
    def get_file_lines(self, path):
 
461
        """Return the content of a file, as lines.
 
462
 
 
463
        :param path: The path of the file.
 
464
        """
 
465
        return osutils.split_lines(self.get_file_text(path))
 
466
 
 
467
    def get_file_verifier(self, path, stat_value=None):
 
468
        """Return a verifier for a file.
 
469
 
 
470
        The default implementation returns a sha1.
 
471
 
 
472
        :param path: The path that this file can be found at.
 
473
            These must point to the same object.
 
474
        :param stat_value: Optional stat value for the object
 
475
        :return: Tuple with verifier name and verifier data
 
476
        """
 
477
        return ("SHA1", self.get_file_sha1(path, stat_value=stat_value))
 
478
 
 
479
    def get_file_sha1(self, path, stat_value=None):
 
480
        """Return the SHA1 file for a file.
 
481
 
 
482
        :note: callers should use get_file_verifier instead
 
483
            where possible, as the underlying repository implementation may
 
484
            have quicker access to a non-sha1 verifier.
 
485
 
 
486
        :param path: The path that this file can be found at.
 
487
        :param stat_value: Optional stat value for the object
 
488
        """
 
489
        raise NotImplementedError(self.get_file_sha1)
 
490
 
 
491
    def get_file_mtime(self, path):
 
492
        """Return the modification time for a file.
 
493
 
 
494
        :param path: The path that this file can be found at.
 
495
        """
 
496
        raise NotImplementedError(self.get_file_mtime)
 
497
 
 
498
    def get_file_size(self, path):
 
499
        """Return the size of a file in bytes.
 
500
 
 
501
        This applies only to regular files.  If invoked on directories or
 
502
        symlinks, it will return None.
 
503
        """
 
504
        raise NotImplementedError(self.get_file_size)
 
505
 
 
506
    def is_executable(self, path):
 
507
        """Check if a file is executable.
 
508
 
 
509
        :param path: The path that this file can be found at.
 
510
        """
 
511
        raise NotImplementedError(self.is_executable)
 
512
 
 
513
    def iter_files_bytes(self, desired_files):
 
514
        """Iterate through file contents.
 
515
 
 
516
        Files will not necessarily be returned in the order they occur in
 
517
        desired_files.  No specific order is guaranteed.
 
518
 
 
519
        Yields pairs of identifier, bytes_iterator.  identifier is an opaque
 
520
        value supplied by the caller as part of desired_files.  It should
 
521
        uniquely identify the file version in the caller's context.  (Examples:
 
522
        an index number or a TreeTransform trans_id.)
 
523
 
 
524
        bytes_iterator is an iterable of bytestrings for the file.  The
 
525
        kind of iterable and length of the bytestrings are unspecified, but for
 
526
        this implementation, it is a tuple containing a single bytestring with
 
527
        the complete text of the file.
 
528
 
 
529
        :param desired_files: a list of (path, identifier) pairs
 
530
        """
 
531
        for path, identifier in desired_files:
 
532
            # We wrap the string in a tuple so that we can return an iterable
 
533
            # of bytestrings.  (Technically, a bytestring is also an iterable
 
534
            # of bytestrings, but iterating through each character is not
 
535
            # performant.)
 
536
            cur_file = (self.get_file_text(path),)
 
537
            yield identifier, cur_file
 
538
 
 
539
    def get_symlink_target(self, path):
 
540
        """Get the target for a given path.
 
541
 
 
542
        It is assumed that the caller already knows that path is referencing
 
543
        a symlink.
 
544
        :param path: The path of the file.
 
545
        :return: The path the symlink points to.
 
546
        """
 
547
        raise NotImplementedError(self.get_symlink_target)
 
548
 
 
549
    def annotate_iter(self, path,
 
550
                      default_revision=_mod_revision.CURRENT_REVISION):
 
551
        """Return an iterator of revision_id, line tuples.
 
552
 
 
553
        For working trees (and mutable trees in general), the special
 
554
        revision_id 'current:' will be used for lines that are new in this
 
555
        tree, e.g. uncommitted changes.
 
556
        :param path: The file to produce an annotated version from
 
557
        :param default_revision: For lines that don't match a basis, mark them
 
558
            with this revision id. Not all implementations will make use of
 
559
            this value.
 
560
        """
 
561
        raise NotImplementedError(self.annotate_iter)
 
562
 
 
563
    def path2id(self, path):
 
564
        """Return the id for path in this tree."""
 
565
        raise NotImplementedError(self.path2id)
 
566
 
 
567
    def is_versioned(self, path):
 
568
        """Check whether path is versioned.
 
569
 
 
570
        :param path: Path to check
 
571
        :return: boolean
 
572
        """
 
573
        return self.path2id(path) is not None
 
574
 
 
575
    def find_related_paths_across_trees(self, paths, trees=[],
 
576
                                        require_versioned=True):
 
577
        """Find related paths in tree corresponding to specified filenames in any
 
578
        of `lookup_trees`.
 
579
 
 
580
        All matches in all trees will be used, and all children of matched
 
581
        directories will be used.
 
582
 
 
583
        :param paths: The filenames to find related paths for (if None, returns
 
584
            None)
 
585
        :param trees: The trees to find file_ids within
 
586
        :param require_versioned: if true, all specified filenames must occur in
 
587
            at least one tree.
 
588
        :return: a set of paths for the specified filenames and their children
 
589
            in `tree`
 
590
        """
 
591
        raise NotImplementedError(self.find_related_paths_across_trees)
 
592
 
 
593
    def lock_read(self):
 
594
        """Lock this tree for multiple read only operations.
 
595
 
 
596
        :return: A breezy.lock.LogicalLockResult.
 
597
        """
 
598
        return lock.LogicalLockResult(self.unlock)
 
599
 
 
600
    def revision_tree(self, revision_id):
 
601
        """Obtain a revision tree for the revision revision_id.
 
602
 
 
603
        The intention of this method is to allow access to possibly cached
 
604
        tree data. Implementors of this method should raise NoSuchRevision if
 
605
        the tree is not locally available, even if they could obtain the
 
606
        tree via a repository or some other means. Callers are responsible
 
607
        for finding the ultimate source for a revision tree.
 
608
 
 
609
        :param revision_id: The revision_id of the requested tree.
 
610
        :return: A Tree.
 
611
        :raises: NoSuchRevision if the tree cannot be obtained.
 
612
        """
 
613
        raise errors.NoSuchRevisionInTree(self, revision_id)
 
614
 
 
615
    def unknowns(self):
 
616
        """What files are present in this tree and unknown.
 
617
 
 
618
        :return: an iterator over the unknown files.
 
619
        """
 
620
        return iter([])
 
621
 
 
622
    def unlock(self):
 
623
        pass
 
624
 
 
625
    def filter_unversioned_files(self, paths):
 
626
        """Filter out paths that are versioned.
 
627
 
 
628
        :return: set of paths.
 
629
        """
 
630
        # NB: we specifically *don't* call self.has_filename, because for
 
631
        # WorkingTrees that can indicate files that exist on disk but that
 
632
        # are not versioned.
 
633
        return set(p for p in paths if not self.is_versioned(p))
 
634
 
 
635
    def walkdirs(self, prefix=""):
 
636
        """Walk the contents of this tree from path down.
 
637
 
 
638
        This yields all the data about the contents of a directory at a time.
 
639
        After each directory has been yielded, if the caller has mutated the
 
640
        list to exclude some directories, they are then not descended into.
 
641
 
 
642
        The data yielded is of the form:
 
643
        (directory-relpath,
 
644
        [(relpath, basename, kind, lstat, path_from_tree_root,
 
645
          versioned_kind), ...]),
 
646
         - directory-path-from-root is the containing dirs path from /
 
647
         - relpath is the relative path within the subtree being walked.
 
648
         - basename is the basename
 
649
         - kind is the kind of the file now. If unknonwn then the file is not
 
650
           present within the tree - but it may be recorded as versioned. See
 
651
           versioned_kind.
 
652
         - lstat is the stat data *if* the file was statted.
 
653
         - path_from_tree_root is the path from the root of the tree.
 
654
         - versioned_kind is the kind of the file as last recorded in the
 
655
           versioning system. If 'unknown' the file is not versioned.
 
656
        One of 'kind' and 'versioned_kind' must not be 'unknown'.
 
657
 
 
658
        :param prefix: Start walking from prefix within the tree rather than
 
659
        at the root. This allows one to walk a subtree but get paths that are
 
660
        relative to a tree rooted higher up.
 
661
        :return: an iterator over the directory data.
 
662
        """
 
663
        raise NotImplementedError(self.walkdirs)
 
664
 
 
665
    def supports_content_filtering(self):
 
666
        return False
 
667
 
 
668
    def _content_filter_stack(self, path=None):
 
669
        """The stack of content filters for a path if filtering is supported.
 
670
 
 
671
        Readers will be applied in first-to-last order.
 
672
        Writers will be applied in last-to-first order.
 
673
        Either the path or the file-id needs to be provided.
 
674
 
 
675
        :param path: path relative to the root of the tree
 
676
            or None if unknown
 
677
        :return: the list of filters - [] if there are none
 
678
        """
 
679
        from . import debug, filters
 
680
        filter_pref_names = filters._get_registered_names()
 
681
        if len(filter_pref_names) == 0:
 
682
            return []
 
683
        prefs = next(self.iter_search_rules([path], filter_pref_names))
 
684
        stk = filters._get_filter_stack_for(prefs)
 
685
        if 'filters' in debug.debug_flags:
 
686
            trace.note("*** {0} content-filter: {1} => {2!r}").format(path, prefs, stk)
 
687
        return stk
 
688
 
 
689
    def _content_filter_stack_provider(self):
 
690
        """A function that returns a stack of ContentFilters.
 
691
 
 
692
        The function takes a path (relative to the top of the tree) and a
 
693
        file-id as parameters.
 
694
 
 
695
        :return: None if content filtering is not supported by this tree.
 
696
        """
 
697
        if self.supports_content_filtering():
 
698
            return self._content_filter_stack
 
699
        else:
 
700
            return None
 
701
 
 
702
    def iter_search_rules(self, path_names, pref_names=None,
 
703
                          _default_searcher=None):
 
704
        """Find the preferences for filenames in a tree.
 
705
 
 
706
        :param path_names: an iterable of paths to find attributes for.
 
707
          Paths are given relative to the root of the tree.
 
708
        :param pref_names: the list of preferences to lookup - None for all
 
709
        :param _default_searcher: private parameter to assist testing - don't use
 
710
        :return: an iterator of tuple sequences, one per path-name.
 
711
          See _RulesSearcher.get_items for details on the tuple sequence.
 
712
        """
 
713
        from . import rules
 
714
        if _default_searcher is None:
 
715
            _default_searcher = rules._per_user_searcher
 
716
        searcher = self._get_rules_searcher(_default_searcher)
 
717
        if searcher is not None:
 
718
            if pref_names is not None:
 
719
                for path in path_names:
 
720
                    yield searcher.get_selected_items(path, pref_names)
 
721
            else:
 
722
                for path in path_names:
 
723
                    yield searcher.get_items(path)
 
724
 
 
725
    def _get_rules_searcher(self, default_searcher):
 
726
        """Get the RulesSearcher for this tree given the default one."""
 
727
        searcher = default_searcher
 
728
        return searcher
 
729
 
 
730
    def archive(self, format, name, root='', subdir=None,
 
731
                force_mtime=None):
 
732
        """Create an archive of this tree.
 
733
 
 
734
        :param format: Format name (e.g. 'tar')
 
735
        :param name: target file name
 
736
        :param root: Root directory name (or None)
 
737
        :param subdir: Subdirectory to export (or None)
 
738
        :return: Iterator over archive chunks
 
739
        """
 
740
        from .archive import create_archive
 
741
        with self.lock_read():
 
742
            return create_archive(format, self, name, root,
 
743
                                  subdir, force_mtime=force_mtime)
 
744
 
 
745
    @classmethod
 
746
    def versionable_kind(cls, kind):
 
747
        """Check if this tree support versioning a specific file kind."""
 
748
        return (kind in ('file', 'directory', 'symlink', 'tree-reference'))
 
749
 
 
750
    def preview_transform(self, pb=None):
 
751
        """Obtain a transform object."""
 
752
        raise NotImplementedError(self.preview_transform)
 
753
 
 
754
 
 
755
class InterTree(InterObject):
 
756
    """This class represents operations taking place between two Trees.
 
757
 
 
758
    Its instances have methods like 'compare' and contain references to the
 
759
    source and target trees these operations are to be carried out on.
 
760
 
 
761
    Clients of breezy should not need to use InterTree directly, rather they
 
762
    should use the convenience methods on Tree such as 'Tree.compare()' which
 
763
    will pass through to InterTree as appropriate.
 
764
    """
 
765
 
 
766
    # Formats that will be used to test this InterTree. If both are
 
767
    # None, this InterTree will not be tested (e.g. because a complex
 
768
    # setup is required)
 
769
    _matching_from_tree_format = None
 
770
    _matching_to_tree_format = None
 
771
 
 
772
    _optimisers = []
 
773
 
 
774
    @classmethod
 
775
    def is_compatible(kls, source, target):
 
776
        # The default implementation is naive and uses the public API, so
 
777
        # it works for all trees.
 
778
        return True
 
779
 
 
780
    def compare(self, want_unchanged=False, specific_files=None,
 
781
                extra_trees=None, require_versioned=False, include_root=False,
 
782
                want_unversioned=False):
 
783
        """Return the changes from source to target.
 
784
 
 
785
        :return: A TreeDelta.
 
786
        :param specific_files: An optional list of file paths to restrict the
 
787
            comparison to. When mapping filenames to ids, all matches in all
 
788
            trees (including optional extra_trees) are used, and all children of
 
789
            matched directories are included.
 
790
        :param want_unchanged: An optional boolean requesting the inclusion of
 
791
            unchanged entries in the result.
 
792
        :param extra_trees: An optional list of additional trees to use when
 
793
            mapping the contents of specific_files (paths) to file_ids.
 
794
        :param require_versioned: An optional boolean (defaults to False). When
 
795
            supplied and True all the 'specific_files' must be versioned, or
 
796
            a PathsNotVersionedError will be thrown.
 
797
        :param want_unversioned: Scan for unversioned paths.
 
798
        """
 
799
        from . import delta
 
800
        trees = (self.source,)
 
801
        if extra_trees is not None:
 
802
            trees = trees + tuple(extra_trees)
 
803
        with self.lock_read():
 
804
            return delta._compare_trees(self.source, self.target, want_unchanged,
 
805
                                        specific_files, include_root, extra_trees=extra_trees,
 
806
                                        require_versioned=require_versioned,
 
807
                                        want_unversioned=want_unversioned)
 
808
 
 
809
    def iter_changes(self, include_unchanged=False,
 
810
                     specific_files=None, pb=None, extra_trees=[],
 
811
                     require_versioned=True, want_unversioned=False):
 
812
        """Generate an iterator of changes between trees.
 
813
 
 
814
        A tuple is returned:
 
815
        (file_id, (path_in_source, path_in_target),
 
816
         changed_content, versioned, parent, name, kind,
 
817
         executable)
 
818
 
 
819
        Changed_content is True if the file's content has changed.  This
 
820
        includes changes to its kind, and to a symlink's target.
 
821
 
 
822
        versioned, parent, name, kind, executable are tuples of (from, to).
 
823
        If a file is missing in a tree, its kind is None.
 
824
 
 
825
        Iteration is done in parent-to-child order, relative to the target
 
826
        tree.
 
827
 
 
828
        There is no guarantee that all paths are in sorted order: the
 
829
        requirement to expand the search due to renames may result in children
 
830
        that should be found early being found late in the search, after
 
831
        lexically later results have been returned.
 
832
        :param require_versioned: Raise errors.PathsNotVersionedError if a
 
833
            path in the specific_files list is not versioned in one of
 
834
            source, target or extra_trees.
 
835
        :param specific_files: An optional list of file paths to restrict the
 
836
            comparison to. When mapping filenames to ids, all matches in all
 
837
            trees (including optional extra_trees) are used, and all children
 
838
            of matched directories are included. The parents in the target tree
 
839
            of the specific files up to and including the root of the tree are
 
840
            always evaluated for changes too.
 
841
        :param want_unversioned: Should unversioned files be returned in the
 
842
            output. An unversioned file is defined as one with (False, False)
 
843
            for the versioned pair.
 
844
        """
 
845
        raise NotImplementedError(self.iter_changes)
 
846
 
 
847
    def file_content_matches(
 
848
            self, source_path, target_path,
 
849
            source_stat=None, target_stat=None):
 
850
        """Check if two files are the same in the source and target trees.
 
851
 
 
852
        This only checks that the contents of the files are the same,
 
853
        it does not touch anything else.
 
854
 
 
855
        :param source_path: Path of the file in the source tree
 
856
        :param target_path: Path of the file in the target tree
 
857
        :param source_stat: Optional stat value of the file in the source tree
 
858
        :param target_stat: Optional stat value of the file in the target tree
 
859
        :return: Boolean indicating whether the files have the same contents
 
860
        """
 
861
        with self.lock_read():
 
862
            source_verifier_kind, source_verifier_data = (
 
863
                self.source.get_file_verifier(source_path, source_stat))
 
864
            target_verifier_kind, target_verifier_data = (
 
865
                self.target.get_file_verifier(
 
866
                    target_path, target_stat))
 
867
            if source_verifier_kind == target_verifier_kind:
 
868
                return (source_verifier_data == target_verifier_data)
 
869
            # Fall back to SHA1 for now
 
870
            if source_verifier_kind != "SHA1":
 
871
                source_sha1 = self.source.get_file_sha1(
 
872
                    source_path, source_stat)
 
873
            else:
 
874
                source_sha1 = source_verifier_data
 
875
            if target_verifier_kind != "SHA1":
 
876
                target_sha1 = self.target.get_file_sha1(
 
877
                    target_path, target_stat)
 
878
            else:
 
879
                target_sha1 = target_verifier_data
 
880
            return (source_sha1 == target_sha1)
 
881
 
 
882
    def find_target_path(self, path, recurse='none'):
 
883
        """Find target tree path.
 
884
 
 
885
        :param path: Path to search for (exists in source)
 
886
        :return: path in target, or None if there is no equivalent path.
 
887
        :raise NoSuchFile: If the path doesn't exist in source
 
888
        """
 
889
        raise NotImplementedError(self.find_target_path)
 
890
 
 
891
    def find_source_path(self, path, recurse='none'):
 
892
        """Find the source tree path.
 
893
 
 
894
        :param path: Path to search for (exists in target)
 
895
        :return: path in source, or None if there is no equivalent path.
 
896
        :raise NoSuchFile: if the path doesn't exist in target
 
897
        """
 
898
        raise NotImplementedError(self.find_source_path)
 
899
 
 
900
    def find_target_paths(self, paths, recurse='none'):
 
901
        """Find target tree paths.
 
902
 
 
903
        :param paths: Iterable over paths in target to search for
 
904
        :return: Dictionary mapping from source paths to paths in target , or
 
905
            None if there is no equivalent path.
 
906
        """
 
907
        ret = {}
 
908
        for path in paths:
 
909
            ret[path] = self.find_target_path(path, recurse=recurse)
 
910
        return ret
 
911
 
 
912
    def find_source_paths(self, paths, recurse='none'):
 
913
        """Find source tree paths.
 
914
 
 
915
        :param paths: Iterable over paths in target to search for
 
916
        :return: Dictionary mapping from target paths to paths in source, or
 
917
            None if there is no equivalent path.
 
918
        """
 
919
        ret = {}
 
920
        for path in paths:
 
921
            ret[path] = self.find_source_path(path, recurse=recurse)
 
922
        return ret
 
923
 
 
924
 
 
925
def find_previous_paths(from_tree, to_tree, paths, recurse='none'):
 
926
    """Find previous tree paths.
 
927
 
 
928
    :param from_tree: From tree
 
929
    :param to_tree: To tree
 
930
    :param paths: Iterable over paths in from_tree to search for
 
931
    :return: Dictionary mapping from from_tree paths to paths in to_tree, or
 
932
        None if there is no equivalent path.
 
933
    """
 
934
    return InterTree.get(to_tree, from_tree).find_source_paths(paths, recurse=recurse)
 
935
 
 
936
 
 
937
def find_previous_path(from_tree, to_tree, path, recurse='none'):
 
938
    """Find previous tree path.
 
939
 
 
940
    :param from_tree: From tree
 
941
    :param to_tree: To tree
 
942
    :param path: Path to search for (exists in from_tree)
 
943
    :return: path in to_tree, or None if there is no equivalent path.
 
944
    :raise NoSuchFile: If the path doesn't exist in from_tree
 
945
    """
 
946
    return InterTree.get(to_tree, from_tree).find_source_path(
 
947
        path, recurse=recurse)
 
948
 
 
949
 
 
950
def get_canonical_path(tree, path, normalize):
 
951
    """Find the canonical path of an item, ignoring case.
 
952
 
 
953
    :param tree: Tree to traverse
 
954
    :param path: Case-insensitive path to look up
 
955
    :param normalize: Function to normalize a filename for comparison
 
956
    :return: The canonical path
 
957
    """
 
958
    # go walkin...
 
959
    cur_path = ''
 
960
    bit_iter = iter(path.split("/"))
 
961
    for elt in bit_iter:
 
962
        lelt = normalize(elt)
 
963
        new_path = None
 
964
        try:
 
965
            for child in tree.iter_child_entries(cur_path):
 
966
                try:
 
967
                    if child.name == elt:
 
968
                        # if we found an exact match, we can stop now; if
 
969
                        # we found an approximate match we need to keep
 
970
                        # searching because there might be an exact match
 
971
                        # later.
 
972
                        new_path = osutils.pathjoin(cur_path, child.name)
 
973
                        break
 
974
                    elif normalize(child.name) == lelt:
 
975
                        new_path = osutils.pathjoin(cur_path, child.name)
 
976
                except errors.NoSuchId:
 
977
                    # before a change is committed we can see this error...
 
978
                    continue
 
979
        except errors.NotADirectory:
 
980
            pass
 
981
        if new_path:
 
982
            cur_path = new_path
 
983
        else:
 
984
            # got to the end of this directory and no entries matched.
 
985
            # Return what matched so far, plus the rest as specified.
 
986
            cur_path = osutils.pathjoin(cur_path, elt, *list(bit_iter))
 
987
            break
 
988
    return cur_path