~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/knit.py

  • Committer: Martin Pool
  • Date: 2006-06-20 05:32:16 UTC
  • mfrom: (1797 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1798.
  • Revision ID: mbp@sourcefrog.net-20060620053216-817857d7ca3e9d1f
[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
# Written by Martin Pool.
3
3
# Modified by Johan Rydberg <jrydberg@gnu.org>
4
4
# Modified by Robert Collins <robert.collins@canonical.com>
 
5
# Modified by Aaron Bentley <aaron.bentley@utoronto.ca>
5
6
#
6
7
# This program is free software; you can redistribute it and/or modify
7
8
# it under the terms of the GNU General Public License as published by
67
68
from cStringIO import StringIO
68
69
import difflib
69
70
from itertools import izip, chain
 
71
import operator
70
72
import os
71
73
import sys
72
74
 
265
267
    stored and retrieved.
266
268
    """
267
269
 
268
 
    def __init__(self, relpath, transport, file_mode=None, access_mode=None, factory=None,
269
 
                 basis_knit=None, delta=True, create=False):
 
270
    def __init__(self, relpath, transport, file_mode=None, access_mode=None, 
 
271
                 factory=None, basis_knit=None, delta=True, create=False):
270
272
        """Construct a knit at location specified by relpath.
271
273
        
272
274
        :param create: If not True, only open an existing knit.
510
512
            diff_hunks.append((op[1], op[2], op[4]-op[3], new_content._lines[op[3]:op[4]]))
511
513
        return diff_hunks
512
514
 
 
515
    def _get_component_versions(self, version_id):
 
516
        basis = self.basis_knit
 
517
        needed_versions = []
 
518
        basis_versions = []
 
519
        cursor = version_id
 
520
 
 
521
        while 1:
 
522
            picked_knit = self
 
523
            if basis and basis._index.has_version(cursor):
 
524
                picked_knit = basis
 
525
                basis_versions.append(cursor)
 
526
            method = picked_knit._index.get_method(cursor)
 
527
            needed_versions.append((method, cursor))
 
528
            if method == 'fulltext':
 
529
                break
 
530
            cursor = picked_knit.get_parents(cursor)[0]
 
531
        return needed_versions, basis_versions
 
532
 
 
533
    def _get_component_positions(self, version_id):
 
534
        needed_versions, basis_versions = \
 
535
            self._get_component_versions(version_id)
 
536
        assert len(basis_versions) == 0
 
537
        positions = []
 
538
        for method, comp_id in needed_versions:
 
539
            data_pos, data_size = self._index.get_position(comp_id)
 
540
            positions.append((method, comp_id, data_pos, data_size))
 
541
        return positions
 
542
 
513
543
    def _get_components(self, version_id):
514
544
        """Return a list of (version_id, method, data) tuples that
515
545
        makes up version specified by version_id of the knit.
533
563
        # basis_revisions is a list of versions that needs to be
534
564
        # fetched but exists in the basis knit.
535
565
 
536
 
        basis = self.basis_knit
537
 
        needed_versions = []
538
 
        basis_versions = []
539
 
        cursor = version_id
540
 
 
541
 
        while 1:
542
 
            picked_knit = self
543
 
            if basis and basis._index.has_version(cursor):
544
 
                picked_knit = basis
545
 
                basis_versions.append(cursor)
546
 
            method = picked_knit._index.get_method(cursor)
547
 
            needed_versions.append((method, cursor))
548
 
            if method == 'fulltext':
549
 
                break
550
 
            cursor = picked_knit.get_parents(cursor)[0]
 
566
        needed_versions, basis_versions = \
 
567
            self._get_component_versions(version_id)
551
568
 
552
569
        components = {}
553
570
        if basis_versions:
 
571
            assert False, "I am broken"
 
572
            basis = self.basis_knit
554
573
            records = []
555
574
            for comp_id in basis_versions:
556
575
                data_pos, data_size = basis._index.get_data_position(comp_id)
603
622
 
604
623
        # digest here is the digest from the last applied component.
605
624
        if sha_strings(content.text()) != digest:
606
 
            import pdb;pdb.set_trace()
607
625
            raise KnitCorrupt(self.filename, 'sha-1 does not match %s' % version_id)
608
626
 
609
627
        return content
717
735
 
718
736
    def _clone_text(self, new_version_id, old_version_id, parents):
719
737
        """See VersionedFile.clone_text()."""
720
 
        # FIXME RBC 20060228 make fast by only inserting an index with null delta.
 
738
        # FIXME RBC 20060228 make fast by only inserting an index with null 
 
739
        # delta.
721
740
        self.add_lines(new_version_id, parents, self.get_lines(old_version_id))
722
741
 
723
742
    def get_lines(self, version_id):
724
743
        """See VersionedFile.get_lines()."""
725
 
        return self._get_content(version_id).text()
 
744
        return self.get_line_list([version_id])[0]
 
745
 
 
746
    def _get_version_components(self, position_map):
 
747
        records = []
 
748
        for version_id, positions in position_map.iteritems():
 
749
            for method, comp_id, position, size in positions:
 
750
                records.append((comp_id, position, size))
 
751
        record_map = self._data.read_records(records)
 
752
 
 
753
        component_map = {}
 
754
        for version_id, positions in position_map.iteritems():
 
755
            components = []
 
756
            for method, comp_id, position, size in positions:
 
757
                data, digest = record_map[comp_id]
 
758
                components.append((comp_id, method, data, digest))
 
759
            component_map[version_id] = components
 
760
        return component_map
 
761
 
 
762
    def get_text(self, version_id):
 
763
        """See VersionedFile.get_text"""
 
764
        return self.get_texts([version_id])[0]
 
765
 
 
766
    def get_texts(self, version_ids):
 
767
        return [''.join(l) for l in self.get_line_list(version_ids)]
 
768
 
 
769
    def get_line_list(self, version_ids):
 
770
        """Return the texts of listed versions as a list of strings."""
 
771
        position_map = {}
 
772
        for version_id in version_ids:
 
773
            if not self.has_version(version_id):
 
774
                raise RevisionNotPresent(version_id, self.filename)
 
775
            position_map[version_id] = \
 
776
                self._get_component_positions(version_id)
 
777
 
 
778
        version_components = self._get_version_components(position_map).items()
 
779
 
 
780
        text_map = {}
 
781
        for version_id, components in version_components:
 
782
            content = None
 
783
            for component_id, method, data, digest in reversed(components):
 
784
                version_idx = self._index.lookup(component_id)
 
785
                if method == 'fulltext':
 
786
                    assert content is None
 
787
                    content = self.factory.parse_fulltext(data, version_idx)
 
788
                elif method == 'line-delta':
 
789
                    delta = self.factory.parse_line_delta(data, version_idx)
 
790
                    content._lines = self._apply_delta(content._lines, delta)
 
791
 
 
792
            if 'no-eol' in self._index.get_options(version_id):
 
793
                line = content._lines[-1][1].rstrip('\n')
 
794
                content._lines[-1] = (content._lines[-1][0], line)
 
795
 
 
796
            # digest here is the digest from the last applied component.
 
797
            if sha_strings(content.text()) != digest:
 
798
                raise KnitCorrupt(self.filename, 
 
799
                                  'sha-1 does not match %s' % version_id)
 
800
 
 
801
            text_map[version_id] = content.text()
 
802
        return [text_map[v] for v in version_ids]
726
803
 
727
804
    def iter_lines_added_or_present_in_versions(self, version_ids=None):
728
805
        """See VersionedFile.iter_lines_added_or_present_in_versions()."""
1373
1450
                needed_records.append((version_id, pos, size))
1374
1451
 
1375
1452
        if len(needed_records):
 
1453
            needed_records.sort(key=operator.itemgetter(1))
1376
1454
            # We take it that the transport optimizes the fetching as good
1377
1455
            # as possible (ie, reads continuous ranges.)
1378
1456
            response = self._transport.readv(self._filename,
1379
1457
                [(pos, size) for version_id, pos, size in needed_records])
1380
1458
 
1381
 
            for (record_id, pos, size), (pos, data) in izip(iter(needed_records), response):
 
1459
            for (record_id, pos, size), (pos, data) in \
 
1460
                izip(iter(needed_records), response):
1382
1461
                content, digest = self._parse_record(record_id, data)
1383
1462
                self._records[record_id] = (digest, content)
1384
1463