~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
from bzrlib import (
26
26
    delta,
27
27
    osutils,
 
28
    revision as _mod_revision,
 
29
    conflicts as _mod_conflicts,
28
30
    symbol_versioning,
29
31
    )
30
32
from bzrlib.decorators import needs_read_lock
91
93
            want_unversioned=want_unversioned,
92
94
            )
93
95
 
94
 
    def _iter_changes(self, from_tree, include_unchanged=False,
 
96
    @symbol_versioning.deprecated_method(symbol_versioning.one_three)
 
97
    def _iter_changes(self, *args, **kwargs):
 
98
        return self.iter_changes(*args, **kwargs)
 
99
 
 
100
    def iter_changes(self, from_tree, include_unchanged=False,
95
101
                     specific_files=None, pb=None, extra_trees=None,
96
102
                     require_versioned=True, want_unversioned=False):
97
103
        intertree = InterTree.get(from_tree, self)
98
 
        return intertree._iter_changes(include_unchanged, specific_files, pb,
 
104
        return intertree.iter_changes(include_unchanged, specific_files, pb,
99
105
            extra_trees, require_versioned, want_unversioned=want_unversioned)
100
106
    
101
107
    def conflicts(self):
103
109
 
104
110
        Each conflict is an instance of bzrlib.conflicts.Conflict.
105
111
        """
106
 
        return []
 
112
        return _mod_conflicts.ConflictList()
107
113
 
108
114
    def extras(self):
109
115
        """For trees that can have unversioned files, return all such paths."""
120
126
    
121
127
    def has_filename(self, filename):
122
128
        """True if the tree has given filename."""
123
 
        raise NotImplementedError()
 
129
        raise NotImplementedError(self.has_filename)
124
130
 
125
131
    def has_id(self, file_id):
126
 
        file_id = osutils.safe_file_id(file_id)
127
132
        return self.inventory.has_id(file_id)
128
133
 
129
134
    __contains__ = has_id
130
135
 
131
136
    def has_or_had_id(self, file_id):
132
 
        file_id = osutils.safe_file_id(file_id)
133
137
        if file_id == self.inventory.root.file_id:
134
138
            return True
135
139
        return self.inventory.has_id(file_id)
145
149
    def __iter__(self):
146
150
        return iter(self.inventory)
147
151
 
 
152
    def all_file_ids(self):
 
153
        """Iterate through all file ids, including ids for missing files."""
 
154
        return set(self.inventory)
 
155
 
148
156
    def id2path(self, file_id):
149
157
        """Return the path for a file id.
150
158
 
151
159
        :raises NoSuchId:
152
160
        """
153
 
        file_id = osutils.safe_file_id(file_id)
154
161
        return self.inventory.id2path(file_id)
155
162
 
156
163
    def is_control_filename(self, filename):
186
193
        raise NotImplementedError("Tree subclass %s must implement kind"
187
194
            % self.__class__.__name__)
188
195
 
 
196
    def stored_kind(self, file_id):
 
197
        """File kind stored for this file_id.
 
198
 
 
199
        May not match kind on disk for working trees.  Always available
 
200
        for versioned files, even when the file itself is missing.
 
201
        """
 
202
        return self.kind(file_id)
 
203
 
 
204
    def path_content_summary(self, path):
 
205
        """Get a summary of the information about path.
 
206
        
 
207
        :param path: A relative path within the tree.
 
208
        :return: A tuple containing kind, size, exec, sha1-or-link.
 
209
            Kind is always present (see tree.kind()).
 
210
            size is present if kind is file, None otherwise.
 
211
            exec is None unless kind is file and the platform supports the 'x'
 
212
                bit.
 
213
            sha1-or-link is the link target if kind is symlink, or the sha1 if
 
214
                it can be obtained without reading the file.
 
215
        """
 
216
        raise NotImplementedError(self.path_content_summary)
 
217
 
189
218
    def get_reference_revision(self, file_id, path=None):
190
219
        raise NotImplementedError("Tree subclass %s must implement "
191
220
                                  "get_reference_revision"
208
237
    def _get_inventory(self):
209
238
        return self._inventory
210
239
    
211
 
    def get_file(self, file_id):
212
 
        """Return a file object for the file file_id in the tree."""
 
240
    def get_file(self, file_id, path=None):
 
241
        """Return a file object for the file file_id in the tree.
 
242
        
 
243
        If both file_id and path are defined, it is implementation defined as
 
244
        to which one is used.
 
245
        """
213
246
        raise NotImplementedError(self.get_file)
214
247
 
215
248
    def get_file_mtime(self, file_id, path=None):
222
255
        raise NotImplementedError(self.get_file_mtime)
223
256
 
224
257
    def get_file_by_path(self, path):
225
 
        return self.get_file(self._inventory.path2id(path))
 
258
        return self.get_file(self._inventory.path2id(path), path)
 
259
 
 
260
    def iter_files_bytes(self, desired_files):
 
261
        """Iterate through file contents.
 
262
 
 
263
        Files will not necessarily be returned in the order they occur in
 
264
        desired_files.  No specific order is guaranteed.
 
265
 
 
266
        Yields pairs of identifier, bytes_iterator.  identifier is an opaque
 
267
        value supplied by the caller as part of desired_files.  It should
 
268
        uniquely identify the file version in the caller's context.  (Examples:
 
269
        an index number or a TreeTransform trans_id.)
 
270
 
 
271
        bytes_iterator is an iterable of bytestrings for the file.  The
 
272
        kind of iterable and length of the bytestrings are unspecified, but for
 
273
        this implementation, it is a tuple containing a single bytestring with
 
274
        the complete text of the file.
 
275
 
 
276
        :param desired_files: a list of (file_id, identifier) pairs
 
277
        """
 
278
        for file_id, identifier in desired_files:
 
279
            # We wrap the string in a tuple so that we can return an iterable
 
280
            # of bytestrings.  (Technically, a bytestring is also an iterable
 
281
            # of bytestrings, but iterating through each character is not
 
282
            # performant.)
 
283
            cur_file = (self.get_file_text(file_id),)
 
284
            yield identifier, cur_file
226
285
 
227
286
    def get_symlink_target(self, file_id):
228
287
        """Get the target for a given file_id.
234
293
        """
235
294
        raise NotImplementedError(self.get_symlink_target)
236
295
 
237
 
    def annotate_iter(self, file_id):
238
 
        """Return an iterator of revision_id, line tuples
 
296
    def get_root_id(self):
 
297
        """Return the file_id for the root of this tree."""
 
298
        raise NotImplementedError(self.get_root_id)
 
299
 
 
300
    def annotate_iter(self, file_id,
 
301
                      default_revision=_mod_revision.CURRENT_REVISION):
 
302
        """Return an iterator of revision_id, line tuples.
239
303
 
240
304
        For working trees (and mutable trees in general), the special
241
305
        revision_id 'current:' will be used for lines that are new in this
242
306
        tree, e.g. uncommitted changes.
243
307
        :param file_id: The file to produce an annotated version from
 
308
        :param default_revision: For lines that don't match a basis, mark them
 
309
            with this revision id. Not all implementations will make use of
 
310
            this value.
244
311
        """
245
312
        raise NotImplementedError(self.annotate_iter)
246
313
 
 
314
    def _get_plan_merge_data(self, file_id, other, base):
 
315
        from bzrlib import merge, versionedfile
 
316
        vf = versionedfile._PlanMergeVersionedFile(file_id)
 
317
        last_revision_a = self._get_file_revision(file_id, vf, 'this:')
 
318
        last_revision_b = other._get_file_revision(file_id, vf, 'other:')
 
319
        if base is None:
 
320
            last_revision_base = None
 
321
        else:
 
322
            last_revision_base = base._get_file_revision(file_id, vf, 'base:')
 
323
        return vf, last_revision_a, last_revision_b, last_revision_base
 
324
 
 
325
    def plan_file_merge(self, file_id, other, base=None):
 
326
        """Generate a merge plan based on annotations.
 
327
 
 
328
        If the file contains uncommitted changes in this tree, they will be
 
329
        attributed to the 'current:' pseudo-revision.  If the file contains
 
330
        uncommitted changes in the other tree, they will be assigned to the
 
331
        'other:' pseudo-revision.
 
332
        """
 
333
        data = self._get_plan_merge_data(file_id, other, base)
 
334
        vf, last_revision_a, last_revision_b, last_revision_base = data
 
335
        return vf.plan_merge(last_revision_a, last_revision_b,
 
336
                             last_revision_base)
 
337
 
 
338
    def plan_file_lca_merge(self, file_id, other, base=None):
 
339
        """Generate a merge plan based lca-newness.
 
340
 
 
341
        If the file contains uncommitted changes in this tree, they will be
 
342
        attributed to the 'current:' pseudo-revision.  If the file contains
 
343
        uncommitted changes in the other tree, they will be assigned to the
 
344
        'other:' pseudo-revision.
 
345
        """
 
346
        data = self._get_plan_merge_data(file_id, other, base)
 
347
        vf, last_revision_a, last_revision_b, last_revision_base = data
 
348
        return vf.plan_lca_merge(last_revision_a, last_revision_b,
 
349
                                 last_revision_base)
 
350
 
 
351
    def _get_file_revision(self, file_id, vf, tree_revision):
 
352
        def file_revision(revision_tree):
 
353
            revision_tree.lock_read()
 
354
            try:
 
355
                return revision_tree.inventory[file_id].revision
 
356
            finally:
 
357
                revision_tree.unlock()
 
358
 
 
359
        def iter_parent_trees():
 
360
            for revision_id in self.get_parent_ids():
 
361
                try:
 
362
                    yield self.revision_tree(revision_id)
 
363
                except:
 
364
                    yield self.repository.revision_tree(revision_id)
 
365
 
 
366
        if getattr(self, '_get_weave', None) is None:
 
367
            last_revision = tree_revision
 
368
            parent_revisions = [file_revision(t) for t in iter_parent_trees()]
 
369
            vf.add_lines(last_revision, parent_revisions,
 
370
                         self.get_file(file_id).readlines())
 
371
            repo = self.branch.repository
 
372
            transaction = repo.get_transaction()
 
373
            base_vf = repo.weave_store.get_weave(file_id, transaction)
 
374
        else:
 
375
            last_revision = file_revision(self)
 
376
            base_vf = self._get_weave(file_id)
 
377
        vf.fallback_versionedfiles.append(base_vf)
 
378
        return last_revision
 
379
 
247
380
    inventory = property(_get_inventory,
248
381
                         doc="Inventory of this Tree")
249
382
 
274
407
    def paths2ids(self, paths, trees=[], require_versioned=True):
275
408
        """Return all the ids that can be reached by walking from paths.
276
409
        
277
 
        Each path is looked up in each this tree and any extras provided in
 
410
        Each path is looked up in this tree and any extras provided in
278
411
        trees, and this is repeated recursively: the children in an extra tree
279
412
        of a directory that has been renamed under a provided path in this tree
280
 
        are all returned, even if none exist until a provided path in this
 
413
        are all returned, even if none exist under a provided path in this
281
414
        tree, and vice versa.
282
415
 
283
416
        :param paths: An iterable of paths to start converting to ids from.
292
425
 
293
426
    def print_file(self, file_id):
294
427
        """Print file with id `file_id` to stdout."""
295
 
        file_id = osutils.safe_file_id(file_id)
296
428
        import sys
297
429
        sys.stdout.write(self.get_file_text(file_id))
298
430
 
356
488
           versioned_kind.
357
489
         - lstat is the stat data *if* the file was statted.
358
490
         - path_from_tree_root is the path from the root of the tree.
359
 
         - file_id is the file_id is the entry is versioned.
 
491
         - file_id is the file_id if the entry is versioned.
360
492
         - versioned_kind is the kind of the file as last recorded in the 
361
493
           versioning system. If 'unknown' the file is not versioned.
362
494
        One of 'kind' and 'versioned_kind' must not be 'unknown'.
387
519
        return False
388
520
 
389
521
    def kind(self, file_id):
390
 
        file_id = osutils.safe_file_id(file_id)
391
522
        assert self._inventory[file_id].kind == "directory"
392
523
        return "directory"
393
524
 
395
526
        return iter([])
396
527
    
397
528
    def __contains__(self, file_id):
398
 
        file_id = osutils.safe_file_id(file_id)
399
529
        return (file_id in self._inventory)
400
530
 
401
531
    def get_file_sha1(self, file_id, path=None, stat_value=None):
496
626
    :param trees: The trees to find file_ids within
497
627
    :param require_versioned: if true, all specified filenames must occur in
498
628
        at least one tree.
499
 
    :return: a set of (path, file ids) for the specified filenames
 
629
    :return: a set of file ids for the specified filenames
500
630
    """
501
631
    not_versioned = []
502
632
    interesting_ids = set()
515
645
 
516
646
 
517
647
def _find_children_across_trees(specified_ids, trees):
518
 
    """Return a set including specified ids and their children
 
648
    """Return a set including specified ids and their children.
519
649
    
520
650
    All matches in all trees will be used.
521
651
 
547
677
    Its instances have methods like 'compare' and contain references to the
548
678
    source and target trees these operations are to be carried out on.
549
679
 
550
 
    clients of bzrlib should not need to use InterTree directly, rather they
 
680
    Clients of bzrlib should not need to use InterTree directly, rather they
551
681
    should use the convenience methods on Tree such as 'Tree.compare()' which
552
682
    will pass through to InterTree as appropriate.
553
683
    """
593
723
            return result
594
724
        return delta._compare_trees(self.source, self.target, want_unchanged,
595
725
            specific_files, include_root, extra_trees=extra_trees,
 
726
            require_versioned=require_versioned,
596
727
            want_unversioned=want_unversioned)
597
728
 
598
 
    def _iter_changes(self, include_unchanged=False,
 
729
    def iter_changes(self, include_unchanged=False,
599
730
                      specific_files=None, pb=None, extra_trees=[],
600
731
                      require_versioned=True, want_unversioned=False):
601
732
        """Generate an iterator of changes between trees.
629
760
        lookup_trees = [self.source]
630
761
        if extra_trees:
631
762
             lookup_trees.extend(extra_trees)
632
 
        specific_file_ids = self.target.paths2ids(specific_files,
633
 
            lookup_trees, require_versioned=require_versioned)
 
763
        if specific_files == []:
 
764
            specific_file_ids = []
 
765
        else:
 
766
            specific_file_ids = self.target.paths2ids(specific_files,
 
767
                lookup_trees, require_versioned=require_versioned)
634
768
        if want_unversioned:
635
 
            all_unversioned = sorted([(p.split('/'), p) for p in self.target.extras()
636
 
                if not specific_files or
 
769
            all_unversioned = sorted([(p.split('/'), p) for p in
 
770
                                     self.target.extras()
 
771
                if specific_files is None or
637
772
                    osutils.is_inside_any(specific_files, p)])
638
773
            all_unversioned = deque(all_unversioned)
639
774
        else: