~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-10-10 08:52:29 UTC
  • mfrom: (2903.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20071010085229-7x5al1tirr29mq0l
Fix 149019 by using a proper line number when reporting errors

Show diffs side-by-side

added added

removed removed

Lines of Context:
50
50
    BzrCheckError,
51
51
    BzrError,
52
52
    )
53
 
from bzrlib.symbol_versioning import deprecated_method
 
53
from bzrlib.symbol_versioning import deprecated_method, zero_ninetyone
54
54
from bzrlib.trace import mutter
55
55
 
56
56
 
112
112
    InventoryFile('2326', 'wibble.c', parent_id='2325', sha1=None, len=None)
113
113
    >>> for path, entry in i.iter_entries():
114
114
    ...     print path
 
115
    ...     assert i.path2id(path)
115
116
    ... 
116
117
    <BLANKLINE>
117
118
    src
141
142
        """
142
143
        return False, False
143
144
 
 
145
    def diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
 
146
             output_to, reverse=False):
 
147
        """Perform a diff from this to to_entry.
 
148
 
 
149
        text_diff will be used for textual difference calculation.
 
150
        This is a template method, override _diff in child classes.
 
151
        """
 
152
        self._read_tree_state(tree.id2path(self.file_id), tree)
 
153
        if to_entry:
 
154
            # cannot diff from one kind to another - you must do a removal
 
155
            # and an addif they do not match.
 
156
            assert self.kind == to_entry.kind
 
157
            to_entry._read_tree_state(to_tree.id2path(to_entry.file_id),
 
158
                                      to_tree)
 
159
        self._diff(text_diff, from_label, tree, to_label, to_entry, to_tree,
 
160
                   output_to, reverse)
 
161
 
144
162
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
145
163
             output_to, reverse=False):
146
164
        """Perform a diff between two entries of the same kind."""
160
178
        for inv in previous_inventories:
161
179
            if self.file_id in inv:
162
180
                ie = inv[self.file_id]
 
181
                assert ie.file_id == self.file_id
163
182
                if ie.revision in candidates:
164
183
                    # same revision value in two different inventories:
165
184
                    # correct possible inconsistencies:
171
190
                            ie.executable = False
172
191
                    except AttributeError:
173
192
                        pass
 
193
                    # must now be the same.
 
194
                    assert candidates[ie.revision] == ie
174
195
                else:
175
196
                    # add this revision as a candidate.
176
197
                    candidates[ie.revision] = ie
177
198
        return candidates
178
199
 
 
200
    @deprecated_method(zero_ninetyone)
 
201
    def find_previous_heads(self, previous_inventories,
 
202
                            versioned_file_store,
 
203
                            transaction,
 
204
                            entry_vf=None):
 
205
        """Return the revisions and entries that directly precede this.
 
206
 
 
207
        Returned as a map from revision to inventory entry.
 
208
 
 
209
        This is a map containing the file revisions in all parents
 
210
        for which the file exists, and its revision is not a parent of
 
211
        any other. If the file is new, the set will be empty.
 
212
 
 
213
        :param versioned_file_store: A store where ancestry data on this
 
214
                                     file id can be queried.
 
215
        :param transaction: The transaction that queries to the versioned 
 
216
                            file store should be completed under.
 
217
        :param entry_vf: The entry versioned file, if its already available.
 
218
        """
 
219
        candidates = self.parent_candidates(previous_inventories)
 
220
 
 
221
        # revision:ie mapping with one revision for each head.
 
222
        heads = {}
 
223
        # common case optimisation
 
224
        if len(candidates) == 1:
 
225
            # if there is only one candidate revision found
 
226
            # then we can avoid opening the versioned file to access ancestry:
 
227
            # there cannot be any ancestors to eliminate when there is 
 
228
            # only one revision available.
 
229
            return candidates
 
230
        
 
231
        # --- what follows is now encapsulated in repository.get_graph.heads(), 
 
232
        #     but that is not accessible from here as we have no repository
 
233
        #     pointer. Note that the repository.get_graph.heads() call can return
 
234
        #     different results *at the moment* because of the kind-changing check
 
235
        #     we have in parent_candidates().
 
236
 
 
237
        # eliminate ancestors amongst the available candidates:
 
238
        # heads are those that are not an ancestor of any other candidate
 
239
        # - this provides convergence at a per-file level.
 
240
        def get_ancestors(weave, entry):
 
241
            return set(weave.get_ancestry(entry.revision, topo_sorted=False))
 
242
        # revision: ancestor list for each head
 
243
        head_ancestors = {}
 
244
        for ie in candidates.values():
 
245
            # may be an ancestor of a known head:
 
246
            already_present = 0 != len(
 
247
                [head for head in heads 
 
248
                 if ie.revision in head_ancestors[head]])
 
249
            if already_present:
 
250
                # an ancestor of an analyzed candidate.
 
251
                continue
 
252
            # not an ancestor of a known head:
 
253
            # load the versioned file for this file id if needed
 
254
            if entry_vf is None:
 
255
                entry_vf = versioned_file_store.get_weave_or_empty(
 
256
                    self.file_id, transaction)
 
257
            ancestors = get_ancestors(entry_vf, ie)
 
258
            # may knock something else out:
 
259
            check_heads = list(heads.keys())
 
260
            for head in check_heads:
 
261
                if head in ancestors:
 
262
                    # this previously discovered 'head' is not
 
263
                    # really a head - its an ancestor of the newly 
 
264
                    # found head,
 
265
                    heads.pop(head)
 
266
            head_ancestors[ie.revision] = ancestors
 
267
            heads[ie.revision] = ie
 
268
        return heads
 
269
 
179
270
    def get_tar_item(self, root, dp, now, tree):
180
271
        """Get a tarfile item and a file stream for its content."""
181
272
        item = tarfile.TarInfo(osutils.pathjoin(root, dp).encode('utf8'))
211
302
        Traceback (most recent call last):
212
303
        InvalidEntryName: Invalid entry name: src/hello.c
213
304
        """
 
305
        assert isinstance(name, basestring), name
214
306
        if '/' in name or '\\' in name:
215
307
            raise errors.InvalidEntryName(name=name)
216
308
        self.executable = False
218
310
        self.text_sha1 = None
219
311
        self.text_size = None
220
312
        self.file_id = file_id
 
313
        assert isinstance(file_id, (str, None.__class__)), \
 
314
            'bad type %r for %r' % (type(file_id), file_id)
221
315
        self.name = name
222
316
        self.text_id = text_id
223
317
        self.parent_id = parent_id
476
570
        if t in checker.checked_texts:
477
571
            prev_sha = checker.checked_texts[t]
478
572
            if prev_sha != self.text_sha1:
479
 
                raise BzrCheckError(
480
 
                    'mismatched sha1 on {%s} in {%s} (%s != %s) %r' %
481
 
                    (self.file_id, tree_revision_id, prev_sha, self.text_sha1,
482
 
                     t))
 
573
                raise BzrCheckError('mismatched sha1 on {%s} in {%s}' %
 
574
                                    (self.file_id, tree_revision_id))
483
575
            else:
484
576
                checker.repeated_text_cnt += 1
485
577
                return
500
592
        # We can't check the length, because Weave doesn't store that
501
593
        # information, and the whole point of looking at the weave's
502
594
        # sha1sum is that we don't have to extract the text.
503
 
        if self.text_sha1 != w.get_sha1s([self.revision])[0]:
 
595
        if self.text_sha1 != w.get_sha1(self.revision):
504
596
            raise BzrCheckError('text {%s} version {%s} wrong sha1' 
505
597
                                % (self.file_id, self.revision))
506
598
        checker.checked_texts[t] = self.text_sha1
516
608
 
517
609
    def detect_changes(self, old_entry):
518
610
        """See InventoryEntry.detect_changes."""
 
611
        assert self.text_sha1 is not None
 
612
        assert old_entry.text_sha1 is not None
519
613
        text_modified = (self.text_sha1 != old_entry.text_sha1)
520
614
        meta_modified = (self.executable != old_entry.executable)
521
615
        return text_modified, meta_modified
523
617
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
524
618
             output_to, reverse=False):
525
619
        """See InventoryEntry._diff."""
526
 
        from bzrlib.diff import DiffText
527
 
        from_file_id = self.file_id
528
 
        if to_entry:
529
 
            to_file_id = to_entry.file_id
530
 
        else:
531
 
            to_file_id = None
532
 
        if reverse:
533
 
            to_file_id, from_file_id = from_file_id, to_file_id
534
 
            tree, to_tree = to_tree, tree
535
 
            from_label, to_label = to_label, from_label
536
 
        differ = DiffText(tree, to_tree, output_to, 'utf-8', '', '',
537
 
                          text_diff)
538
 
        return differ.diff_text(from_file_id, to_file_id, from_label, to_label)
 
620
        try:
 
621
            from_text = tree.get_file(self.file_id).readlines()
 
622
            if to_entry:
 
623
                to_text = to_tree.get_file(to_entry.file_id).readlines()
 
624
            else:
 
625
                to_text = []
 
626
            if not reverse:
 
627
                text_diff(from_label, from_text,
 
628
                          to_label, to_text, output_to)
 
629
            else:
 
630
                text_diff(to_label, to_text,
 
631
                          from_label, from_text, output_to)
 
632
        except errors.BinaryFile:
 
633
            if reverse:
 
634
                label_pair = (to_label, from_label)
 
635
            else:
 
636
                label_pair = (from_label, to_label)
 
637
            print >> output_to, \
 
638
                  ("Binary files %s and %s differ" % label_pair).encode('utf8')
539
639
 
540
640
    def has_text(self):
541
641
        """See InventoryEntry.has_text."""
633
733
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
634
734
             output_to, reverse=False):
635
735
        """See InventoryEntry._diff."""
636
 
        from bzrlib.diff import DiffSymlink
637
 
        old_target = self.symlink_target
 
736
        from_text = self.symlink_target
638
737
        if to_entry is not None:
639
 
            new_target = to_entry.symlink_target
640
 
        else:
641
 
            new_target = None
642
 
        if not reverse:
643
 
            old_tree = tree
644
 
            new_tree = to_tree
645
 
        else:
646
 
            old_tree = to_tree
647
 
            new_tree = tree
648
 
            new_target, old_target = old_target, new_target
649
 
        differ = DiffSymlink(old_tree, new_tree, output_to)
650
 
        return differ.diff_symlink(old_target, new_target)
 
738
            to_text = to_entry.symlink_target
 
739
            if reverse:
 
740
                temp = from_text
 
741
                from_text = to_text
 
742
                to_text = temp
 
743
            print >>output_to, '=== target changed %r => %r' % (from_text, to_text)
 
744
        else:
 
745
            if not reverse:
 
746
                print >>output_to, '=== target was %r' % self.symlink_target
 
747
            else:
 
748
                print >>output_to, '=== target is %r' % self.symlink_target
651
749
 
652
750
    def __init__(self, file_id, name, parent_id):
653
751
        super(InventoryLink, self).__init__(file_id, name, parent_id)
771
869
        an id of None.
772
870
        """
773
871
        if root_id is not None:
 
872
            assert root_id.__class__ == str
774
873
            self._set_root(InventoryDirectory(root_id, u'', None))
775
874
        else:
776
875
            self.root = None
847
946
    def copy(self):
848
947
        # TODO: jam 20051218 Should copy also copy the revision_id?
849
948
        entries = self.iter_entries()
850
 
        if self.root is None:
851
 
            return Inventory(root_id=None)
852
949
        other = Inventory(entries.next()[1].file_id)
853
950
        # copy recursively so we know directories will be added before
854
951
        # their children.  There are more efficient ways than this...
855
 
        for path, entry in entries:
 
952
        for path, entry in entries():
856
953
            other.add(entry.copy())
857
954
        return other
858
955
 
1075
1172
                                         self._byid[entry.file_id])
1076
1173
 
1077
1174
        if entry.parent_id is None:
 
1175
            assert self.root is None and len(self._byid) == 0
1078
1176
            self.root = entry
1079
1177
        else:
1080
1178
            try:
1126
1224
        False
1127
1225
        """
1128
1226
        ie = self[file_id]
 
1227
 
 
1228
        assert ie.parent_id is None or \
 
1229
            self[ie.parent_id].children[ie.name] == ie
 
1230
        
1129
1231
        del self._byid[file_id]
1130
1232
        if ie.parent_id is not None:
1131
1233
            del self[ie.parent_id].children[ie.name]
1219
1321
                if children is None:
1220
1322
                    return None
1221
1323
                cie = children[f]
 
1324
                assert cie.name == f
 
1325
                assert cie.parent_id == parent.file_id
1222
1326
                parent = cie
1223
1327
            except KeyError:
1224
1328
                # or raise an error?
1317
1421
 
1318
1422
    :raises InvalidNormalization: When name is not normalized, and cannot be
1319
1423
        accessed on this platform by the normalized path.
1320
 
    :return: The NFC normalised version of name.
 
1424
    :return: The NFC/NFKC normalised version of name.
1321
1425
    """
1322
1426
    #------- This has been copied to bzrlib.dirstate.DirState.add, please
1323
1427
    # keep them synchronised.