~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-12-20 04:20:19 UTC
  • mfrom: (3062.2.13 fast-plan-merge2)
  • Revision ID: pqm@pqm.ubuntu.com-20071220042019-wsij5vgvhgw4qhdt
Annotate merge can do cherrypicks (abentley)

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_in, deprecated_method
 
53
from bzrlib.symbol_versioning import deprecated_method
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
    @deprecated_method(symbol_versioning.one_zero)
 
146
    def diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
 
147
             output_to, reverse=False):
 
148
        """Perform a diff from this to to_entry.
 
149
 
 
150
        text_diff will be used for textual difference calculation.
 
151
        This is a template method, override _diff in child classes.
 
152
        """
 
153
        self._read_tree_state(tree.id2path(self.file_id), tree)
 
154
        if to_entry:
 
155
            # cannot diff from one kind to another - you must do a removal
 
156
            # and an addif they do not match.
 
157
            assert self.kind == to_entry.kind
 
158
            to_entry._read_tree_state(to_tree.id2path(to_entry.file_id),
 
159
                                      to_tree)
 
160
        self._diff(text_diff, from_label, tree, to_label, to_entry, to_tree,
 
161
                   output_to, reverse)
 
162
 
144
163
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
145
164
             output_to, reverse=False):
146
165
        """Perform a diff between two entries of the same kind."""
160
179
        for inv in previous_inventories:
161
180
            if self.file_id in inv:
162
181
                ie = inv[self.file_id]
 
182
                assert ie.file_id == self.file_id
163
183
                if ie.revision in candidates:
164
184
                    # same revision value in two different inventories:
165
185
                    # correct possible inconsistencies:
171
191
                            ie.executable = False
172
192
                    except AttributeError:
173
193
                        pass
 
194
                    # must now be the same.
 
195
                    assert candidates[ie.revision] == ie
174
196
                else:
175
197
                    # add this revision as a candidate.
176
198
                    candidates[ie.revision] = ie
177
199
        return candidates
178
200
 
179
 
    @deprecated_method(deprecated_in((1, 6, 0)))
 
201
    @deprecated_method(symbol_versioning.zero_ninetyone)
 
202
    def find_previous_heads(self, previous_inventories,
 
203
                            versioned_file_store,
 
204
                            transaction,
 
205
                            entry_vf=None):
 
206
        """Return the revisions and entries that directly precede this.
 
207
 
 
208
        Returned as a map from revision to inventory entry.
 
209
 
 
210
        This is a map containing the file revisions in all parents
 
211
        for which the file exists, and its revision is not a parent of
 
212
        any other. If the file is new, the set will be empty.
 
213
 
 
214
        :param versioned_file_store: A store where ancestry data on this
 
215
                                     file id can be queried.
 
216
        :param transaction: The transaction that queries to the versioned 
 
217
                            file store should be completed under.
 
218
        :param entry_vf: The entry versioned file, if its already available.
 
219
        """
 
220
        candidates = self.parent_candidates(previous_inventories)
 
221
 
 
222
        # revision:ie mapping with one revision for each head.
 
223
        heads = {}
 
224
        # common case optimisation
 
225
        if len(candidates) == 1:
 
226
            # if there is only one candidate revision found
 
227
            # then we can avoid opening the versioned file to access ancestry:
 
228
            # there cannot be any ancestors to eliminate when there is 
 
229
            # only one revision available.
 
230
            return candidates
 
231
        
 
232
        # --- what follows is now encapsulated in repository.get_graph.heads(), 
 
233
        #     but that is not accessible from here as we have no repository
 
234
        #     pointer. Note that the repository.get_graph.heads() call can return
 
235
        #     different results *at the moment* because of the kind-changing check
 
236
        #     we have in parent_candidates().
 
237
 
 
238
        # eliminate ancestors amongst the available candidates:
 
239
        # heads are those that are not an ancestor of any other candidate
 
240
        # - this provides convergence at a per-file level.
 
241
        def get_ancestors(weave, entry):
 
242
            return set(weave.get_ancestry(entry.revision, topo_sorted=False))
 
243
        # revision: ancestor list for each head
 
244
        head_ancestors = {}
 
245
        for ie in candidates.values():
 
246
            # may be an ancestor of a known head:
 
247
            already_present = 0 != len(
 
248
                [head for head in heads 
 
249
                 if ie.revision in head_ancestors[head]])
 
250
            if already_present:
 
251
                # an ancestor of an analyzed candidate.
 
252
                continue
 
253
            # not an ancestor of a known head:
 
254
            # load the versioned file for this file id if needed
 
255
            if entry_vf is None:
 
256
                entry_vf = versioned_file_store.get_weave_or_empty(
 
257
                    self.file_id, transaction)
 
258
            ancestors = get_ancestors(entry_vf, ie)
 
259
            # may knock something else out:
 
260
            check_heads = list(heads.keys())
 
261
            for head in check_heads:
 
262
                if head in ancestors:
 
263
                    # this previously discovered 'head' is not
 
264
                    # really a head - its an ancestor of the newly 
 
265
                    # found head,
 
266
                    heads.pop(head)
 
267
            head_ancestors[ie.revision] = ancestors
 
268
            heads[ie.revision] = ie
 
269
        return heads
 
270
 
180
271
    def get_tar_item(self, root, dp, now, tree):
181
272
        """Get a tarfile item and a file stream for its content."""
182
273
        item = tarfile.TarInfo(osutils.pathjoin(root, dp).encode('utf8'))
212
303
        Traceback (most recent call last):
213
304
        InvalidEntryName: Invalid entry name: src/hello.c
214
305
        """
 
306
        assert isinstance(name, basestring), name
215
307
        if '/' in name or '\\' in name:
216
308
            raise errors.InvalidEntryName(name=name)
217
309
        self.executable = False
219
311
        self.text_sha1 = None
220
312
        self.text_size = None
221
313
        self.file_id = file_id
 
314
        assert isinstance(file_id, (str, None.__class__)), \
 
315
            'bad type %r for %r' % (type(file_id), file_id)
222
316
        self.name = name
223
317
        self.text_id = text_id
224
318
        self.parent_id = parent_id
239
333
        raise BzrError("don't know how to export {%s} of kind %r" %
240
334
                       (self.file_id, self.kind))
241
335
 
242
 
    @deprecated_method(deprecated_in((1, 6, 0)))
243
336
    def put_on_disk(self, dest, dp, tree):
244
337
        """Create a representation of self on disk in the prefix dest.
245
338
        
474
567
 
475
568
    def _check(self, checker, tree_revision_id, tree):
476
569
        """See InventoryEntry._check"""
477
 
        key = (self.file_id, self.revision)
478
 
        if key in checker.checked_texts:
479
 
            prev_sha = checker.checked_texts[key]
 
570
        t = (self.file_id, self.revision)
 
571
        if t in checker.checked_texts:
 
572
            prev_sha = checker.checked_texts[t]
480
573
            if prev_sha != self.text_sha1:
481
574
                raise BzrCheckError(
482
575
                    'mismatched sha1 on {%s} in {%s} (%s != %s) %r' %
486
579
                checker.repeated_text_cnt += 1
487
580
                return
488
581
 
 
582
        if self.file_id not in checker.checked_weaves:
 
583
            mutter('check weave {%s}', self.file_id)
 
584
            w = tree._get_weave(self.file_id)
 
585
            # Not passing a progress bar, because it creates a new
 
586
            # progress, which overwrites the current progress,
 
587
            # and doesn't look nice
 
588
            w.check()
 
589
            checker.checked_weaves[self.file_id] = True
 
590
        else:
 
591
            w = tree._get_weave(self.file_id)
 
592
 
489
593
        mutter('check version {%s} of {%s}', tree_revision_id, self.file_id)
490
594
        checker.checked_text_cnt += 1
491
595
        # We can't check the length, because Weave doesn't store that
492
596
        # information, and the whole point of looking at the weave's
493
597
        # sha1sum is that we don't have to extract the text.
494
 
        if (self.text_sha1 != tree._repository.texts.get_sha1s([key])[key]):
495
 
            raise BzrCheckError('text {%s} version {%s} wrong sha1' % key)
496
 
        checker.checked_texts[key] = self.text_sha1
 
598
        if self.text_sha1 != w.get_sha1(self.revision):
 
599
            raise BzrCheckError('text {%s} version {%s} wrong sha1' 
 
600
                                % (self.file_id, self.revision))
 
601
        checker.checked_texts[t] = self.text_sha1
497
602
 
498
603
    def copy(self):
499
604
        other = InventoryFile(self.file_id, self.name, self.parent_id)
506
611
 
507
612
    def detect_changes(self, old_entry):
508
613
        """See InventoryEntry.detect_changes."""
 
614
        assert self.text_sha1 is not None
 
615
        assert old_entry.text_sha1 is not None
509
616
        text_modified = (self.text_sha1 != old_entry.text_sha1)
510
617
        meta_modified = (self.executable != old_entry.executable)
511
618
        return text_modified, meta_modified
761
868
        an id of None.
762
869
        """
763
870
        if root_id is not None:
 
871
            assert root_id.__class__ == str
764
872
            self._set_root(InventoryDirectory(root_id, u'', None))
765
873
        else:
766
874
            self.root = None
815
923
                # adds come later
816
924
                continue
817
925
            # Preserve unaltered children of file_id for later reinsertion.
818
 
            file_id_children = getattr(self[file_id], 'children', {})
819
 
            if len(file_id_children):
820
 
                children[file_id] = file_id_children
 
926
            children[file_id] = getattr(self[file_id], 'children', {})
821
927
            # Remove file_id and the unaltered children. If file_id is not
822
928
            # being deleted it will be reinserted back later.
823
929
            self.remove_recursive_id(file_id)
829
935
        for new_path, new_entry in sorted((np, e) for op, np, f, e in
830
936
                                          delta if np is not None):
831
937
            if new_entry.kind == 'directory':
832
 
                # Pop the child which to allow detection of children whose
833
 
                # parents were deleted and which were not reattached to a new
834
 
                # parent.
835
 
                new_entry.children = children.pop(new_entry.file_id, {})
 
938
                new_entry.children = children.get(new_entry.file_id, {})
836
939
            self.add(new_entry)
837
 
        if len(children):
838
 
            # Get the parent id that was deleted
839
 
            parent_id, children = children.popitem()
840
 
            raise errors.InconsistentDelta("<deleted>", parent_id,
841
 
                "The file id was deleted but its children were not deleted.")
842
940
 
843
941
    def _set_root(self, ie):
844
942
        self.root = ie
850
948
        if self.root is None:
851
949
            return Inventory(root_id=None)
852
950
        other = Inventory(entries.next()[1].file_id)
853
 
        other.root.revision = self.root.revision
854
951
        # copy recursively so we know directories will be added before
855
952
        # their children.  There are more efficient ways than this...
856
953
        for path, entry in entries:
1076
1173
                                         self._byid[entry.file_id])
1077
1174
 
1078
1175
        if entry.parent_id is None:
 
1176
            assert self.root is None and len(self._byid) == 0
1079
1177
            self.root = entry
1080
1178
        else:
1081
1179
            try:
1127
1225
        False
1128
1226
        """
1129
1227
        ie = self[file_id]
 
1228
 
 
1229
        assert ie.parent_id is None or \
 
1230
            self[ie.parent_id].children[ie.name] == ie
 
1231
        
1130
1232
        del self._byid[file_id]
1131
1233
        if ie.parent_id is not None:
1132
1234
            del self[ie.parent_id].children[ie.name]
1220
1322
                if children is None:
1221
1323
                    return None
1222
1324
                cie = children[f]
 
1325
                assert cie.name == f
 
1326
                assert cie.parent_id == parent.file_id
1223
1327
                parent = cie
1224
1328
            except KeyError:
1225
1329
                # or raise an error?
1318
1422
 
1319
1423
    :raises InvalidNormalization: When name is not normalized, and cannot be
1320
1424
        accessed on this platform by the normalized path.
1321
 
    :return: The NFC normalised version of name.
 
1425
    :return: The NFC/NFKC normalised version of name.
1322
1426
    """
1323
1427
    #------- This has been copied to bzrlib.dirstate.DirState.add, please
1324
1428
    # keep them synchronised.