/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-07-18 23:14:00 UTC
  • mfrom: (7490.40.62 work)
  • mto: This revision was merged to the branch mainline in revision 7519.
  • Revision ID: jelmer@jelmer.uk-20200718231400-jaes9qltn8oi8xss
Merge lp:brz/3.1.

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