/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 breezy/bundle/bundle_data.py

  • Committer: Jelmer Vernooij
  • Date: 2018-05-06 11:48:54 UTC
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@jelmer.uk-20180506114854-h4qd9ojaqy8wxjsd
Move .mailmap to root.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
from __future__ import absolute_import
20
20
 
21
21
import base64
22
 
from io import BytesIO
23
22
import os
24
23
import pprint
25
24
 
26
 
from ... import (
 
25
from .. import (
27
26
    cache_utf8,
28
27
    osutils,
29
28
    timestamp,
30
29
    )
31
30
from . import apply_bundle
32
 
from ...errors import (
 
31
from ..errors import (
33
32
    TestamentMismatch,
34
33
    BzrError,
35
 
    NoSuchId,
36
34
    )
37
 
from ..inventory import (
 
35
from ..bzr.inventory import (
38
36
    Inventory,
39
37
    InventoryDirectory,
40
38
    InventoryFile,
41
39
    InventoryLink,
42
40
    )
43
 
from ...osutils import sha_string, sha_strings, pathjoin
44
 
from ...revision import Revision, NULL_REVISION
45
 
from ...sixish import (
 
41
from ..osutils import sha_string, pathjoin
 
42
from ..revision import Revision, NULL_REVISION
 
43
from ..sixish import (
 
44
    BytesIO,
46
45
    viewitems,
47
46
    )
48
47
from ..testament import StrictTestament
49
 
from ...trace import mutter, warning
50
 
from ...tree import (
51
 
    InterTree,
52
 
    Tree,
53
 
    )
54
 
from ..xml5 import serializer_v5
 
48
from ..trace import mutter, warning
 
49
from ..tree import Tree
 
50
from ..bzr.xml5 import serializer_v5
55
51
 
56
52
 
57
53
class RevisionInfo(object):
58
54
    """Gets filled out for each revision object that is read.
59
55
    """
60
 
 
61
56
    def __init__(self, revision_id):
62
57
        self.revision_id = revision_id
63
58
        self.sha1 = None
78
73
 
79
74
    def as_revision(self):
80
75
        rev = Revision(revision_id=self.revision_id,
81
 
                       committer=self.committer,
82
 
                       timestamp=float(self.timestamp),
83
 
                       timezone=int(self.timezone),
84
 
                       inventory_sha1=self.inventory_sha1,
85
 
                       message='\n'.join(self.message))
 
76
            committer=self.committer,
 
77
            timestamp=float(self.timestamp),
 
78
            timezone=int(self.timezone),
 
79
            inventory_sha1=self.inventory_sha1,
 
80
            message='\n'.join(self.message))
86
81
 
87
82
        if self.parent_ids:
88
83
            rev.parent_ids.extend(self.parent_ids)
97
92
                    value = ''
98
93
                else:
99
94
                    key = str(property[:key_end])
100
 
                    value = property[key_end + 2:]
 
95
                    value = property[key_end+2:]
101
96
                rev.properties[key] = value
102
97
 
103
98
        return rev
120
115
    """This contains the meta information. Stuff that allows you to
121
116
    recreate the revision or inventory XML.
122
117
    """
123
 
 
124
118
    def __init__(self, bundle_format=None):
125
119
        self.bundle_format = None
126
120
        self.committer = None
160
154
            if rev.timestamp is None:
161
155
                if rev.date is not None:
162
156
                    rev.timestamp, rev.timezone = \
163
 
                        unpack_highres_date(rev.date)
 
157
                            unpack_highres_date(rev.date)
164
158
                else:
165
159
                    rev.timestamp = self.timestamp
166
160
                    rev.timezone = self.timezone
215
209
        revision_info = self.get_revision_info(revision_id)
216
210
        inventory_revision_id = revision_id
217
211
        bundle_tree = BundleTree(repository.revision_tree(base),
218
 
                                 inventory_revision_id)
 
212
                                  inventory_revision_id)
219
213
        self._update_tree(bundle_tree, revision_id)
220
214
 
221
215
        inv = bundle_tree.inventory
231
225
        """
232
226
        rev_to_sha = {}
233
227
        inv_to_sha = {}
234
 
 
235
228
        def add_sha(d, revision_id, sha1):
236
229
            if revision_id is None:
237
230
                if sha1 is not None:
238
231
                    raise BzrError('A Null revision should always'
239
 
                                   'have a null sha1 hash')
 
232
                        'have a null sha1 hash')
240
233
                return
241
234
            if revision_id in d:
242
235
                # This really should have been validated as part
243
236
                # of _validate_revisions but lets do it again
244
237
                if sha1 != d[revision_id]:
245
238
                    raise BzrError('** Revision %r referenced with 2 different'
246
 
                                   ' sha hashes %s != %s' % (revision_id,
247
 
                                                             sha1, d[revision_id]))
 
239
                            ' sha hashes %s != %s' % (revision_id,
 
240
                                sha1, d[revision_id]))
248
241
            else:
249
242
                d[revision_id] = sha1
250
243
 
268
261
                                                                revision_id)
269
262
                if sha1 != local_sha1:
270
263
                    raise BzrError('sha1 mismatch. For revision id {%s}'
271
 
                                   'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
 
264
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
272
265
                else:
273
266
                    count += 1
274
267
            elif revision_id not in checked:
286
279
        so build up an inventory, and make sure the hashes match.
287
280
        """
288
281
        # Now we should have a complete inventory entry.
289
 
        cs = serializer_v5.write_inventory_to_chunks(inv)
290
 
        sha1 = sha_strings(cs)
 
282
        s = serializer_v5.write_inventory_to_string(inv)
 
283
        sha1 = sha_string(s)
291
284
        # Target revision is the last entry in the real_revisions list
292
285
        rev = self.get_revision(revision_id)
293
286
        if rev.revision_id != revision_id:
294
287
            raise AssertionError()
295
288
        if sha1 != rev.inventory_sha1:
296
289
            with open(',,bogus-inv', 'wb') as f:
297
 
                f.writelines(cs)
 
290
                f.write(s)
298
291
            warning('Inventory sha hash mismatch for revision %s. %s'
299
292
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
300
293
 
301
 
    def _testament(self, revision, tree):
302
 
        raise NotImplementedError(self._testament)
303
 
 
304
294
    def _validate_revision(self, tree, revision_id):
305
295
        """Make sure all revision entries match their checksum."""
306
296
 
313
303
            raise AssertionError()
314
304
        if not (rev.revision_id == revision_id):
315
305
            raise AssertionError()
316
 
        testament = self._testament(rev, tree)
317
 
        sha1 = testament.as_sha1()
 
306
        sha1 = self._testament_sha1(rev, tree)
318
307
        if sha1 != rev_info.sha1:
319
308
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
320
309
        if rev.revision_id in rev_to_sha1:
321
310
            raise BzrError('Revision {%s} given twice in the list'
322
 
                           % (rev.revision_id))
 
311
                    % (rev.revision_id))
323
312
        rev_to_sha1[rev.revision_id] = sha1
324
313
 
325
314
    def _update_tree(self, bundle_tree, revision_id):
360
349
 
361
350
        def do_patch(path, lines, encoding):
362
351
            if encoding == 'base64':
363
 
                patch = base64.b64decode(b''.join(lines))
 
352
                patch = base64.decodestring(''.join(lines))
364
353
            elif encoding is None:
365
 
                patch = b''.join(lines)
 
354
                patch =  ''.join(lines)
366
355
            else:
367
356
                raise ValueError(encoding)
368
357
            bundle_tree.note_patch(path, patch)
371
360
            info = extra.split(' // ')
372
361
            if len(info) < 2:
373
362
                raise BzrError('renamed action lines need both a from and to'
374
 
                               ': %r' % extra)
 
363
                        ': %r' % extra)
375
364
            old_path = info[0]
376
365
            if info[1].startswith('=> '):
377
366
                new_path = info[1][3:]
390
379
                # TODO: in the future we might allow file ids to be
391
380
                # given for removed entries
392
381
                raise BzrError('removed action lines should only have the path'
393
 
                               ': %r' % extra)
 
382
                        ': %r' % extra)
394
383
            path = info[0]
395
384
            bundle_tree.note_deletion(path)
396
385
 
398
387
            info = extra.split(' // ')
399
388
            if len(info) <= 1:
400
389
                raise BzrError('add action lines require the path and file id'
401
 
                               ': %r' % extra)
 
390
                        ': %r' % extra)
402
391
            elif len(info) > 5:
403
392
                raise BzrError('add action lines have fewer than 5 entries.'
404
 
                               ': %r' % extra)
 
393
                        ': %r' % extra)
405
394
            path = info[0]
406
395
            if not info[1].startswith('file-id:'):
407
396
                raise BzrError('The file-id should follow the path for an add'
408
 
                               ': %r' % extra)
 
397
                        ': %r' % extra)
409
398
            # This will be Unicode because of how the stream is read. Turn it
410
399
            # back into a utf8 file_id
411
400
            file_id = cache_utf8.encode(info[1][8:])
423
412
            info = extra.split(' // ')
424
413
            if len(info) < 1:
425
414
                raise BzrError('modified action lines have at least'
426
 
                               'the path in them: %r' % extra)
 
415
                        'the path in them: %r' % extra)
427
416
            path = info[0]
428
417
 
429
418
            last_modified, encoding = extra_info(info[1:], path)
432
421
                do_patch(path, lines, encoding)
433
422
 
434
423
        valid_actions = {
435
 
            'renamed': renamed,
436
 
            'removed': removed,
437
 
            'added': added,
438
 
            'modified': modified
 
424
            'renamed':renamed,
 
425
            'removed':removed,
 
426
            'added':added,
 
427
            'modified':modified
439
428
        }
440
429
        for action_line, lines in \
441
 
                self.get_revision_info(revision_id).tree_actions:
 
430
            self.get_revision_info(revision_id).tree_actions:
442
431
            first = action_line.find(' ')
443
432
            if first == -1:
444
433
                raise BzrError('Bogus action line'
445
 
                               ' (no opening space): %r' % action_line)
446
 
            second = action_line.find(' ', first + 1)
 
434
                        ' (no opening space): %r' % action_line)
 
435
            second = action_line.find(' ', first+1)
447
436
            if second == -1:
448
437
                raise BzrError('Bogus action line'
449
 
                               ' (missing second space): %r' % action_line)
 
438
                        ' (missing second space): %r' % action_line)
450
439
            action = action_line[:first]
451
 
            kind = action_line[first + 1:second]
 
440
            kind = action_line[first+1:second]
452
441
            if kind not in ('file', 'directory', 'symlink'):
453
442
                raise BzrError('Bogus action line'
454
 
                               ' (invalid object kind %r): %r' % (kind, action_line))
455
 
            extra = action_line[second + 1:]
 
443
                        ' (invalid object kind %r): %r' % (kind, action_line))
 
444
            extra = action_line[second+1:]
456
445
 
457
446
            if action not in valid_actions:
458
447
                raise BzrError('Bogus action line'
459
 
                               ' (unrecognized action): %r' % action_line)
 
448
                        ' (unrecognized action): %r' % action_line)
460
449
            valid_actions[action](kind, extra, lines)
461
450
 
462
451
    def install_revisions(self, target_repo, stream_input=True):
480
469
 
481
470
    def __init__(self, base_tree, revision_id):
482
471
        self.base_tree = base_tree
483
 
        self._renamed = {}  # Mapping from old_path => new_path
484
 
        self._renamed_r = {}  # new_path => old_path
485
 
        self._new_id = {}  # new_path => new_id
486
 
        self._new_id_r = {}  # new_id => new_path
487
 
        self._kinds = {}  # new_path => kind
488
 
        self._last_changed = {}  # new_id => revision_id
489
 
        self._executable = {}  # new_id => executable value
 
472
        self._renamed = {} # Mapping from old_path => new_path
 
473
        self._renamed_r = {} # new_path => old_path
 
474
        self._new_id = {} # new_path => new_id
 
475
        self._new_id_r = {} # new_id => new_path
 
476
        self._kinds = {} # new_path => kind
 
477
        self._last_changed = {} # new_id => revision_id
 
478
        self._executable = {} # new_id => executable value
490
479
        self.patches = {}
491
 
        self._targets = {}  # new path => new symlink target
 
480
        self._targets = {} # new path => new symlink target
492
481
        self.deleted = []
 
482
        self.contents_by_id = True
493
483
        self.revision_id = revision_id
494
484
        self._inventory = None
495
 
        self._base_inter = InterTree.get(self.base_tree, self)
496
485
 
497
486
    def __str__(self):
498
487
        return pprint.pformat(self.__dict__)
516
505
        if (file_id in self._last_changed
517
506
                and self._last_changed[file_id] != revision_id):
518
507
            raise BzrError('Mismatched last-changed revision for file_id {%s}'
519
 
                           ': %s != %s' % (file_id,
520
 
                                           self._last_changed[file_id],
521
 
                                           revision_id))
 
508
                    ': %s != %s' % (file_id,
 
509
                                    self._last_changed[file_id],
 
510
                                    revision_id))
522
511
        self._last_changed[file_id] = revision_id
523
512
 
524
513
    def note_patch(self, new_path, patch):
555
544
                old_path = pathjoin(old_dir, basename)
556
545
        else:
557
546
            old_path = new_path
558
 
        # If the new path wasn't in renamed, the old one shouldn't be in
559
 
        # renamed_r
 
547
        #If the new path wasn't in renamed, the old one shouldn't be in
 
548
        #renamed_r
560
549
        if old_path in self._renamed_r:
561
550
            return None
562
551
        return old_path
581
570
                new_path = pathjoin(new_dir, basename)
582
571
        else:
583
572
            new_path = old_path
584
 
        # If the old path wasn't in renamed, the new one shouldn't be in
585
 
        # renamed_r
 
573
        #If the old path wasn't in renamed, the new one shouldn't be in
 
574
        #renamed_r
586
575
        if new_path in self._renamed:
587
576
            return None
588
577
        return new_path
589
578
 
 
579
    def get_root_id(self):
 
580
        return self.path2id('')
 
581
 
590
582
    def path2id(self, path):
591
583
        """Return the id of the file present at path in the target tree."""
592
584
        file_id = self._new_id.get(path)
599
591
            return None
600
592
        return self.base_tree.path2id(old_path)
601
593
 
602
 
    def id2path(self, file_id, recurse='down'):
 
594
    def id2path(self, file_id):
603
595
        """Return the new path in the target tree of the file with id file_id"""
604
596
        path = self._new_id_r.get(file_id)
605
597
        if path is not None:
606
598
            return path
607
 
        old_path = self.base_tree.id2path(file_id, recurse)
 
599
        old_path = self.base_tree.id2path(file_id)
608
600
        if old_path is None:
609
 
            raise NoSuchId(file_id, self)
 
601
            return None
610
602
        if old_path in self.deleted:
611
 
            raise NoSuchId(file_id, self)
612
 
        new_path = self.new_path(old_path)
613
 
        if new_path is None:
614
 
            raise NoSuchId(file_id, self)
615
 
        return new_path
616
 
 
617
 
    def get_file(self, path):
 
603
            return None
 
604
        return self.new_path(old_path)
 
605
 
 
606
    def old_contents_id(self, file_id):
 
607
        """Return the id in the base_tree for the given file_id.
 
608
        Return None if the file did not exist in base.
 
609
        """
 
610
        if self.contents_by_id:
 
611
            if self.base_tree.has_id(file_id):
 
612
                return file_id
 
613
            else:
 
614
                return None
 
615
        new_path = self.id2path(file_id)
 
616
        return self.base_tree.path2id(new_path)
 
617
 
 
618
    def get_file(self, path, file_id=None):
618
619
        """Return a file-like object containing the new contents of the
619
620
        file given by file_id.
620
621
 
622
623
                in the text-store, so that the file contents would
623
624
                then be cached.
624
625
        """
625
 
        old_path = self._base_inter.find_source_path(path)
626
 
        if old_path is None:
 
626
        if file_id is None:
 
627
            file_id = self.path2id(path)
 
628
        base_id = self.old_contents_id(file_id)
 
629
        if (base_id is not None and
 
630
            base_id != self.base_tree.get_root_id()):
 
631
            old_path = self.old_path(path)
 
632
            patch_original = self.base_tree.get_file(
 
633
                    old_path, base_id)
 
634
        else:
627
635
            patch_original = None
628
 
        else:
629
 
            patch_original = self.base_tree.get_file(old_path)
630
636
        file_patch = self.patches.get(path)
631
637
        if file_patch is None:
632
638
            if (patch_original is None and
633
 
                    self.kind(path) == 'directory'):
 
639
                self.kind(path, file_id) == 'directory'):
634
640
                return BytesIO()
635
641
            if patch_original is None:
636
642
                raise AssertionError("None: %s" % file_id)
637
643
            return patch_original
638
644
 
639
 
        if file_patch.startswith(b'\\'):
 
645
        if file_patch.startswith('\\'):
640
646
            raise ValueError(
641
647
                'Malformed patch for %s, %r' % (file_id, file_patch))
642
648
        return patched_file(file_patch, patch_original)
643
649
 
644
 
    def get_symlink_target(self, path):
 
650
    def get_symlink_target(self, path, file_id=None):
645
651
        try:
646
652
            return self._targets[path]
647
653
        except KeyError:
648
654
            old_path = self.old_path(path)
649
 
            return self.base_tree.get_symlink_target(old_path)
 
655
            return self.base_tree.get_symlink_target(old_path, file_id)
650
656
 
651
 
    def kind(self, path):
 
657
    def kind(self, path, file_id=None):
652
658
        try:
653
659
            return self._kinds[path]
654
660
        except KeyError:
655
661
            old_path = self.old_path(path)
656
 
            return self.base_tree.kind(old_path)
 
662
            return self.base_tree.kind(old_path, file_id)
657
663
 
658
 
    def get_file_revision(self, path):
 
664
    def get_file_revision(self, path, file_id=None):
659
665
        if path in self._last_changed:
660
666
            return self._last_changed[path]
661
667
        else:
662
668
            old_path = self.old_path(path)
663
 
            return self.base_tree.get_file_revision(old_path)
 
669
            return self.base_tree.get_file_revision(old_path, file_id)
664
670
 
665
 
    def is_executable(self, path):
 
671
    def is_executable(self, path, file_id=None):
666
672
        if path in self._executable:
667
673
            return self._executable[path]
668
674
        else:
669
675
            old_path = self.old_path(path)
670
 
            return self.base_tree.is_executable(old_path)
 
676
            return self.base_tree.is_executable(old_path, file_id)
671
677
 
672
 
    def get_last_changed(self, path):
 
678
    def get_last_changed(self, path, file_id=None):
673
679
        if path in self._last_changed:
674
680
            return self._last_changed[path]
675
681
        old_path = self.old_path(path)
676
 
        return self.base_tree.get_file_revision(old_path)
 
682
        return self.base_tree.get_file_revision(old_path, file_id)
677
683
 
678
 
    def get_size_and_sha1(self, new_path):
 
684
    def get_size_and_sha1(self, new_path, file_id=None):
679
685
        """Return the size and sha1 hash of the given file id.
680
686
        If the file was not locally modified, this is extracted
681
687
        from the base_tree. Rather than re-reading the file.
686
692
            # If the entry does not have a patch, then the
687
693
            # contents must be the same as in the base_tree
688
694
            base_path = self.old_path(new_path)
689
 
            text_size = self.base_tree.get_file_size(base_path)
690
 
            text_sha1 = self.base_tree.get_file_sha1(base_path)
 
695
            text_size = self.base_tree.get_file_size(base_path, file_id)
 
696
            text_sha1 = self.base_tree.get_file_sha1(base_path, file_id)
691
697
            return text_size, text_sha1
692
 
        fileobj = self.get_file(new_path)
 
698
        fileobj = self.get_file(new_path, file_id)
693
699
        content = fileobj.read()
694
700
        return len(content), sha_string(content)
695
701
 
708
714
                parent_path = dirname(path)
709
715
                parent_id = self.path2id(parent_path)
710
716
 
711
 
            kind = self.kind(path)
712
 
            revision_id = self.get_last_changed(path)
 
717
            kind = self.kind(path, file_id)
 
718
            revision_id = self.get_last_changed(path, file_id)
713
719
 
714
720
            name = basename(path)
715
721
            if kind == 'directory':
716
722
                ie = InventoryDirectory(file_id, name, parent_id)
717
723
            elif kind == 'file':
718
724
                ie = InventoryFile(file_id, name, parent_id)
719
 
                ie.executable = self.is_executable(path)
 
725
                ie.executable = self.is_executable(path, file_id)
720
726
            elif kind == 'symlink':
721
727
                ie = InventoryLink(file_id, name, parent_id)
722
 
                ie.symlink_target = self.get_symlink_target(path)
 
728
                ie.symlink_target = self.get_symlink_target(path, file_id)
723
729
            ie.revision = revision_id
724
730
 
725
731
            if kind == 'file':
726
 
                ie.text_size, ie.text_sha1 = self.get_size_and_sha1(path)
 
732
                ie.text_size, ie.text_sha1 = self.get_size_and_sha1(
 
733
                        path, file_id)
727
734
                if ie.text_size is None:
728
735
                    raise BzrError(
729
736
                        'Got a text_size of None for file_id %r' % file_id)
762
769
                return
763
770
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
764
771
        if inv.root is not None and not include_root and from_dir is None:
765
 
            # skip the root for compatibility with the current apis.
 
772
            # skip the root for compatability with the current apis.
766
773
            next(entries)
767
774
        for path, entry in entries:
768
 
            yield path, 'V', entry.kind, entry
 
775
            yield path, 'V', entry.kind, entry.file_id, entry
769
776
 
770
777
    def sorted_path_id(self):
771
778
        paths = []
772
779
        for result in viewitems(self._new_id):
773
780
            paths.append(result)
774
781
        for id in self.base_tree.all_file_ids():
775
 
            try:
776
 
                path = self.id2path(id, recurse='none')
777
 
            except NoSuchId:
 
782
            path = self.id2path(id)
 
783
            if path is None:
778
784
                continue
779
785
            paths.append((path, id))
780
786
        paths.sort()
785
791
    """Produce a file-like object with the patched version of a text"""
786
792
    from breezy.patches import iter_patched
787
793
    from breezy.iterablefile import IterableFile
788
 
    if file_patch == b"":
 
794
    if file_patch == "":
789
795
        return IterableFile(())
790
796
    # string.splitlines(True) also splits on '\r', but the iter_patched code
791
797
    # only expects to iterate over '\n' style lines
792
798
    return IterableFile(iter_patched(original,
793
 
                                     BytesIO(file_patch).readlines()))
 
799
                BytesIO(file_patch).readlines()))