~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

[merge] bzr.dev 2255

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
import os
21
21
from cStringIO import StringIO
22
 
from warnings import warn
23
22
 
24
23
import bzrlib
25
 
from bzrlib import delta
 
24
from bzrlib import (
 
25
    delta,
 
26
    osutils,
 
27
    symbol_versioning,
 
28
    )
26
29
from bzrlib.decorators import needs_read_lock
27
30
from bzrlib.errors import BzrError, BzrCheckError
28
31
from bzrlib import errors
83
86
            require_versioned=require_versioned,
84
87
            include_root=include_root
85
88
            )
 
89
 
 
90
    def _iter_changes(self, from_tree, include_unchanged=False, 
 
91
                     specific_file_ids=None, pb=None):
 
92
        intertree = InterTree.get(from_tree, self)
 
93
        return intertree._iter_changes(from_tree, self, include_unchanged, 
 
94
                                       specific_file_ids, pb)
86
95
    
87
96
    def conflicts(self):
88
97
        """Get a list of the conflicts in the tree.
132
141
        """
133
142
        return self.bzrdir.is_control_filename(filename)
134
143
 
135
 
    def iter_entries_by_dir(self):
 
144
    def iter_entries_by_dir(self, specific_file_ids=None):
136
145
        """Walk the tree in 'by_dir' order.
137
146
 
138
147
        This will yield each entry in the tree as a (path, entry) tuple. The
140
149
        preceeded by the parent of a directory, and all the contents of a 
141
150
        directory are grouped together.
142
151
        """
143
 
        return self.inventory.iter_entries_by_dir()
 
152
        return self.inventory.iter_entries_by_dir(
 
153
            specific_file_ids=specific_file_ids)
144
154
 
145
155
    def kind(self, file_id):
146
156
        raise NotImplementedError("subclasses must implement kind")
147
157
 
 
158
    def _comparison_data(self, entry, path):
 
159
        """Return a tuple of kind, executable, stat_value for a file.
 
160
 
 
161
        entry may be None if there is no inventory entry for the file, but
 
162
        path must always be supplied.
 
163
 
 
164
        kind is None if there is no file present (even if an inventory id is
 
165
        present).  executable is False for non-file entries.
 
166
        """
 
167
        raise NotImplementedError(self._comparison_data)
 
168
 
 
169
    def _file_size(entry, stat_value):
 
170
        raise NotImplementedError(self._file_size)
 
171
 
148
172
    def _get_inventory(self):
149
173
        return self._inventory
150
174
    
155
179
    def get_file_by_path(self, path):
156
180
        return self.get_file(self._inventory.path2id(path))
157
181
 
 
182
    def annotate_iter(self, file_id):
 
183
        """Return an iterator of revision_id, line tuples
 
184
 
 
185
        For working trees (and mutable trees in general), the special
 
186
        revision_id 'current:' will be used for lines that are new in this
 
187
        tree, e.g. uncommitted changes.
 
188
        :param file_id: The file to produce an annotated version from
 
189
        """
 
190
        raise NotImplementedError(self.annotate_iter)
 
191
 
158
192
    inventory = property(_get_inventory,
159
193
                         doc="Inventory of this Tree")
160
194
 
259
293
        raise NotImplementedError(self.walkdirs)
260
294
 
261
295
 
262
 
# for compatibility
263
 
from bzrlib.revisiontree import RevisionTree
264
 
 
265
 
 
266
296
class EmptyTree(Tree):
267
297
 
268
298
    def __init__(self):
269
 
        self._inventory = Inventory()
270
 
        warn('EmptyTree is deprecated as of bzr 0.9 please use '
271
 
            'repository.revision_tree instead.',
272
 
            DeprecationWarning, stacklevel=2)
 
299
        self._inventory = Inventory(root_id=None)
 
300
        symbol_versioning.warn('EmptyTree is deprecated as of bzr 0.9 please'
 
301
                               ' use repository.revision_tree instead.',
 
302
                               DeprecationWarning, stacklevel=2)
273
303
 
274
304
    def get_parent_ids(self):
275
305
        return []
284
314
        assert self._inventory[file_id].kind == "directory"
285
315
        return "directory"
286
316
 
287
 
    def list_files(self):
 
317
    def list_files(self, include_root=False):
288
318
        return iter([])
289
319
    
290
320
    def __contains__(self, file_id):
291
321
        return (file_id in self._inventory)
292
322
 
293
 
    def get_file_sha1(self, file_id, path=None):
 
323
    def get_file_sha1(self, file_id, path=None, stat_value=None):
294
324
        return None
295
325
 
296
326
 
476
506
            return delta.TreeDelta()
477
507
        return delta._compare_trees(self.source, self.target, want_unchanged,
478
508
            specific_file_ids, include_root)
 
509
 
 
510
    def _iter_changes(self, from_tree, to_tree, include_unchanged, 
 
511
                      specific_file_ids, pb):
 
512
        """Generate an iterator of changes between trees.
 
513
 
 
514
        A tuple is returned:
 
515
        (file_id, path, changed_content, versioned, parent, name, kind,
 
516
         executable)
 
517
 
 
518
        Path is relative to the to_tree.  changed_content is True if the file's
 
519
        content has changed.  This includes changes to its kind, and to
 
520
        a symlink's target.
 
521
 
 
522
        versioned, parent, name, kind, executable are tuples of (from, to).
 
523
        If a file is missing in a tree, its kind is None.
 
524
 
 
525
        Iteration is done in parent-to-child order, relative to the to_tree.
 
526
        """
 
527
        to_paths = {}
 
528
        from_entries_by_dir = list(from_tree.inventory.iter_entries_by_dir(
 
529
            specific_file_ids=specific_file_ids))
 
530
        from_data = dict((e.file_id, (p, e)) for p, e in from_entries_by_dir)
 
531
        to_entries_by_dir = list(to_tree.inventory.iter_entries_by_dir(
 
532
            specific_file_ids=specific_file_ids))
 
533
        num_entries = len(from_entries_by_dir) + len(to_entries_by_dir)
 
534
        entry_count = 0
 
535
        for to_path, to_entry in to_entries_by_dir:
 
536
            file_id = to_entry.file_id
 
537
            to_paths[file_id] = to_path
 
538
            entry_count += 1
 
539
            changed_content = False
 
540
            from_path, from_entry = from_data.get(file_id, (None, None))
 
541
            from_versioned = (from_entry is not None)
 
542
            if from_entry is not None:
 
543
                from_versioned = True
 
544
                from_name = from_entry.name
 
545
                from_parent = from_entry.parent_id
 
546
                from_kind, from_executable, from_stat = \
 
547
                    from_tree._comparison_data(from_entry, from_path)
 
548
                entry_count += 1
 
549
            else:
 
550
                from_versioned = False
 
551
                from_kind = None
 
552
                from_parent = None
 
553
                from_name = None
 
554
                from_executable = None
 
555
            versioned = (from_versioned, True)
 
556
            to_kind, to_executable, to_stat = \
 
557
                to_tree._comparison_data(to_entry, to_path)
 
558
            kind = (from_kind, to_kind)
 
559
            if kind[0] != kind[1]:
 
560
                changed_content = True
 
561
            elif from_kind == 'file':
 
562
                from_size = from_tree._file_size(from_entry, from_stat)
 
563
                to_size = to_tree._file_size(to_entry, to_stat)
 
564
                if from_size != to_size:
 
565
                    changed_content = True
 
566
                elif (from_tree.get_file_sha1(file_id, from_path, from_stat) !=
 
567
                    to_tree.get_file_sha1(file_id, to_path, to_stat)):
 
568
                    changed_content = True
 
569
            elif from_kind == 'symlink':
 
570
                if (from_tree.get_symlink_target(file_id) != 
 
571
                    to_tree.get_symlink_target(file_id)):
 
572
                    changed_content = True
 
573
            parent = (from_parent, to_entry.parent_id)
 
574
            name = (from_name, to_entry.name)
 
575
            executable = (from_executable, to_executable)
 
576
            if pb is not None:
 
577
                pb.update('comparing files', entry_count, num_entries)
 
578
            if (changed_content is not False or versioned[0] != versioned[1] 
 
579
                or parent[0] != parent[1] or name[0] != name[1] or 
 
580
                executable[0] != executable[1] or include_unchanged):
 
581
                yield (file_id, to_path, changed_content, versioned, parent,
 
582
                       name, kind, executable)
 
583
 
 
584
        def get_to_path(from_entry):
 
585
            if from_entry.parent_id is None:
 
586
                to_path = ''
 
587
            else:
 
588
                if from_entry.parent_id not in to_paths:
 
589
                    get_to_path(from_tree.inventory[from_entry.parent_id])
 
590
                to_path = osutils.pathjoin(to_paths[from_entry.parent_id],
 
591
                                           from_entry.name)
 
592
            to_paths[from_entry.file_id] = to_path
 
593
            return to_path
 
594
 
 
595
        for path, from_entry in from_entries_by_dir:
 
596
            file_id = from_entry.file_id
 
597
            if file_id in to_paths:
 
598
                continue
 
599
            to_path = get_to_path(from_entry)
 
600
            entry_count += 1
 
601
            if pb is not None:
 
602
                pb.update('comparing files', entry_count, num_entries)
 
603
            versioned = (True, False)
 
604
            parent = (from_entry.parent_id, None)
 
605
            name = (from_entry.name, None)
 
606
            from_kind, from_executable, stat_value = \
 
607
                from_tree._comparison_data(from_entry, path)
 
608
            kind = (from_kind, None)
 
609
            executable = (from_executable, None)
 
610
            changed_content = True
 
611
            # the parent's path is necessarily known at this point.
 
612
            yield(file_id, to_path, changed_content, versioned, parent,
 
613
                  name, kind, executable)
 
614
 
 
615
 
 
616
# This was deprecated before 0.12, but did not have an official warning
 
617
@symbol_versioning.deprecated_function(symbol_versioning.zero_twelve)
 
618
def RevisionTree(*args, **kwargs):
 
619
    """RevisionTree has moved to bzrlib.revisiontree.RevisionTree()
 
620
 
 
621
    Accessing it as bzrlib.tree.RevisionTree has been deprecated as of
 
622
    bzr 0.12.
 
623
    """
 
624
    from bzrlib.revisiontree import RevisionTree as _RevisionTree
 
625
    return _RevisionTree(*args, **kwargs)
 
626
 
 
627