~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Jelmer Vernooij
  • Date: 2016-04-03 16:32:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6617.
  • Revision ID: jelmer@jelmer.uk-20160403163231-h72bo0uyek2gikw0
Don't put French text in doc/en/user-reference when LANGUAGE=fr_CH.UTF_8.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Tree classes, representing directory at point in time.
18
18
"""
19
19
 
 
20
from __future__ import absolute_import
 
21
 
20
22
import os
21
23
 
22
24
from bzrlib.lazy_import import lazy_import
35
37
    rules,
36
38
    trace,
37
39
    )
 
40
from bzrlib.i18n import gettext
38
41
""")
39
42
 
40
43
from bzrlib.decorators import needs_read_lock
58
61
    trees or versioned trees.
59
62
    """
60
63
 
 
64
    def has_versioned_directories(self):
 
65
        """Whether this tree can contain explicitly versioned directories.
 
66
 
 
67
        This defaults to True, but some implementations may want to override
 
68
        it.
 
69
        """
 
70
        return True
 
71
 
61
72
    def changes_from(self, other, want_unchanged=False, specific_files=None,
62
73
        extra_trees=None, require_versioned=False, include_root=False,
63
74
        want_unversioned=False):
184
195
        """
185
196
        raise NotImplementedError(self.iter_entries_by_dir)
186
197
 
 
198
    def iter_child_entries(self, file_id, path=None):
 
199
        """Iterate over the children of a directory or tree reference.
 
200
 
 
201
        :param file_id: File id of the directory/tree-reference
 
202
        :param path: Optional path of the directory
 
203
        :raise NoSuchId: When the file_id does not exist
 
204
        :return: Iterator over entries in the directory
 
205
        """
 
206
        raise NotImplementedError(self.iter_child_entries)
 
207
 
187
208
    def list_files(self, include_root=False, from_dir=None, recursive=True):
188
209
        """List all files in this tree.
189
210
 
277
298
 
278
299
        :param file_id: The file_id of the file.
279
300
        :param path: The path of the file.
 
301
 
280
302
        If both file_id and path are supplied, an implementation may use
281
303
        either one.
 
304
 
 
305
        :returns: A single byte string for the whole file.
282
306
        """
283
307
        my_file = self.get_file(file_id, path)
284
308
        try:
297
321
        """
298
322
        return osutils.split_lines(self.get_file_text(file_id, path))
299
323
 
 
324
    def get_file_verifier(self, file_id, path=None, stat_value=None):
 
325
        """Return a verifier for a file.
 
326
 
 
327
        The default implementation returns a sha1.
 
328
 
 
329
        :param file_id: The handle for this file.
 
330
        :param path: The path that this file can be found at.
 
331
            These must point to the same object.
 
332
        :param stat_value: Optional stat value for the object
 
333
        :return: Tuple with verifier name and verifier data
 
334
        """
 
335
        return ("SHA1", self.get_file_sha1(file_id, path=path,
 
336
            stat_value=stat_value))
 
337
 
300
338
    def get_file_sha1(self, file_id, path=None, stat_value=None):
301
339
        """Return the SHA1 file for a file.
302
340
 
 
341
        :note: callers should use get_file_verifier instead
 
342
            where possible, as the underlying repository implementation may
 
343
            have quicker access to a non-sha1 verifier.
 
344
 
303
345
        :param file_id: The handle for this file.
304
346
        :param path: The path that this file can be found at.
305
347
            These must point to the same object.
325
367
        """
326
368
        raise NotImplementedError(self.get_file_size)
327
369
 
328
 
    def get_file_by_path(self, path):
329
 
        raise NotImplementedError(self.get_file_by_path)
330
 
 
331
370
    def is_executable(self, file_id, path=None):
332
371
        """Check if a file is executable.
333
372
 
503
542
        return find_ids_across_trees(paths, [self] + list(trees), require_versioned)
504
543
 
505
544
    def iter_children(self, file_id):
506
 
        entry = self.iter_entries_by_dir([file_id]).next()[1]
507
 
        for child in getattr(entry, 'children', {}).itervalues():
508
 
            yield child.file_id
 
545
        """Iterate over the file ids of the children of an entry.
 
546
 
 
547
        :param file_id: File id of the entry
 
548
        :return: Iterator over child file ids.
 
549
        """
 
550
        raise NotImplementedError(self.iter_children)
509
551
 
510
552
    def lock_read(self):
511
553
        """Lock this tree for multiple read only operations.
512
 
        
 
554
 
513
555
        :return: A bzrlib.lock.LogicalLockResult.
514
556
        """
515
557
        pass
602
644
        prefs = self.iter_search_rules([path], filter_pref_names).next()
603
645
        stk = filters._get_filter_stack_for(prefs)
604
646
        if 'filters' in debug.debug_flags:
605
 
            trace.note("*** %s content-filter: %s => %r" % (path,prefs,stk))
 
647
            trace.note(gettext("*** {0} content-filter: {1} => {2!r}").format(path,prefs,stk))
606
648
        return stk
607
649
 
608
650
    def _content_filter_stack_provider(self):
739
781
            yield cur_path
740
782
        # all done.
741
783
 
 
784
    @deprecated_method(deprecated_in((2, 5, 0)))
742
785
    def _get_inventory(self):
743
786
        return self._inventory
744
787
 
745
788
    inventory = property(_get_inventory,
746
789
                         doc="Inventory of this Tree")
747
790
 
 
791
    def _get_root_inventory(self):
 
792
        return self._inventory
 
793
 
 
794
    root_inventory = property(_get_root_inventory,
 
795
        doc="Root inventory of this tree")
 
796
 
 
797
    def _unpack_file_id(self, file_id):
 
798
        """Find the inventory and inventory file id for a tree file id.
 
799
 
 
800
        :param file_id: The tree file id, as bytestring or tuple
 
801
        :return: Inventory and inventory file id
 
802
        """
 
803
        if isinstance(file_id, tuple):
 
804
            if len(file_id) != 1:
 
805
                raise ValueError("nested trees not yet supported: %r" % file_id)
 
806
            file_id = file_id[0]
 
807
        return self.root_inventory, file_id
 
808
 
748
809
    @needs_read_lock
749
810
    def path2id(self, path):
750
811
        """Return the id for path in this tree."""
751
 
        return self._inventory.path2id(path)
 
812
        return self._path2inv_file_id(path)[1]
 
813
 
 
814
    def _path2inv_file_id(self, path):
 
815
        """Lookup a inventory and inventory file id by path.
 
816
 
 
817
        :param path: Path to look up
 
818
        :return: tuple with inventory and inventory file id
 
819
        """
 
820
        # FIXME: Support nested trees
 
821
        return self.root_inventory, self.root_inventory.path2id(path)
752
822
 
753
823
    def id2path(self, file_id):
754
824
        """Return the path for a file id.
755
825
 
756
826
        :raises NoSuchId:
757
827
        """
758
 
        return self.inventory.id2path(file_id)
 
828
        inventory, file_id = self._unpack_file_id(file_id)
 
829
        return inventory.id2path(file_id)
759
830
 
760
831
    def has_id(self, file_id):
761
 
        return self.inventory.has_id(file_id)
 
832
        inventory, file_id = self._unpack_file_id(file_id)
 
833
        return inventory.has_id(file_id)
762
834
 
763
835
    def has_or_had_id(self, file_id):
764
 
        return self.inventory.has_id(file_id)
 
836
        inventory, file_id = self._unpack_file_id(file_id)
 
837
        return inventory.has_id(file_id)
765
838
 
766
839
    def all_file_ids(self):
767
 
        return set(self.inventory)
 
840
        return set(
 
841
            [entry.file_id for path, entry in self.iter_entries_by_dir()])
768
842
 
769
843
    @deprecated_method(deprecated_in((2, 4, 0)))
770
844
    def __iter__(self):
771
 
        return iter(self.inventory)
 
845
        return iter(self.all_file_ids())
772
846
 
773
847
    def filter_unversioned_files(self, paths):
774
848
        """Filter out paths that are versioned.
778
852
        # NB: we specifically *don't* call self.has_filename, because for
779
853
        # WorkingTrees that can indicate files that exist on disk but that
780
854
        # are not versioned.
781
 
        pred = self.inventory.has_filename
782
 
        return set((p for p in paths if not pred(p)))
 
855
        return set((p for p in paths if self.path2id(p) is None))
783
856
 
784
857
    @needs_read_lock
785
858
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
794
867
            down to specific_file_ids that have been requested. This has no
795
868
            impact if specific_file_ids is None.
796
869
        """
797
 
        return self.inventory.iter_entries_by_dir(
798
 
            specific_file_ids=specific_file_ids, yield_parents=yield_parents)
799
 
 
 
870
        if specific_file_ids is None:
 
871
            inventory_file_ids = None
 
872
        else:
 
873
            inventory_file_ids = []
 
874
            for tree_file_id in specific_file_ids:
 
875
                inventory, inv_file_id = self._unpack_file_id(tree_file_id)
 
876
                if not inventory is self.root_inventory: # for now
 
877
                    raise AssertionError("%r != %r" % (
 
878
                        inventory, self.root_inventory))
 
879
                inventory_file_ids.append(inv_file_id)
 
880
        # FIXME: Handle nested trees
 
881
        return self.root_inventory.iter_entries_by_dir(
 
882
            specific_file_ids=inventory_file_ids, yield_parents=yield_parents)
 
883
 
 
884
    @needs_read_lock
 
885
    def iter_child_entries(self, file_id, path=None):
 
886
        inv, inv_file_id = self._unpack_file_id(file_id)
 
887
        return inv[inv_file_id].children.itervalues()
 
888
 
 
889
    @deprecated_method(deprecated_in((2, 5, 0)))
800
890
    def get_file_by_path(self, path):
801
 
        return self.get_file(self._inventory.path2id(path), path)
 
891
        return self.get_file(self.path2id(path), path)
 
892
 
 
893
    def iter_children(self, file_id, path=None):
 
894
        """See Tree.iter_children."""
 
895
        entry = self.iter_entries_by_dir([file_id]).next()[1]
 
896
        for child in getattr(entry, 'children', {}).itervalues():
 
897
            yield child.file_id
802
898
 
803
899
 
804
900
def find_ids_across_trees(filenames, trees, require_versioned=True):
952
1048
        if source_kind != target_kind:
953
1049
            changed_content = True
954
1050
        elif source_kind == 'file':
955
 
            if (self.source.get_file_sha1(file_id, source_path, source_stat) !=
956
 
                self.target.get_file_sha1(file_id, target_path, target_stat)):
 
1051
            if not self.file_content_matches(file_id, file_id, source_path,
 
1052
                    target_path, source_stat, target_stat):
957
1053
                changed_content = True
958
1054
        elif source_kind == 'symlink':
959
1055
            if (self.source.get_symlink_target(file_id) !=
960
1056
                self.target.get_symlink_target(file_id)):
961
1057
                changed_content = True
962
 
            # XXX: Yes, the indentation below is wrong. But fixing it broke
963
 
            # test_merge.TestMergerEntriesLCAOnDisk.
964
 
            # test_nested_tree_subtree_renamed_and_modified. We'll wait for
965
 
            # the fix from bzr.dev -- vila 2009026
966
 
            elif source_kind == 'tree-reference':
967
 
                if (self.source.get_reference_revision(file_id, source_path)
968
 
                    != self.target.get_reference_revision(file_id, target_path)):
 
1058
        elif source_kind == 'tree-reference':
 
1059
            if (self.source.get_reference_revision(file_id, source_path)
 
1060
                != self.target.get_reference_revision(file_id, target_path)):
969
1061
                    changed_content = True
970
1062
        parent = (source_parent, target_parent)
971
1063
        name = (source_name, target_name)
1186
1278
        :param file_id: The file_id to lookup.
1187
1279
        """
1188
1280
        try:
1189
 
            inventory = tree.inventory
 
1281
            inventory = tree.root_inventory
1190
1282
        except NotImplementedError:
1191
1283
            # No inventory available.
1192
1284
            try:
1267
1359
                        if old_entry is None:
1268
1360
                            # Reusing a discarded change.
1269
1361
                            old_entry = self._get_entry(self.source, file_id)
1270
 
                        for child in old_entry.children.values():
1271
 
                            precise_file_ids.add(child.file_id)
 
1362
                        precise_file_ids.update(
 
1363
                                self.source.iter_children(file_id))
1272
1364
                    changed_file_ids.add(result[0])
1273
1365
                    yield result
1274
1366
 
 
1367
    @needs_read_lock
 
1368
    def file_content_matches(self, source_file_id, target_file_id,
 
1369
            source_path=None, target_path=None, source_stat=None, target_stat=None):
 
1370
        """Check if two files are the same in the source and target trees.
 
1371
 
 
1372
        This only checks that the contents of the files are the same,
 
1373
        it does not touch anything else.
 
1374
 
 
1375
        :param source_file_id: File id of the file in the source tree
 
1376
        :param target_file_id: File id of the file in the target tree
 
1377
        :param source_path: Path of the file in the source tree
 
1378
        :param target_path: Path of the file in the target tree
 
1379
        :param source_stat: Optional stat value of the file in the source tree
 
1380
        :param target_stat: Optional stat value of the file in the target tree
 
1381
        :return: Boolean indicating whether the files have the same contents
 
1382
        """
 
1383
        source_verifier_kind, source_verifier_data = self.source.get_file_verifier(
 
1384
            source_file_id, source_path, source_stat)
 
1385
        target_verifier_kind, target_verifier_data = self.target.get_file_verifier(
 
1386
            target_file_id, target_path, target_stat)
 
1387
        if source_verifier_kind == target_verifier_kind:
 
1388
            return (source_verifier_data == target_verifier_data)
 
1389
        # Fall back to SHA1 for now
 
1390
        if source_verifier_kind != "SHA1":
 
1391
            source_sha1 = self.source.get_file_sha1(source_file_id,
 
1392
                    source_path, source_stat)
 
1393
        else:
 
1394
            source_sha1 = source_verifier_data
 
1395
        if target_verifier_kind != "SHA1":
 
1396
            target_sha1 = self.target.get_file_sha1(target_file_id,
 
1397
                    target_path, target_stat)
 
1398
        else:
 
1399
            target_sha1 = target_verifier_data
 
1400
        return (source_sha1 == target_sha1)
1275
1401
 
1276
1402
InterTree.register_optimiser(InterTree)
1277
1403
 
1383
1509
            return (None, None)
1384
1510
        else:
1385
1511
            self._out_of_order_processed.add(file_id)
1386
 
            cur_ie = other_tree.inventory[file_id]
 
1512
            cur_ie = other_tree.root_inventory[file_id]
1387
1513
            return (cur_path, cur_ie)
1388
1514
 
1389
1515
    def iter_all(self):