/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: 2018-11-16 19:47:19 UTC
  • mfrom: (7178 work)
  • mto: This revision was merged to the branch mainline in revision 7179.
  • Revision ID: jelmer@jelmer.uk-20181116194719-m5ut2wfuze5x9s1p
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
from __future__ import absolute_import
21
21
 
22
 
import os
23
 
 
24
22
from .lazy_import import lazy_import
25
23
lazy_import(globals(), """
26
24
import collections
97
95
    """See TreeEntry. This is a reference to a nested tree in a working tree."""
98
96
 
99
97
    def kind_character(self):
100
 
        return '/'
 
98
        return '+'
101
99
 
102
100
 
103
101
class Tree(object):
130
128
        return True
131
129
 
132
130
    def changes_from(self, other, want_unchanged=False, specific_files=None,
133
 
        extra_trees=None, require_versioned=False, include_root=False,
134
 
        want_unversioned=False):
 
131
                     extra_trees=None, require_versioned=False, include_root=False,
 
132
                     want_unversioned=False):
135
133
        """Return a TreeDelta of the changes from other to this tree.
136
134
 
137
135
        :param other: A tree to compare with.
142
140
        :param want_unchanged: An optional boolean requesting the inclusion of
143
141
            unchanged entries in the result.
144
142
        :param extra_trees: An optional list of additional trees to use when
145
 
            mapping the contents of specific_files (paths) to file_ids.
 
143
            mapping the contents of specific_files (paths) to their identities.
146
144
        :param require_versioned: An optional boolean (defaults to False). When
147
145
            supplied and True all the 'specific_files' must be versioned, or
148
146
            a PathsNotVersionedError will be thrown.
169
167
        """See InterTree.iter_changes"""
170
168
        intertree = InterTree.get(from_tree, self)
171
169
        return intertree.iter_changes(include_unchanged, specific_files, pb,
172
 
            extra_trees, require_versioned, want_unversioned=want_unversioned)
 
170
                                      extra_trees, require_versioned, want_unversioned=want_unversioned)
173
171
 
174
172
    def conflicts(self):
175
173
        """Get a list of the conflicts in the tree.
245
243
        """
246
244
        raise NotImplementedError(self.iter_entries_by_dir)
247
245
 
248
 
    def iter_child_entries(self, path, file_id=None):
 
246
    def iter_child_entries(self, path):
249
247
        """Iterate over the children of a directory or tree reference.
250
248
 
251
249
        :param path: Path of the directory
252
 
        :param file_id: Optional file id of the directory/tree-reference
253
 
        :raise NoSuchId: When the file_id does not exist
 
250
        :raise NoSuchFile: When the path does not exist
254
251
        :return: Iterator over entries in the directory
255
252
        """
256
253
        raise NotImplementedError(self.iter_child_entries)
272
269
                if entry.kind == 'tree-reference':
273
270
                    yield path, entry.file_id
274
271
 
275
 
    def kind(self, path, file_id=None):
 
272
    def kind(self, path):
276
273
        raise NotImplementedError("Tree subclass %s must implement kind"
277
 
            % self.__class__.__name__)
 
274
                                  % self.__class__.__name__)
278
275
 
279
 
    def stored_kind(self, path, file_id=None):
280
 
        """File kind stored for this file_id.
 
276
    def stored_kind(self, path):
 
277
        """File kind stored for this path.
281
278
 
282
279
        May not match kind on disk for working trees.  Always available
283
280
        for versioned files, even when the file itself is missing.
284
281
        """
285
 
        return self.kind(path, file_id)
 
282
        return self.kind(path)
286
283
 
287
284
    def path_content_summary(self, path):
288
285
        """Get a summary of the information about path.
302
299
        """
303
300
        raise NotImplementedError(self.path_content_summary)
304
301
 
305
 
    def get_reference_revision(self, path, file_id=None):
 
302
    def get_reference_revision(self, path):
306
303
        raise NotImplementedError("Tree subclass %s must implement "
307
304
                                  "get_reference_revision"
308
 
            % self.__class__.__name__)
 
305
                                  % self.__class__.__name__)
309
306
 
310
307
    def _comparison_data(self, entry, path):
311
308
        """Return a tuple of kind, executable, stat_value for a file.
318
315
        """
319
316
        raise NotImplementedError(self._comparison_data)
320
317
 
321
 
    def get_file(self, path, file_id=None):
322
 
        """Return a file object for the file file_id in the tree.
323
 
 
324
 
        If both file_id and path are defined, it is implementation defined as
325
 
        to which one is used.
 
318
    def get_file(self, path):
 
319
        """Return a file object for the file path in the tree.
326
320
        """
327
321
        raise NotImplementedError(self.get_file)
328
322
 
329
 
    def get_file_with_stat(self, path, file_id=None):
330
 
        """Get a file handle and stat object for file_id.
 
323
    def get_file_with_stat(self, path):
 
324
        """Get a file handle and stat object for path.
331
325
 
332
326
        The default implementation returns (self.get_file, None) for backwards
333
327
        compatibility.
334
328
 
335
329
        :param path: The path of the file.
336
 
        :param file_id: The file id to read, if it is known.
337
330
        :return: A tuple (file_handle, stat_value_or_None). If the tree has
338
331
            no stat facility, or need for a stat cache feedback during commit,
339
332
            it may return None for the second element of the tuple.
340
333
        """
341
 
        return (self.get_file(path, file_id), None)
 
334
        return (self.get_file(path), None)
342
335
 
343
 
    def get_file_text(self, path, file_id=None):
 
336
    def get_file_text(self, path):
344
337
        """Return the byte content of a file.
345
338
 
346
339
        :param path: The path of the file.
347
 
        :param file_id: The file_id of the file.
348
 
 
349
 
        If both file_id and path are supplied, an implementation may use
350
 
        either one.
351
340
 
352
341
        :returns: A single byte string for the whole file.
353
342
        """
354
 
        with self.get_file(path, file_id) as my_file:
 
343
        with self.get_file(path) as my_file:
355
344
            return my_file.read()
356
345
 
357
 
    def get_file_lines(self, path, file_id=None):
 
346
    def get_file_lines(self, path):
358
347
        """Return the content of a file, as lines.
359
348
 
360
349
        :param path: The path of the file.
361
 
        :param file_id: The file_id of the file.
362
 
 
363
 
        If both file_id and path are supplied, an implementation may use
364
 
        either one.
365
350
        """
366
 
        return osutils.split_lines(self.get_file_text(path, file_id))
 
351
        return osutils.split_lines(self.get_file_text(path))
367
352
 
368
 
    def get_file_verifier(self, path, file_id=None, stat_value=None):
 
353
    def get_file_verifier(self, path, stat_value=None):
369
354
        """Return a verifier for a file.
370
355
 
371
356
        The default implementation returns a sha1.
372
357
 
373
 
        :param file_id: The handle for this file.
374
358
        :param path: The path that this file can be found at.
375
359
            These must point to the same object.
376
360
        :param stat_value: Optional stat value for the object
377
361
        :return: Tuple with verifier name and verifier data
378
362
        """
379
 
        return ("SHA1", self.get_file_sha1(path, file_id,
380
 
            stat_value=stat_value))
 
363
        return ("SHA1", self.get_file_sha1(path, stat_value=stat_value))
381
364
 
382
 
    def get_file_sha1(self, path, file_id=None, stat_value=None):
 
365
    def get_file_sha1(self, path, stat_value=None):
383
366
        """Return the SHA1 file for a file.
384
367
 
385
368
        :note: callers should use get_file_verifier instead
387
370
            have quicker access to a non-sha1 verifier.
388
371
 
389
372
        :param path: The path that this file can be found at.
390
 
        :param file_id: The handle for this file.
391
 
            These must point to the same object.
392
373
        :param stat_value: Optional stat value for the object
393
374
        """
394
375
        raise NotImplementedError(self.get_file_sha1)
395
376
 
396
 
    def get_file_mtime(self, path, file_id=None):
 
377
    def get_file_mtime(self, path):
397
378
        """Return the modification time for a file.
398
379
 
399
380
        :param path: The path that this file can be found at.
400
 
        :param file_id: The handle for this file.
401
 
            These must point to the same object.
402
381
        """
403
382
        raise NotImplementedError(self.get_file_mtime)
404
383
 
405
 
    def get_file_size(self, path, file_id=None):
 
384
    def get_file_size(self, path):
406
385
        """Return the size of a file in bytes.
407
386
 
408
387
        This applies only to regular files.  If invoked on directories or
409
388
        symlinks, it will return None.
410
 
        :param file_id: The file-id of the file
411
389
        """
412
390
        raise NotImplementedError(self.get_file_size)
413
391
 
414
 
    def is_executable(self, path, file_id=None):
 
392
    def is_executable(self, path):
415
393
        """Check if a file is executable.
416
394
 
417
395
        :param path: The path that this file can be found at.
418
 
        :param file_id: The handle for this file.
419
 
            These must point to the same object.
420
396
        """
421
397
        raise NotImplementedError(self.is_executable)
422
398
 
446
422
            cur_file = (self.get_file_text(path),)
447
423
            yield identifier, cur_file
448
424
 
449
 
    def get_symlink_target(self, path, file_id=None):
450
 
        """Get the target for a given file_id.
 
425
    def get_symlink_target(self, path):
 
426
        """Get the target for a given path.
451
427
 
452
 
        It is assumed that the caller already knows that file_id is referencing
 
428
        It is assumed that the caller already knows that path is referencing
453
429
        a symlink.
454
 
        :param file_id: Handle for the symlink entry.
455
430
        :param path: The path of the file.
456
 
        If both file_id and path are supplied, an implementation may use
457
 
        either one.
458
431
        :return: The path the symlink points to.
459
432
        """
460
433
        raise NotImplementedError(self.get_symlink_target)
463
436
        """Return the file_id for the root of this tree."""
464
437
        raise NotImplementedError(self.get_root_id)
465
438
 
466
 
    def annotate_iter(self, path, file_id=None,
 
439
    def annotate_iter(self, path,
467
440
                      default_revision=_mod_revision.CURRENT_REVISION):
468
441
        """Return an iterator of revision_id, line tuples.
469
442
 
470
443
        For working trees (and mutable trees in general), the special
471
444
        revision_id 'current:' will be used for lines that are new in this
472
445
        tree, e.g. uncommitted changes.
473
 
        :param file_id: The file to produce an annotated version from
 
446
        :param path: The file to produce an annotated version from
474
447
        :param default_revision: For lines that don't match a basis, mark them
475
448
            with this revision id. Not all implementations will make use of
476
449
            this value.
498
471
        return self.path2id(path) is not None
499
472
 
500
473
    def find_related_paths_across_trees(self, paths, trees=[],
501
 
            require_versioned=True):
 
474
                                        require_versioned=True):
502
475
        """Find related paths in tree corresponding to specified filenames in any
503
476
        of `lookup_trees`.
504
477
 
610
583
        prefs = next(self.iter_search_rules([path], filter_pref_names))
611
584
        stk = filters._get_filter_stack_for(prefs)
612
585
        if 'filters' in debug.debug_flags:
613
 
            trace.note(gettext("*** {0} content-filter: {1} => {2!r}").format(path, prefs, stk))
 
586
            trace.note(
 
587
                gettext("*** {0} content-filter: {1} => {2!r}").format(path, prefs, stk))
614
588
        return stk
615
589
 
616
590
    def _content_filter_stack_provider(self):
623
597
        """
624
598
        if self.supports_content_filtering():
625
599
            return lambda path, file_id: \
626
 
                    self._content_filter_stack(path)
 
600
                self._content_filter_stack(path)
627
601
        else:
628
602
            return None
629
603
 
630
604
    def iter_search_rules(self, path_names, pref_names=None,
631
 
        _default_searcher=None):
 
605
                          _default_searcher=None):
632
606
        """Find the preferences for filenames in a tree.
633
607
 
634
608
        :param path_names: an iterable of paths to find attributes for.
667
641
        from .archive import create_archive
668
642
        with self.lock_read():
669
643
            return create_archive(format, self, name, root,
670
 
                    subdir, force_mtime=force_mtime)
 
644
                                  subdir, force_mtime=force_mtime)
671
645
 
672
646
    @classmethod
673
647
    def versionable_kind(cls, kind):
749
723
        elif source_kind == 'file':
750
724
            if not self.file_content_matches(
751
725
                    source_path, target_path,
752
 
                    file_id, file_id, source_stat, target_stat):
 
726
                    source_stat, target_stat):
753
727
                changed_content = True
754
728
        elif source_kind == 'symlink':
755
 
            if (self.source.get_symlink_target(source_path, file_id) !=
756
 
                self.target.get_symlink_target(target_path, file_id)):
 
729
            if (self.source.get_symlink_target(source_path) !=
 
730
                    self.target.get_symlink_target(target_path)):
757
731
                changed_content = True
758
732
        elif source_kind == 'tree-reference':
759
 
            if (self.source.get_reference_revision(source_path, file_id)
760
 
                != self.target.get_reference_revision(target_path, file_id)):
 
733
            if (self.source.get_reference_revision(source_path)
 
734
                    != self.target.get_reference_revision(target_path)):
761
735
                    changed_content = True
762
736
        parent = (source_parent, target_parent)
763
737
        name = (source_name, target_name)
764
738
        executable = (source_executable, target_executable)
765
 
        if (changed_content is not False or versioned[0] != versioned[1]
766
 
            or parent[0] != parent[1] or name[0] != name[1] or
767
 
            executable[0] != executable[1]):
 
739
        if (changed_content is not False or versioned[0] != versioned[1] or
 
740
            parent[0] != parent[1] or name[0] != name[1] or
 
741
                executable[0] != executable[1]):
768
742
            changes = True
769
743
        else:
770
744
            changes = False
772
746
                versioned, parent, name, kind, executable), changes
773
747
 
774
748
    def compare(self, want_unchanged=False, specific_files=None,
775
 
        extra_trees=None, require_versioned=False, include_root=False,
776
 
        want_unversioned=False):
 
749
                extra_trees=None, require_versioned=False, include_root=False,
 
750
                want_unversioned=False):
777
751
        """Return the changes from source to target.
778
752
 
779
753
        :return: A TreeDelta.
795
769
            trees = trees + tuple(extra_trees)
796
770
        with self.lock_read():
797
771
            return delta._compare_trees(self.source, self.target, want_unchanged,
798
 
                specific_files, include_root, extra_trees=extra_trees,
799
 
                require_versioned=require_versioned,
800
 
                want_unversioned=want_unversioned)
 
772
                                        specific_files, include_root, extra_trees=extra_trees,
 
773
                                        require_versioned=require_versioned,
 
774
                                        want_unversioned=want_unversioned)
801
775
 
802
776
    def iter_changes(self, include_unchanged=False,
803
 
                      specific_files=None, pb=None, extra_trees=[],
804
 
                      require_versioned=True, want_unversioned=False):
 
777
                     specific_files=None, pb=None, extra_trees=[],
 
778
                     require_versioned=True, want_unversioned=False):
805
779
        """Generate an iterator of changes between trees.
806
780
 
807
781
        A tuple is returned:
836
810
            for the versioned pair.
837
811
        """
838
812
        if not extra_trees:
839
 
             extra_trees = []
 
813
            extra_trees = []
840
814
        else:
841
 
             extra_trees = list(extra_trees)
 
815
            extra_trees = list(extra_trees)
842
816
        # The ids of items we need to examine to insure delta consistency.
843
817
        precise_file_ids = set()
844
818
        changed_file_ids = []
847
821
            source_specific_files = []
848
822
        else:
849
823
            target_specific_files = self.target.find_related_paths_across_trees(
850
 
                    specific_files, [self.source] + extra_trees,
851
 
                    require_versioned=require_versioned)
 
824
                specific_files, [self.source] + extra_trees,
 
825
                require_versioned=require_versioned)
852
826
            source_specific_files = self.source.find_related_paths_across_trees(
853
 
                    specific_files, [self.target] + extra_trees,
854
 
                    require_versioned=require_versioned)
 
827
                specific_files, [self.target] + extra_trees,
 
828
                require_versioned=require_versioned)
855
829
        if specific_files is not None:
856
830
            # reparented or added entries must have their parents included
857
831
            # so that valid deltas can be created. The seen_parents set
863
837
            seen_dirs = set()
864
838
        if want_unversioned:
865
839
            all_unversioned = sorted([(p.split('/'), p) for p in
866
 
                                     self.target.extras()
867
 
                if specific_files is None or
868
 
                    osutils.is_inside_any(specific_files, p)])
 
840
                                      self.target.extras()
 
841
                                      if specific_files is None or
 
842
                                      osutils.is_inside_any(specific_files, p)])
869
843
            all_unversioned = collections.deque(all_unversioned)
870
844
        else:
871
845
            all_unversioned = collections.deque()
883
857
        fake_entry = TreeFile()
884
858
        for target_path, target_entry in to_entries_by_dir:
885
859
            while (all_unversioned and
886
 
                all_unversioned[0][0] < target_path.split('/')):
 
860
                   all_unversioned[0][0] < target_path.split('/')):
887
861
                unversioned_path = all_unversioned.popleft()
888
862
                target_kind, target_executable, target_stat = \
889
 
                    self.target._comparison_data(fake_entry, unversioned_path[1])
 
863
                    self.target._comparison_data(
 
864
                        fake_entry, unversioned_path[1])
890
865
                yield (None, (None, unversioned_path[1]), True, (False, False),
891
 
                    (None, None),
892
 
                    (None, unversioned_path[0][-1]),
893
 
                    (None, target_kind),
894
 
                    (None, target_executable))
 
866
                       (None, None),
 
867
                       (None, unversioned_path[0][-1]),
 
868
                       (None, target_kind),
 
869
                       (None, target_executable))
895
870
            source_path, source_entry = from_data.get(target_entry.file_id,
896
 
                (None, None))
 
871
                                                      (None, None))
897
872
            result, changes = self._changes_from_entries(source_entry,
898
 
                target_entry, source_path=source_path, target_path=target_path)
 
873
                                                         target_entry, source_path=source_path, target_path=target_path)
899
874
            to_paths[result[0]] = result[1][1]
900
875
            entry_count += 1
901
876
            if result[3][0]:
924
899
            to_kind, to_executable, to_stat = \
925
900
                self.target._comparison_data(fake_entry, unversioned_path[1])
926
901
            yield (None, (None, unversioned_path[1]), True, (False, False),
927
 
                (None, None),
928
 
                (None, unversioned_path[0][-1]),
929
 
                (None, to_kind),
930
 
                (None, to_executable))
 
902
                   (None, None),
 
903
                   (None, unversioned_path[0][-1]),
 
904
                   (None, to_kind),
 
905
                   (None, to_executable))
931
906
        # Yield all remaining source paths
932
907
        for path, from_entry in from_entries_by_dir:
933
908
            file_id = from_entry.file_id
953
928
        changed_file_ids = set(changed_file_ids)
954
929
        if specific_files is not None:
955
930
            for result in self._handle_precise_ids(precise_file_ids,
956
 
                changed_file_ids):
 
931
                                                   changed_file_ids):
957
932
                yield result
958
933
 
959
934
    def _get_entry(self, tree, path):
964
939
        desired.
965
940
 
966
941
        :param tree: The tree to lookup the entry in.
967
 
        :param file_id: The file_id to lookup.
 
942
        :param path: The path to look up
968
943
        """
969
944
        # No inventory available.
970
945
        try:
974
949
            return None
975
950
 
976
951
    def _handle_precise_ids(self, precise_file_ids, changed_file_ids,
977
 
        discarded_changes=None):
 
952
                            discarded_changes=None):
978
953
        """Fill out a partial iter_changes to be consistent.
979
954
 
980
955
        :param precise_file_ids: The file ids of parents that were seen during
1029
1004
                        source_path = None
1030
1005
                        source_entry = None
1031
1006
                    else:
1032
 
                        source_entry = self._get_entry(self.source, source_path)
 
1007
                        source_entry = self._get_entry(
 
1008
                            self.source, source_path)
1033
1009
                    try:
1034
1010
                        target_path = self.target.id2path(file_id)
1035
1011
                    except errors.NoSuchId:
1036
1012
                        target_path = None
1037
1013
                        target_entry = None
1038
1014
                    else:
1039
 
                        target_entry = self._get_entry(self.target, target_path)
 
1015
                        target_entry = self._get_entry(
 
1016
                            self.target, target_path)
1040
1017
                    result, changes = self._changes_from_entries(
1041
1018
                        source_entry, target_entry, source_path, target_path)
1042
1019
                else:
1051
1028
                        # to be included.
1052
1029
                        if source_entry is None:
1053
1030
                            # Reusing a discarded change.
1054
 
                            source_entry = self._get_entry(self.source, result[1][0])
 
1031
                            source_entry = self._get_entry(
 
1032
                                self.source, result[1][0])
1055
1033
                        precise_file_ids.update(
1056
 
                                child.file_id
1057
 
                                for child in self.source.iter_child_entries(result[1][0]))
 
1034
                            child.file_id
 
1035
                            for child in self.source.iter_child_entries(result[1][0]))
1058
1036
                    changed_file_ids.add(result[0])
1059
1037
                    yield result
1060
1038
 
1061
1039
    def file_content_matches(
1062
1040
            self, source_path, target_path,
1063
 
            source_file_id=None, target_file_id=None,
1064
1041
            source_stat=None, target_stat=None):
1065
1042
        """Check if two files are the same in the source and target trees.
1066
1043
 
1077
1054
        """
1078
1055
        with self.lock_read():
1079
1056
            source_verifier_kind, source_verifier_data = (
1080
 
                    self.source.get_file_verifier(
1081
 
                        source_path, source_file_id, source_stat))
 
1057
                self.source.get_file_verifier(source_path, source_stat))
1082
1058
            target_verifier_kind, target_verifier_data = (
1083
1059
                self.target.get_file_verifier(
1084
 
                    target_path, target_file_id, target_stat))
 
1060
                    target_path, target_stat))
1085
1061
            if source_verifier_kind == target_verifier_kind:
1086
1062
                return (source_verifier_data == target_verifier_data)
1087
1063
            # Fall back to SHA1 for now
1088
1064
            if source_verifier_kind != "SHA1":
1089
1065
                source_sha1 = self.source.get_file_sha1(
1090
 
                        source_path, source_file_id, source_stat)
 
1066
                    source_path, source_file_id, source_stat)
1091
1067
            else:
1092
1068
                source_sha1 = source_verifier_data
1093
1069
            if target_verifier_kind != "SHA1":
1094
1070
                target_sha1 = self.target.get_file_sha1(
1095
 
                        target_path, target_file_id, target_stat)
 
1071
                    target_path, target_file_id, target_stat)
1096
1072
            else:
1097
1073
                target_sha1 = target_verifier_data
1098
1074
            return (source_sha1 == target_sha1)
1099
1075
 
 
1076
 
1100
1077
InterTree.register_optimiser(InterTree)
1101
1078
 
1102
1079
 
1324
1301
                other_extra.pop(file_id)
1325
1302
                other_values = [(None, None)] * idx
1326
1303
                other_values.append((other_path, other_ie))
1327
 
                for alt_idx, alt_extra in enumerate(self._others_extra[idx+1:]):
 
1304
                for alt_idx, alt_extra in enumerate(self._others_extra[idx + 1:]):
1328
1305
                    alt_idx = alt_idx + idx + 1
1329
1306
                    alt_extra = self._others_extra[alt_idx]
1330
1307
                    alt_tree = self._other_trees[alt_idx]
1331
1308
                    other_values.append(self._lookup_by_file_id(
1332
 
                                            alt_extra, alt_tree, file_id))
 
1309
                        alt_extra, alt_tree, file_id))
1333
1310
                yield other_path, file_id, None, other_values
1334
1311
 
1335
1312