/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/bundle/bundle_data.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Read in a bundle stream, and process it into a BundleReader object."""
18
18
 
19
19
import base64
20
 
from io import BytesIO
 
20
from cStringIO import StringIO
21
21
import os
22
22
import pprint
23
23
 
24
 
from ... import (
25
 
    cache_utf8,
 
24
from bzrlib import (
26
25
    osutils,
27
26
    timestamp,
28
27
    )
29
 
from . import apply_bundle
30
 
from ...errors import (
31
 
    TestamentMismatch,
32
 
    BzrError,
33
 
    NoSuchId,
34
 
    )
35
 
from ..inventory import (
36
 
    Inventory,
37
 
    InventoryDirectory,
38
 
    InventoryFile,
39
 
    InventoryLink,
40
 
    )
41
 
from ..inventorytree import InventoryTree
42
 
from ...osutils import sha_string, sha_strings, pathjoin
43
 
from ...revision import Revision, NULL_REVISION
44
 
from ..testament import StrictTestament
45
 
from ...trace import mutter, warning
46
 
from ...tree import (
47
 
    InterTree,
48
 
    Tree,
49
 
    )
50
 
from ..xml5 import serializer_v5
 
28
import bzrlib.errors
 
29
from bzrlib.bundle import apply_bundle
 
30
from bzrlib.errors import (TestamentMismatch, BzrError,
 
31
                           MalformedHeader, MalformedPatches, NotABundle)
 
32
from bzrlib.inventory import (Inventory, InventoryEntry,
 
33
                              InventoryDirectory, InventoryFile,
 
34
                              InventoryLink)
 
35
from bzrlib.osutils import sha_file, sha_string, pathjoin
 
36
from bzrlib.revision import Revision, NULL_REVISION
 
37
from bzrlib.testament import StrictTestament
 
38
from bzrlib.trace import mutter, warning
 
39
import bzrlib.transport
 
40
from bzrlib.tree import Tree
 
41
import bzrlib.urlutils
 
42
from bzrlib.xml5 import serializer_v5
51
43
 
52
44
 
53
45
class RevisionInfo(object):
54
46
    """Gets filled out for each revision object that is read.
55
47
    """
56
 
 
57
48
    def __init__(self, revision_id):
58
49
        self.revision_id = revision_id
59
50
        self.sha1 = None
74
65
 
75
66
    def as_revision(self):
76
67
        rev = Revision(revision_id=self.revision_id,
77
 
                       committer=self.committer,
78
 
                       timestamp=float(self.timestamp),
79
 
                       timezone=int(self.timezone),
80
 
                       inventory_sha1=self.inventory_sha1,
81
 
                       message='\n'.join(self.message))
 
68
            committer=self.committer,
 
69
            timestamp=float(self.timestamp),
 
70
            timezone=int(self.timezone),
 
71
            inventory_sha1=self.inventory_sha1,
 
72
            message='\n'.join(self.message))
82
73
 
83
74
        if self.parent_ids:
84
75
            rev.parent_ids.extend(self.parent_ids)
93
84
                    value = ''
94
85
                else:
95
86
                    key = str(property[:key_end])
96
 
                    value = property[key_end + 2:]
 
87
                    value = property[key_end+2:]
97
88
                rev.properties[key] = value
98
89
 
99
90
        return rev
108
99
        revision_info.timestamp = revision.timestamp
109
100
        revision_info.message = revision.message.split('\n')
110
101
        revision_info.properties = [': '.join(p) for p in
111
 
                                    revision.properties.items()]
 
102
                                    revision.properties.iteritems()]
112
103
        return revision_info
113
104
 
114
105
 
116
107
    """This contains the meta information. Stuff that allows you to
117
108
    recreate the revision or inventory XML.
118
109
    """
119
 
 
120
110
    def __init__(self, bundle_format=None):
121
111
        self.bundle_format = None
122
112
        self.committer = None
146
136
        split up, based on the assumptions that can be made
147
137
        when information is missing.
148
138
        """
149
 
        from breezy.timestamp import unpack_highres_date
 
139
        from bzrlib.timestamp import unpack_highres_date
150
140
        # Put in all of the guessable information.
151
141
        if not self.timestamp and self.date:
152
142
            self.timestamp, self.timezone = unpack_highres_date(self.date)
156
146
            if rev.timestamp is None:
157
147
                if rev.date is not None:
158
148
                    rev.timestamp, rev.timezone = \
159
 
                        unpack_highres_date(rev.date)
 
149
                            unpack_highres_date(rev.date)
160
150
                else:
161
151
                    rev.timestamp = self.timestamp
162
152
                    rev.timezone = self.timezone
211
201
        revision_info = self.get_revision_info(revision_id)
212
202
        inventory_revision_id = revision_id
213
203
        bundle_tree = BundleTree(repository.revision_tree(base),
214
 
                                 inventory_revision_id)
 
204
                                  inventory_revision_id)
215
205
        self._update_tree(bundle_tree, revision_id)
216
206
 
217
207
        inv = bundle_tree.inventory
218
208
        self._validate_inventory(inv, revision_id)
219
 
        self._validate_revision(bundle_tree, revision_id)
 
209
        self._validate_revision(inv, revision_id)
220
210
 
221
211
        return bundle_tree
222
212
 
227
217
        """
228
218
        rev_to_sha = {}
229
219
        inv_to_sha = {}
230
 
 
231
220
        def add_sha(d, revision_id, sha1):
232
221
            if revision_id is None:
233
222
                if sha1 is not None:
234
223
                    raise BzrError('A Null revision should always'
235
 
                                   'have a null sha1 hash')
 
224
                        'have a null sha1 hash')
236
225
                return
237
226
            if revision_id in d:
238
227
                # This really should have been validated as part
239
228
                # of _validate_revisions but lets do it again
240
229
                if sha1 != d[revision_id]:
241
230
                    raise BzrError('** Revision %r referenced with 2 different'
242
 
                                   ' sha hashes %s != %s' % (revision_id,
243
 
                                                             sha1, d[revision_id]))
 
231
                            ' sha hashes %s != %s' % (revision_id,
 
232
                                sha1, d[revision_id]))
244
233
            else:
245
234
                d[revision_id] = sha1
246
235
 
256
245
 
257
246
        count = 0
258
247
        missing = {}
259
 
        for revision_id, sha1 in rev_to_sha.items():
 
248
        for revision_id, sha1 in rev_to_sha.iteritems():
260
249
            if repository.has_revision(revision_id):
261
250
                testament = StrictTestament.from_revision(repository,
262
251
                                                          revision_id)
264
253
                                                                revision_id)
265
254
                if sha1 != local_sha1:
266
255
                    raise BzrError('sha1 mismatch. For revision id {%s}'
267
 
                                   'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
 
256
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
268
257
                else:
269
258
                    count += 1
270
259
            elif revision_id not in checked:
282
271
        so build up an inventory, and make sure the hashes match.
283
272
        """
284
273
        # Now we should have a complete inventory entry.
285
 
        cs = serializer_v5.write_inventory_to_chunks(inv)
286
 
        sha1 = sha_strings(cs)
 
274
        s = serializer_v5.write_inventory_to_string(inv)
 
275
        sha1 = sha_string(s)
287
276
        # Target revision is the last entry in the real_revisions list
288
277
        rev = self.get_revision(revision_id)
289
278
        if rev.revision_id != revision_id:
290
279
            raise AssertionError()
291
280
        if sha1 != rev.inventory_sha1:
292
 
            with open(',,bogus-inv', 'wb') as f:
293
 
                f.writelines(cs)
 
281
            f = open(',,bogus-inv', 'wb')
 
282
            try:
 
283
                f.write(s)
 
284
            finally:
 
285
                f.close()
294
286
            warning('Inventory sha hash mismatch for revision %s. %s'
295
287
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
296
288
 
297
 
    def _testament(self, revision, tree):
298
 
        raise NotImplementedError(self._testament)
299
 
 
300
 
    def _validate_revision(self, tree, revision_id):
 
289
    def _validate_revision(self, inventory, revision_id):
301
290
        """Make sure all revision entries match their checksum."""
302
291
 
303
 
        # This is a mapping from each revision id to its sha hash
 
292
        # This is a mapping from each revision id to it's sha hash
304
293
        rev_to_sha1 = {}
305
294
 
306
295
        rev = self.get_revision(revision_id)
309
298
            raise AssertionError()
310
299
        if not (rev.revision_id == revision_id):
311
300
            raise AssertionError()
312
 
        testament = self._testament(rev, tree)
313
 
        sha1 = testament.as_sha1()
 
301
        sha1 = self._testament_sha1(rev, inventory)
314
302
        if sha1 != rev_info.sha1:
315
303
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
316
304
        if rev.revision_id in rev_to_sha1:
317
305
            raise BzrError('Revision {%s} given twice in the list'
318
 
                           % (rev.revision_id))
 
306
                    % (rev.revision_id))
319
307
        rev_to_sha1[rev.revision_id] = sha1
320
308
 
321
309
    def _update_tree(self, bundle_tree, revision_id):
329
317
            if last_changed is not None:
330
318
                # last_changed will be a Unicode string because of how it was
331
319
                # read. Convert it back to utf8.
332
 
                changed_revision_id = cache_utf8.encode(last_changed)
 
320
                changed_revision_id = osutils.safe_revision_id(last_changed,
 
321
                                                               warn=False)
333
322
            else:
334
323
                changed_revision_id = revision_id
335
324
            bundle_tree.note_last_changed(path, changed_revision_id)
356
345
 
357
346
        def do_patch(path, lines, encoding):
358
347
            if encoding == 'base64':
359
 
                patch = base64.b64decode(b''.join(lines))
 
348
                patch = base64.decodestring(''.join(lines))
360
349
            elif encoding is None:
361
 
                patch = b''.join(lines)
 
350
                patch =  ''.join(lines)
362
351
            else:
363
352
                raise ValueError(encoding)
364
353
            bundle_tree.note_patch(path, patch)
367
356
            info = extra.split(' // ')
368
357
            if len(info) < 2:
369
358
                raise BzrError('renamed action lines need both a from and to'
370
 
                               ': %r' % extra)
 
359
                        ': %r' % extra)
371
360
            old_path = info[0]
372
361
            if info[1].startswith('=> '):
373
362
                new_path = info[1][3:]
386
375
                # TODO: in the future we might allow file ids to be
387
376
                # given for removed entries
388
377
                raise BzrError('removed action lines should only have the path'
389
 
                               ': %r' % extra)
 
378
                        ': %r' % extra)
390
379
            path = info[0]
391
380
            bundle_tree.note_deletion(path)
392
381
 
394
383
            info = extra.split(' // ')
395
384
            if len(info) <= 1:
396
385
                raise BzrError('add action lines require the path and file id'
397
 
                               ': %r' % extra)
 
386
                        ': %r' % extra)
398
387
            elif len(info) > 5:
399
388
                raise BzrError('add action lines have fewer than 5 entries.'
400
 
                               ': %r' % extra)
 
389
                        ': %r' % extra)
401
390
            path = info[0]
402
391
            if not info[1].startswith('file-id:'):
403
392
                raise BzrError('The file-id should follow the path for an add'
404
 
                               ': %r' % extra)
 
393
                        ': %r' % extra)
405
394
            # This will be Unicode because of how the stream is read. Turn it
406
395
            # back into a utf8 file_id
407
 
            file_id = cache_utf8.encode(info[1][8:])
 
396
            file_id = osutils.safe_file_id(info[1][8:], warn=False)
408
397
 
409
398
            bundle_tree.note_id(file_id, path, kind)
410
399
            # this will be overridden in extra_info if executable is specified.
419
408
            info = extra.split(' // ')
420
409
            if len(info) < 1:
421
410
                raise BzrError('modified action lines have at least'
422
 
                               'the path in them: %r' % extra)
 
411
                        'the path in them: %r' % extra)
423
412
            path = info[0]
424
413
 
425
414
            last_modified, encoding = extra_info(info[1:], path)
428
417
                do_patch(path, lines, encoding)
429
418
 
430
419
        valid_actions = {
431
 
            'renamed': renamed,
432
 
            'removed': removed,
433
 
            'added': added,
434
 
            'modified': modified
 
420
            'renamed':renamed,
 
421
            'removed':removed,
 
422
            'added':added,
 
423
            'modified':modified
435
424
        }
436
425
        for action_line, lines in \
437
 
                self.get_revision_info(revision_id).tree_actions:
 
426
            self.get_revision_info(revision_id).tree_actions:
438
427
            first = action_line.find(' ')
439
428
            if first == -1:
440
429
                raise BzrError('Bogus action line'
441
 
                               ' (no opening space): %r' % action_line)
442
 
            second = action_line.find(' ', first + 1)
 
430
                        ' (no opening space): %r' % action_line)
 
431
            second = action_line.find(' ', first+1)
443
432
            if second == -1:
444
433
                raise BzrError('Bogus action line'
445
 
                               ' (missing second space): %r' % action_line)
 
434
                        ' (missing second space): %r' % action_line)
446
435
            action = action_line[:first]
447
 
            kind = action_line[first + 1:second]
 
436
            kind = action_line[first+1:second]
448
437
            if kind not in ('file', 'directory', 'symlink'):
449
438
                raise BzrError('Bogus action line'
450
 
                               ' (invalid object kind %r): %r' % (kind, action_line))
451
 
            extra = action_line[second + 1:]
 
439
                        ' (invalid object kind %r): %r' % (kind, action_line))
 
440
            extra = action_line[second+1:]
452
441
 
453
442
            if action not in valid_actions:
454
443
                raise BzrError('Bogus action line'
455
 
                               ' (unrecognized action): %r' % action_line)
 
444
                        ' (unrecognized action): %r' % action_line)
456
445
            valid_actions[action](kind, extra, lines)
457
446
 
458
447
    def install_revisions(self, target_repo, stream_input=True):
472
461
        return None, self.target, 'inapplicable'
473
462
 
474
463
 
475
 
class BundleTree(InventoryTree):
476
 
 
 
464
class BundleTree(Tree):
477
465
    def __init__(self, base_tree, revision_id):
478
466
        self.base_tree = base_tree
479
 
        self._renamed = {}  # Mapping from old_path => new_path
480
 
        self._renamed_r = {}  # new_path => old_path
481
 
        self._new_id = {}  # new_path => new_id
482
 
        self._new_id_r = {}  # new_id => new_path
483
 
        self._kinds = {}  # new_path => kind
484
 
        self._last_changed = {}  # new_id => revision_id
485
 
        self._executable = {}  # new_id => executable value
 
467
        self._renamed = {} # Mapping from old_path => new_path
 
468
        self._renamed_r = {} # new_path => old_path
 
469
        self._new_id = {} # new_path => new_id
 
470
        self._new_id_r = {} # new_id => new_path
 
471
        self._kinds = {} # new_id => kind
 
472
        self._last_changed = {} # new_id => revision_id
 
473
        self._executable = {} # new_id => executable value
486
474
        self.patches = {}
487
 
        self._targets = {}  # new path => new symlink target
 
475
        self._targets = {} # new path => new symlink target
488
476
        self.deleted = []
 
477
        self.contents_by_id = True
489
478
        self.revision_id = revision_id
490
479
        self._inventory = None
491
 
        self._base_inter = InterTree.get(self.base_tree, self)
492
480
 
493
481
    def __str__(self):
494
482
        return pprint.pformat(self.__dict__)
506
494
        """Files that don't exist in base need a new id."""
507
495
        self._new_id[new_path] = new_id
508
496
        self._new_id_r[new_id] = new_path
509
 
        self._kinds[new_path] = kind
 
497
        self._kinds[new_id] = kind
510
498
 
511
499
    def note_last_changed(self, file_id, revision_id):
512
500
        if (file_id in self._last_changed
513
501
                and self._last_changed[file_id] != revision_id):
514
502
            raise BzrError('Mismatched last-changed revision for file_id {%s}'
515
 
                           ': %s != %s' % (file_id,
516
 
                                           self._last_changed[file_id],
517
 
                                           revision_id))
 
503
                    ': %s != %s' % (file_id,
 
504
                                    self._last_changed[file_id],
 
505
                                    revision_id))
518
506
        self._last_changed[file_id] = revision_id
519
507
 
520
508
    def note_patch(self, new_path, patch):
539
527
        old_path = self._renamed.get(new_path)
540
528
        if old_path is not None:
541
529
            return old_path
542
 
        dirname, basename = os.path.split(new_path)
 
530
        dirname,basename = os.path.split(new_path)
543
531
        # dirname is not '' doesn't work, because
544
532
        # dirname may be a unicode entry, and is
545
533
        # requires the objects to be identical
551
539
                old_path = pathjoin(old_dir, basename)
552
540
        else:
553
541
            old_path = new_path
554
 
        # If the new path wasn't in renamed, the old one shouldn't be in
555
 
        # renamed_r
 
542
        #If the new path wasn't in renamed, the old one shouldn't be in
 
543
        #renamed_r
556
544
        if old_path in self._renamed_r:
557
545
            return None
558
546
        return old_path
568
556
            return new_path
569
557
        if new_path in self._renamed:
570
558
            return None
571
 
        dirname, basename = os.path.split(old_path)
 
559
        dirname,basename = os.path.split(old_path)
572
560
        if dirname != '':
573
561
            new_dir = self.new_path(dirname)
574
562
            if new_dir is None:
577
565
                new_path = pathjoin(new_dir, basename)
578
566
        else:
579
567
            new_path = old_path
580
 
        # If the old path wasn't in renamed, the new one shouldn't be in
581
 
        # renamed_r
 
568
        #If the old path wasn't in renamed, the new one shouldn't be in
 
569
        #renamed_r
582
570
        if new_path in self._renamed:
583
571
            return None
584
572
        return new_path
593
581
            return None
594
582
        if old_path in self.deleted:
595
583
            return None
596
 
        return self.base_tree.path2id(old_path)
 
584
        if getattr(self.base_tree, 'path2id', None) is not None:
 
585
            return self.base_tree.path2id(old_path)
 
586
        else:
 
587
            return self.base_tree.inventory.path2id(old_path)
597
588
 
598
 
    def id2path(self, file_id, recurse='down'):
 
589
    def id2path(self, file_id):
599
590
        """Return the new path in the target tree of the file with id file_id"""
600
591
        path = self._new_id_r.get(file_id)
601
592
        if path is not None:
602
593
            return path
603
 
        old_path = self.base_tree.id2path(file_id, recurse)
 
594
        old_path = self.base_tree.id2path(file_id)
604
595
        if old_path is None:
605
 
            raise NoSuchId(file_id, self)
 
596
            return None
606
597
        if old_path in self.deleted:
607
 
            raise NoSuchId(file_id, self)
608
 
        new_path = self.new_path(old_path)
609
 
        if new_path is None:
610
 
            raise NoSuchId(file_id, self)
611
 
        return new_path
612
 
 
613
 
    def get_file(self, path):
 
598
            return None
 
599
        return self.new_path(old_path)
 
600
 
 
601
    def old_contents_id(self, file_id):
 
602
        """Return the id in the base_tree for the given file_id.
 
603
        Return None if the file did not exist in base.
 
604
        """
 
605
        if self.contents_by_id:
 
606
            if self.base_tree.has_id(file_id):
 
607
                return file_id
 
608
            else:
 
609
                return None
 
610
        new_path = self.id2path(file_id)
 
611
        return self.base_tree.path2id(new_path)
 
612
 
 
613
    def get_file(self, file_id):
614
614
        """Return a file-like object containing the new contents of the
615
615
        file given by file_id.
616
616
 
618
618
                in the text-store, so that the file contents would
619
619
                then be cached.
620
620
        """
621
 
        old_path = self._base_inter.find_source_path(path)
622
 
        if old_path is None:
 
621
        base_id = self.old_contents_id(file_id)
 
622
        if (base_id is not None and
 
623
            base_id != self.base_tree.inventory.root.file_id):
 
624
            patch_original = self.base_tree.get_file(base_id)
 
625
        else:
623
626
            patch_original = None
624
 
        else:
625
 
            patch_original = self.base_tree.get_file(old_path)
626
 
        file_patch = self.patches.get(path)
 
627
        file_patch = self.patches.get(self.id2path(file_id))
627
628
        if file_patch is None:
628
629
            if (patch_original is None and
629
 
                    self.kind(path) == 'directory'):
630
 
                return BytesIO()
 
630
                self.get_kind(file_id) == 'directory'):
 
631
                return StringIO()
631
632
            if patch_original is None:
632
633
                raise AssertionError("None: %s" % file_id)
633
634
            return patch_original
634
635
 
635
 
        if file_patch.startswith(b'\\'):
 
636
        if file_patch.startswith('\\'):
636
637
            raise ValueError(
637
638
                'Malformed patch for %s, %r' % (file_id, file_patch))
638
639
        return patched_file(file_patch, patch_original)
639
640
 
640
 
    def get_symlink_target(self, path):
641
 
        try:
642
 
            return self._targets[path]
643
 
        except KeyError:
644
 
            old_path = self.old_path(path)
645
 
            return self.base_tree.get_symlink_target(old_path)
646
 
 
647
 
    def kind(self, path):
648
 
        try:
649
 
            return self._kinds[path]
650
 
        except KeyError:
651
 
            old_path = self.old_path(path)
652
 
            return self.base_tree.kind(old_path)
653
 
 
654
 
    def get_file_revision(self, path):
655
 
        if path in self._last_changed:
656
 
            return self._last_changed[path]
657
 
        else:
658
 
            old_path = self.old_path(path)
659
 
            return self.base_tree.get_file_revision(old_path)
660
 
 
661
 
    def is_executable(self, path):
 
641
    def get_symlink_target(self, file_id):
 
642
        new_path = self.id2path(file_id)
 
643
        try:
 
644
            return self._targets[new_path]
 
645
        except KeyError:
 
646
            return self.base_tree.get_symlink_target(file_id)
 
647
 
 
648
    def get_kind(self, file_id):
 
649
        if file_id in self._kinds:
 
650
            return self._kinds[file_id]
 
651
        return self.base_tree.inventory[file_id].kind
 
652
 
 
653
    def is_executable(self, file_id):
 
654
        path = self.id2path(file_id)
662
655
        if path in self._executable:
663
656
            return self._executable[path]
664
657
        else:
665
 
            old_path = self.old_path(path)
666
 
            return self.base_tree.is_executable(old_path)
 
658
            return self.base_tree.inventory[file_id].executable
667
659
 
668
 
    def get_last_changed(self, path):
 
660
    def get_last_changed(self, file_id):
 
661
        path = self.id2path(file_id)
669
662
        if path in self._last_changed:
670
663
            return self._last_changed[path]
671
 
        old_path = self.old_path(path)
672
 
        return self.base_tree.get_file_revision(old_path)
 
664
        return self.base_tree.inventory[file_id].revision
673
665
 
674
 
    def get_size_and_sha1(self, new_path):
 
666
    def get_size_and_sha1(self, file_id):
675
667
        """Return the size and sha1 hash of the given file id.
676
668
        If the file was not locally modified, this is extracted
677
669
        from the base_tree. Rather than re-reading the file.
678
670
        """
 
671
        new_path = self.id2path(file_id)
679
672
        if new_path is None:
680
673
            return None, None
681
674
        if new_path not in self.patches:
682
675
            # If the entry does not have a patch, then the
683
676
            # contents must be the same as in the base_tree
684
 
            base_path = self.old_path(new_path)
685
 
            text_size = self.base_tree.get_file_size(base_path)
686
 
            text_sha1 = self.base_tree.get_file_sha1(base_path)
687
 
            return text_size, text_sha1
688
 
        fileobj = self.get_file(new_path)
 
677
            ie = self.base_tree.inventory[file_id]
 
678
            if ie.text_size is None:
 
679
                return ie.text_size, ie.text_sha1
 
680
            return int(ie.text_size), ie.text_sha1
 
681
        fileobj = self.get_file(file_id)
689
682
        content = fileobj.read()
690
683
        return len(content), sha_string(content)
691
684
 
695
688
        This need to be called before ever accessing self.inventory
696
689
        """
697
690
        from os.path import dirname, basename
 
691
        base_inv = self.base_tree.inventory
698
692
        inv = Inventory(None, self.revision_id)
699
693
 
700
 
        def add_entry(path, file_id):
 
694
        def add_entry(file_id):
 
695
            path = self.id2path(file_id)
 
696
            if path is None:
 
697
                return
701
698
            if path == '':
702
699
                parent_id = None
703
700
            else:
704
701
                parent_path = dirname(path)
705
702
                parent_id = self.path2id(parent_path)
706
703
 
707
 
            kind = self.kind(path)
708
 
            revision_id = self.get_last_changed(path)
 
704
            kind = self.get_kind(file_id)
 
705
            revision_id = self.get_last_changed(file_id)
709
706
 
710
707
            name = basename(path)
711
708
            if kind == 'directory':
712
709
                ie = InventoryDirectory(file_id, name, parent_id)
713
710
            elif kind == 'file':
714
711
                ie = InventoryFile(file_id, name, parent_id)
715
 
                ie.executable = self.is_executable(path)
 
712
                ie.executable = self.is_executable(file_id)
716
713
            elif kind == 'symlink':
717
714
                ie = InventoryLink(file_id, name, parent_id)
718
 
                ie.symlink_target = self.get_symlink_target(path)
 
715
                ie.symlink_target = self.get_symlink_target(file_id)
719
716
            ie.revision = revision_id
720
717
 
721
 
            if kind == 'file':
722
 
                ie.text_size, ie.text_sha1 = self.get_size_and_sha1(path)
723
 
                if ie.text_size is None:
724
 
                    raise BzrError(
725
 
                        'Got a text_size of None for file_id %r' % file_id)
 
718
            if kind in ('directory', 'symlink'):
 
719
                ie.text_size, ie.text_sha1 = None, None
 
720
            else:
 
721
                ie.text_size, ie.text_sha1 = self.get_size_and_sha1(file_id)
 
722
            if (ie.text_size is None) and (kind == 'file'):
 
723
                raise BzrError('Got a text_size of None for file_id %r' % file_id)
726
724
            inv.add(ie)
727
725
 
728
726
        sorted_entries = self.sorted_path_id()
729
727
        for path, file_id in sorted_entries:
730
 
            add_entry(path, file_id)
 
728
            add_entry(file_id)
731
729
 
732
730
        return inv
733
731
 
738
736
    # at that instant
739
737
    inventory = property(_get_inventory)
740
738
 
741
 
    root_inventory = property(_get_inventory)
742
 
 
743
 
    def all_file_ids(self):
744
 
        return {entry.file_id for path, entry in self.inventory.iter_entries()}
745
 
 
746
 
    def all_versioned_paths(self):
747
 
        return {path for path, entry in self.inventory.iter_entries()}
748
 
 
749
 
    def list_files(self, include_root=False, from_dir=None, recursive=True):
750
 
        # The only files returned by this are those from the version
751
 
        inv = self.inventory
752
 
        if from_dir is None:
753
 
            from_dir_id = None
754
 
        else:
755
 
            from_dir_id = inv.path2id(from_dir)
756
 
            if from_dir_id is None:
757
 
                # Directory not versioned
758
 
                return
759
 
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
760
 
        if inv.root is not None and not include_root and from_dir is None:
761
 
            # skip the root for compatibility with the current apis.
762
 
            next(entries)
763
 
        for path, entry in entries:
764
 
            yield path, 'V', entry.kind, entry
 
739
    def __iter__(self):
 
740
        for path, entry in self.inventory.iter_entries():
 
741
            yield entry.file_id
765
742
 
766
743
    def sorted_path_id(self):
767
744
        paths = []
768
 
        for result in self._new_id.items():
 
745
        for result in self._new_id.iteritems():
769
746
            paths.append(result)
770
 
        for id in self.base_tree.all_file_ids():
771
 
            try:
772
 
                path = self.id2path(id, recurse='none')
773
 
            except NoSuchId:
 
747
        for id in self.base_tree:
 
748
            path = self.id2path(id)
 
749
            if path is None:
774
750
                continue
775
751
            paths.append((path, id))
776
752
        paths.sort()
779
755
 
780
756
def patched_file(file_patch, original):
781
757
    """Produce a file-like object with the patched version of a text"""
782
 
    from breezy.patches import iter_patched
783
 
    from breezy.iterablefile import IterableFile
784
 
    if file_patch == b"":
 
758
    from bzrlib.patches import iter_patched
 
759
    from bzrlib.iterablefile import IterableFile
 
760
    if file_patch == "":
785
761
        return IterableFile(())
786
762
    # string.splitlines(True) also splits on '\r', but the iter_patched code
787
763
    # only expects to iterate over '\n' style lines
788
764
    return IterableFile(iter_patched(original,
789
 
                                     BytesIO(file_patch).readlines()))
 
765
                StringIO(file_patch).readlines()))