/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: Robert Collins
  • Date: 2010-05-11 08:36:16 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100511083616-b8fjb19zomwupid0
Make all lock methods return Result objects, rather than lock_read returning self, as per John's review.

Show diffs side-by-side

added added

removed removed

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