~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-31 16:12:57 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060731161257-91a231523255332c
new official bzr.ico

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
import time
22
22
from unittest import TestSuite
23
23
 
24
 
from bzrlib import (
25
 
    bzrdir, 
26
 
    check, 
27
 
    delta, 
28
 
    gpg, 
29
 
    errors, 
30
 
    osutils,
31
 
    transactions,
32
 
    ui, 
33
 
    xml5, 
34
 
    xml6,
35
 
    )
 
24
from bzrlib import bzrdir, check, delta, gpg, errors, xml5, ui, transactions, osutils
36
25
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
26
from bzrlib.errors import InvalidRevisionId
38
27
from bzrlib.graph import Graph
39
28
from bzrlib.inter import InterObject
40
 
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
 
29
from bzrlib.inventory import Inventory
41
30
from bzrlib.knit import KnitVersionedFile, KnitPlainFactory
42
31
from bzrlib.lockable_files import LockableFiles, TransportLock
43
32
from bzrlib.lockdir import LockDir
47
36
from bzrlib.revisiontree import RevisionTree
48
37
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
49
38
from bzrlib.store.text import TextStore
50
 
from bzrlib import symbol_versioning
51
39
from bzrlib.symbol_versioning import (deprecated_method,
52
40
        zero_nine, 
53
41
        )
54
42
from bzrlib.testament import Testament
55
 
from bzrlib.trace import mutter, note, warning
 
43
from bzrlib.trace import mutter, note
56
44
from bzrlib.tsort import topo_sort
57
45
from bzrlib.weave import WeaveFile
58
46
 
59
47
 
60
 
# Old formats display a warning, but only once
61
 
_deprecation_warning_done = False
62
 
 
63
 
 
64
48
class Repository(object):
65
49
    """Repository holding history for one or more branches.
66
50
 
85
69
        assert inv.revision_id is None or inv.revision_id == revid, \
86
70
            "Mismatch between inventory revision" \
87
71
            " id and insertion revid (%r, %r)" % (inv.revision_id, revid)
88
 
        assert inv.root is not None
89
 
        inv_text = self.serialise_inventory(inv)
 
72
        inv_text = xml5.serializer_v5.write_inventory_to_string(inv)
90
73
        inv_sha1 = osutils.sha_string(inv_text)
91
74
        inv_vf = self.control_weaves.get_weave('inventory',
92
75
                                               self.get_transaction())
205
188
        self.control_weaves = control_store
206
189
        # TODO: make sure to construct the right store classes, etc, depending
207
190
        # on whether escaping is required.
208
 
        self._warn_if_deprecated()
209
 
        self._serializer = xml5.serializer_v5
210
191
 
211
192
    def __repr__(self):
212
193
        return '%s(%r)' % (self.__class__.__name__, 
274
255
        :param revprops: Optional dictionary of revision properties.
275
256
        :param revision_id: Optional revision id.
276
257
        """
277
 
        return _CommitBuilder(self, parents, config, timestamp, timezone,
278
 
                              committer, revprops, revision_id)
 
258
        return CommitBuilder(self, parents, config, timestamp, timezone,
 
259
                             committer, revprops, revision_id)
279
260
 
280
261
    def unlock(self):
281
262
        self.control_files.unlock()
411
392
        revision_ids. Each altered file-ids has the exact revision_ids that
412
393
        altered it listed explicitly.
413
394
        """
414
 
        assert self._serializer.support_altered_by_hack, \
 
395
        assert isinstance(self._format, (RepositoryFormat5,
 
396
                                         RepositoryFormat6,
 
397
                                         RepositoryFormat7,
 
398
                                         RepositoryFormatKnit1)), \
415
399
            ("fileids_altered_by_revision_ids only supported for branches " 
416
400
             "which store inventory as unnested xml, not on %r" % self)
417
401
        selected_revision_ids = set(revision_ids)
426
410
        # revisions. We don't need to see all lines in the inventory because
427
411
        # only those added in an inventory in rev X can contain a revision=X
428
412
        # line.
429
 
        pb = ui.ui_factory.nested_progress_bar()
430
 
        try:
431
 
            for line in w.iter_lines_added_or_present_in_versions(
432
 
                selected_revision_ids, pb=pb):
433
 
                start = line.find('file_id="')+9
434
 
                if start < 9: continue
435
 
                end = line.find('"', start)
436
 
                assert end>= 0
437
 
                file_id = _unescape_xml(line[start:end])
 
413
        for line in w.iter_lines_added_or_present_in_versions(selected_revision_ids):
 
414
            start = line.find('file_id="')+9
 
415
            if start < 9: continue
 
416
            end = line.find('"', start)
 
417
            assert end>= 0
 
418
            file_id = _unescape_xml(line[start:end])
438
419
 
439
 
                start = line.find('revision="')+10
440
 
                if start < 10: continue
441
 
                end = line.find('"', start)
442
 
                assert end>= 0
443
 
                revision_id = _unescape_xml(line[start:end])
444
 
                if revision_id in selected_revision_ids:
445
 
                    result.setdefault(file_id, set()).add(revision_id)
446
 
        finally:
447
 
            pb.finished()
 
420
            start = line.find('revision="')+10
 
421
            if start < 10: continue
 
422
            end = line.find('"', start)
 
423
            assert end>= 0
 
424
            revision_id = _unescape_xml(line[start:end])
 
425
            if revision_id in selected_revision_ids:
 
426
                result.setdefault(file_id, set()).add(revision_id)
448
427
        return result
449
428
 
450
429
    @needs_read_lock
464
443
        :param revision_id: The expected revision id of the inventory.
465
444
        :param xml: A serialised inventory.
466
445
        """
467
 
        result = self._serializer.read_inventory_from_string(xml)
468
 
        result.root.revision = revision_id
469
 
        return result
470
 
 
471
 
    def serialise_inventory(self, inv):
472
 
        return self._serializer.write_inventory_to_string(inv)
 
446
        return xml5.serializer_v5.read_inventory_from_string(xml)
473
447
 
474
448
    @needs_read_lock
475
449
    def get_inventory_xml(self, revision_id):
706
680
        result.check()
707
681
        return result
708
682
 
709
 
    def _warn_if_deprecated(self):
710
 
        global _deprecation_warning_done
711
 
        if _deprecation_warning_done:
712
 
            return
713
 
        _deprecation_warning_done = True
714
 
        warning("Format %s for %s is deprecated - please use 'bzr upgrade' to get better performance"
715
 
                % (self._format, self.bzrdir.transport.base))
716
 
 
717
 
    def supports_rich_root(self):
718
 
        return self._format.rich_root_data
719
 
 
720
683
 
721
684
class AllInOneRepository(Repository):
722
685
    """Legacy support - the repository behaviour for all-in-one branches."""
785
748
            parent_trees[p_id] = repository.revision_tree(None)
786
749
 
787
750
    inv = revision_tree.inventory
 
751
    
 
752
    # backwards compatability hack: skip the root id.
788
753
    entries = inv.iter_entries()
789
 
    # backwards compatability hack: skip the root id.
790
 
    if not repository.supports_rich_root():
791
 
        path, root = entries.next()
792
 
        if root.revision != rev.revision_id:
793
 
            raise errors.IncompatibleRevision(repr(repository))
 
754
    entries.next()
794
755
    # Add the texts that are not already present
795
756
    for path, ie in entries:
796
757
        w = repository.weave_store.get_weave_or_empty(ie.file_id,
831
792
                                                _revision_store,
832
793
                                                control_store,
833
794
                                                text_store)
 
795
 
834
796
        dir_mode = self.control_files._dir_mode
835
797
        file_mode = self.control_files._file_mode
836
798
 
865
827
class KnitRepository(MetaDirRepository):
866
828
    """Knit format repository."""
867
829
 
868
 
    def _warn_if_deprecated(self):
869
 
        # This class isn't deprecated
870
 
        pass
871
 
 
872
830
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
873
831
        inv_vf.add_lines_with_ghosts(revid, parents, lines)
874
832
 
1011
969
        return self._get_revision_vf().get_parents(revision_id)
1012
970
 
1013
971
 
1014
 
class KnitRepository2(KnitRepository):
1015
 
    """"""
1016
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1017
 
                 control_store, text_store):
1018
 
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1019
 
                              _revision_store, control_store, text_store)
1020
 
        self._serializer = xml6.serializer_v6
1021
 
 
1022
 
    def deserialise_inventory(self, revision_id, xml):
1023
 
        """Transform the xml into an inventory object. 
1024
 
 
1025
 
        :param revision_id: The expected revision id of the inventory.
1026
 
        :param xml: A serialised inventory.
1027
 
        """
1028
 
        result = self._serializer.read_inventory_from_string(xml)
1029
 
        assert result.root.revision is not None
1030
 
        return result
1031
 
 
1032
 
    def serialise_inventory(self, inv):
1033
 
        """Transform the inventory object into XML text.
1034
 
 
1035
 
        :param revision_id: The expected revision id of the inventory.
1036
 
        :param xml: A serialised inventory.
1037
 
        """
1038
 
        assert inv.revision_id is not None
1039
 
        assert inv.root.revision is not None
1040
 
        return KnitRepository.serialise_inventory(self, inv)
1041
 
 
1042
 
    def get_commit_builder(self, branch, parents, config, timestamp=None, 
1043
 
                           timezone=None, committer=None, revprops=None, 
1044
 
                           revision_id=None):
1045
 
        """Obtain a CommitBuilder for this repository.
1046
 
        
1047
 
        :param branch: Branch to commit to.
1048
 
        :param parents: Revision ids of the parents of the new revision.
1049
 
        :param config: Configuration to use.
1050
 
        :param timestamp: Optional timestamp recorded for commit.
1051
 
        :param timezone: Optional timezone for timestamp.
1052
 
        :param committer: Optional committer to set for commit.
1053
 
        :param revprops: Optional dictionary of revision properties.
1054
 
        :param revision_id: Optional revision id.
1055
 
        """
1056
 
        return RootCommitBuilder(self, parents, config, timestamp, timezone,
1057
 
                                 committer, revprops, revision_id)
1058
 
 
1059
 
 
1060
972
class RepositoryFormat(object):
1061
973
    """A repository format.
1062
974
 
1087
999
    _formats = {}
1088
1000
    """The known formats."""
1089
1001
 
1090
 
    def __str__(self):
1091
 
        return "<%s>" % self.__class__.__name__
1092
 
 
1093
1002
    @classmethod
1094
1003
    def find_format(klass, a_bzrdir):
1095
1004
        """Return the format for the repository object in a_bzrdir."""
1156
1065
                                  control_files,
1157
1066
                                  prefixed=True,
1158
1067
                                  versionedfile_class=WeaveFile,
1159
 
                                  versionedfile_kwargs={},
1160
1068
                                  escaped=False):
1161
1069
        weave_transport = control_files._transport.clone(name)
1162
1070
        dir_mode = control_files._dir_mode
1165
1073
                                  dir_mode=dir_mode,
1166
1074
                                  file_mode=file_mode,
1167
1075
                                  versionedfile_class=versionedfile_class,
1168
 
                                  versionedfile_kwargs=versionedfile_kwargs,
1169
1076
                                  escaped=escaped)
1170
1077
 
1171
1078
    def initialize(self, a_bzrdir, shared=False):
1187
1094
        """
1188
1095
        return True
1189
1096
 
1190
 
    def check_conversion_target(self, target_format):
1191
 
        raise NotImplementedError(self.check_conversion_target)
1192
 
 
1193
1097
    def open(self, a_bzrdir, _found=False):
1194
1098
        """Return an instance of this format for the bzrdir a_bzrdir.
1195
1099
        
1214
1118
class PreSplitOutRepositoryFormat(RepositoryFormat):
1215
1119
    """Base class for the pre split out repository formats."""
1216
1120
 
1217
 
    rich_root_data = False
1218
 
 
1219
1121
    def initialize(self, a_bzrdir, shared=False, _internal=False):
1220
1122
        """Create a weave repository.
1221
1123
        
1285
1187
                                  control_store=control_store,
1286
1188
                                  text_store=text_store)
1287
1189
 
1288
 
    def check_conversion_target(self, target_format):
1289
 
        pass
1290
 
 
1291
1190
 
1292
1191
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1293
1192
    """Bzr repository format 4.
1404
1303
class MetaDirRepositoryFormat(RepositoryFormat):
1405
1304
    """Common base class for the new repositories using the metadir layout."""
1406
1305
 
1407
 
    rich_root_data = False
1408
 
 
1409
1306
    def __init__(self):
1410
1307
        super(MetaDirRepositoryFormat, self).__init__()
1411
1308
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1463
1360
        """See RepositoryFormat.get_format_description()."""
1464
1361
        return "Weave repository format 7"
1465
1362
 
1466
 
    def check_conversion_target(self, target_format):
1467
 
        pass
1468
 
 
1469
1363
    def _get_revision_store(self, repo_transport, control_files):
1470
1364
        """See RepositoryFormat._get_revision_store()."""
1471
1365
        return self._get_text_rev_store(repo_transport,
1530
1424
                                 text_store=text_store)
1531
1425
 
1532
1426
 
1533
 
class RepositoryFormatKnit(MetaDirRepositoryFormat):
1534
 
    """Bzr repository knit format (generalized). 
 
1427
class RepositoryFormatKnit1(MetaDirRepositoryFormat):
 
1428
    """Bzr repository knit format 1.
1535
1429
 
1536
1430
    This repository format has:
1537
1431
     - knits for file texts and inventory
1542
1436
     - an optional 'shared-storage' flag
1543
1437
     - an optional 'no-working-trees' flag
1544
1438
     - a LockDir lock
 
1439
 
 
1440
    This format was introduced in bzr 0.8.
1545
1441
    """
1546
1442
 
1547
1443
    def _get_control_store(self, repo_transport, control_files):
1554
1450
            versionedfile_kwargs={'factory':KnitPlainFactory()},
1555
1451
            )
1556
1452
 
 
1453
    def get_format_string(self):
 
1454
        """See RepositoryFormat.get_format_string()."""
 
1455
        return "Bazaar-NG Knit Repository Format 1"
 
1456
 
 
1457
    def get_format_description(self):
 
1458
        """See RepositoryFormat.get_format_description()."""
 
1459
        return "Knit repository format 1"
 
1460
 
1557
1461
    def _get_revision_store(self, repo_transport, control_files):
1558
1462
        """See RepositoryFormat._get_revision_store()."""
1559
1463
        from bzrlib.store.revision.knit import KnitRevisionStore
1563
1467
            prefixed=False,
1564
1468
            precious=True,
1565
1469
            versionedfile_class=KnitVersionedFile,
1566
 
            versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory(),},
 
1470
            versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory()},
1567
1471
            escaped=True,
1568
1472
            )
1569
1473
        return KnitRevisionStore(versioned_file_store)
1574
1478
                                              transport,
1575
1479
                                              control_files,
1576
1480
                                              versionedfile_class=KnitVersionedFile,
1577
 
                                              versionedfile_kwargs={
1578
 
                                                  'create_parent_dir':True,
1579
 
                                                  'delay_create':True,
1580
 
                                                  'dir_mode':control_files._dir_mode,
1581
 
                                              },
1582
1481
                                              escaped=True)
1583
1482
 
1584
1483
    def initialize(self, a_bzrdir, shared=False):
1632
1531
                              text_store=text_store)
1633
1532
 
1634
1533
 
1635
 
class RepositoryFormatKnit1(RepositoryFormatKnit):
1636
 
    """Bzr repository knit format 1.
1637
 
 
1638
 
    This repository format has:
1639
 
     - knits for file texts and inventory
1640
 
     - hash subdirectory based stores.
1641
 
     - knits for revisions and signatures
1642
 
     - TextStores for revisions and signatures.
1643
 
     - a format marker of its own
1644
 
     - an optional 'shared-storage' flag
1645
 
     - an optional 'no-working-trees' flag
1646
 
     - a LockDir lock
1647
 
 
1648
 
    This format was introduced in bzr 0.8.
1649
 
    """
1650
 
    def get_format_string(self):
1651
 
        """See RepositoryFormat.get_format_string()."""
1652
 
        return "Bazaar-NG Knit Repository Format 1"
1653
 
 
1654
 
    def get_format_description(self):
1655
 
        """See RepositoryFormat.get_format_description()."""
1656
 
        return "Knit repository format 1"
1657
 
 
1658
 
    def check_conversion_target(self, target_format):
1659
 
        pass
1660
 
 
1661
 
 
1662
 
class RepositoryFormatKnit2(RepositoryFormatKnit):
1663
 
    """Bzr repository knit format 2.
1664
 
 
1665
 
    THIS FORMAT IS EXPERIMENTAL
1666
 
    This repository format has:
1667
 
     - knits for file texts and inventory
1668
 
     - hash subdirectory based stores.
1669
 
     - knits for revisions and signatures
1670
 
     - TextStores for revisions and signatures.
1671
 
     - a format marker of its own
1672
 
     - an optional 'shared-storage' flag
1673
 
     - an optional 'no-working-trees' flag
1674
 
     - a LockDir lock
1675
 
     - Support for recording full info about the tree root
1676
 
 
1677
 
    """
1678
 
    
1679
 
    rich_root_data = True
1680
 
 
1681
 
    def get_format_string(self):
1682
 
        """See RepositoryFormat.get_format_string()."""
1683
 
        return "Bazaar Knit Repository Format 2\n"
1684
 
 
1685
 
    def get_format_description(self):
1686
 
        """See RepositoryFormat.get_format_description()."""
1687
 
        return "Knit repository format 2"
1688
 
 
1689
 
    def check_conversion_target(self, target_format):
1690
 
        if not target_format.rich_root_data:
1691
 
            raise errors.BadConversionTarget(
1692
 
                'Does not support rich root data.', target_format)
1693
 
 
1694
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1695
 
        """See RepositoryFormat.open().
1696
 
        
1697
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1698
 
                                    repository at a slightly different url
1699
 
                                    than normal. I.e. during 'upgrade'.
1700
 
        """
1701
 
        if not _found:
1702
 
            format = RepositoryFormat.find_format(a_bzrdir)
1703
 
            assert format.__class__ ==  self.__class__
1704
 
        if _override_transport is not None:
1705
 
            repo_transport = _override_transport
1706
 
        else:
1707
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1708
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
1709
 
        text_store = self._get_text_store(repo_transport, control_files)
1710
 
        control_store = self._get_control_store(repo_transport, control_files)
1711
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1712
 
        return KnitRepository2(_format=self,
1713
 
                               a_bzrdir=a_bzrdir,
1714
 
                               control_files=control_files,
1715
 
                               _revision_store=_revision_store,
1716
 
                               control_store=control_store,
1717
 
                               text_store=text_store)
1718
 
 
1719
 
 
1720
 
 
1721
1534
# formats which have no format string are not discoverable
1722
1535
# and not independently creatable, so are not registered.
1723
1536
RepositoryFormat.register_format(RepositoryFormat7())
1724
1537
_default_format = RepositoryFormatKnit1()
1725
1538
RepositoryFormat.register_format(_default_format)
1726
 
RepositoryFormat.register_format(RepositoryFormatKnit2())
1727
1539
RepositoryFormat.set_default_format(_default_format)
1728
1540
_legacy_formats = [RepositoryFormat4(),
1729
1541
                   RepositoryFormat5(),
1742
1554
    InterRepository.get(other).method_name(parameters).
1743
1555
    """
1744
1556
 
1745
 
    _optimisers = []
 
1557
    _optimisers = set()
1746
1558
    """The available optimised InterRepository types."""
1747
1559
 
 
1560
    @needs_write_lock
1748
1561
    def copy_content(self, revision_id=None, basis=None):
1749
 
        raise NotImplementedError(self.copy_content)
1750
 
 
 
1562
        """Make a complete copy of the content in self into destination.
 
1563
        
 
1564
        This is a destructive operation! Do not use it on existing 
 
1565
        repositories.
 
1566
 
 
1567
        :param revision_id: Only copy the content needed to construct
 
1568
                            revision_id and its parents.
 
1569
        :param basis: Copy the needed data preferentially from basis.
 
1570
        """
 
1571
        try:
 
1572
            self.target.set_make_working_trees(self.source.make_working_trees())
 
1573
        except NotImplementedError:
 
1574
            pass
 
1575
        # grab the basis available data
 
1576
        if basis is not None:
 
1577
            self.target.fetch(basis, revision_id=revision_id)
 
1578
        # but don't bother fetching if we have the needed data now.
 
1579
        if (revision_id not in (None, NULL_REVISION) and 
 
1580
            self.target.has_revision(revision_id)):
 
1581
            return
 
1582
        self.target.fetch(self.source, revision_id=revision_id)
 
1583
 
 
1584
    @needs_write_lock
1751
1585
    def fetch(self, revision_id=None, pb=None):
1752
1586
        """Fetch the content required to construct revision_id.
1753
1587
 
1754
 
        The content is copied from self.source to self.target.
 
1588
        The content is copied from source to target.
1755
1589
 
1756
1590
        :param revision_id: if None all content is copied, if NULL_REVISION no
1757
1591
                            content is copied.
1761
1595
        Returns the copied revision count and the failed revisions in a tuple:
1762
1596
        (copied, failures).
1763
1597
        """
1764
 
        raise NotImplementedError(self.fetch)
1765
 
   
 
1598
        from bzrlib.fetch import GenericRepoFetcher
 
1599
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
 
1600
               self.source, self.source._format, self.target, self.target._format)
 
1601
        f = GenericRepoFetcher(to_repository=self.target,
 
1602
                               from_repository=self.source,
 
1603
                               last_revision=revision_id,
 
1604
                               pb=pb)
 
1605
        return f.count_copied, f.failed_revisions
 
1606
 
1766
1607
    @needs_read_lock
1767
1608
    def missing_revision_ids(self, revision_id=None):
1768
1609
        """Return the revision ids that source has that target does not.
1776
1617
        target_ids = set(self.target.all_revision_ids())
1777
1618
        if revision_id is not None:
1778
1619
            source_ids = self.source.get_ancestry(revision_id)
1779
 
            assert source_ids[0] is None
 
1620
            assert source_ids[0] == None
1780
1621
            source_ids.pop(0)
1781
1622
        else:
1782
1623
            source_ids = self.source.all_revision_ids()
1787
1628
        return [rev_id for rev_id in source_ids if rev_id in result_set]
1788
1629
 
1789
1630
 
1790
 
class InterSameDataRepository(InterRepository):
1791
 
    """Code for converting between repositories that represent the same data.
1792
 
    
1793
 
    Data format and model must match for this to work.
1794
 
    """
1795
 
 
1796
 
    _matching_repo_format = RepositoryFormat4()
1797
 
    """Repository format for testing with."""
1798
 
 
1799
 
    @staticmethod
1800
 
    def is_compatible(source, target):
1801
 
        if not isinstance(source, Repository):
1802
 
            return False
1803
 
        if not isinstance(target, Repository):
1804
 
            return False
1805
 
        if source._format.rich_root_data == target._format.rich_root_data:
1806
 
            return True
1807
 
        else:
1808
 
            return False
1809
 
 
1810
 
    @needs_write_lock
1811
 
    def copy_content(self, revision_id=None, basis=None):
1812
 
        """Make a complete copy of the content in self into destination.
1813
 
        
1814
 
        This is a destructive operation! Do not use it on existing 
1815
 
        repositories.
1816
 
 
1817
 
        :param revision_id: Only copy the content needed to construct
1818
 
                            revision_id and its parents.
1819
 
        :param basis: Copy the needed data preferentially from basis.
1820
 
        """
1821
 
        try:
1822
 
            self.target.set_make_working_trees(self.source.make_working_trees())
1823
 
        except NotImplementedError:
1824
 
            pass
1825
 
        # grab the basis available data
1826
 
        if basis is not None:
1827
 
            self.target.fetch(basis, revision_id=revision_id)
1828
 
        # but don't bother fetching if we have the needed data now.
1829
 
        if (revision_id not in (None, NULL_REVISION) and 
1830
 
            self.target.has_revision(revision_id)):
1831
 
            return
1832
 
        self.target.fetch(self.source, revision_id=revision_id)
1833
 
 
1834
 
    @needs_write_lock
1835
 
    def fetch(self, revision_id=None, pb=None):
1836
 
        """See InterRepository.fetch()."""
1837
 
        from bzrlib.fetch import GenericRepoFetcher
1838
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1839
 
               self.source, self.source._format, self.target, 
1840
 
               self.target._format)
1841
 
        f = GenericRepoFetcher(to_repository=self.target,
1842
 
                               from_repository=self.source,
1843
 
                               last_revision=revision_id,
1844
 
                               pb=pb)
1845
 
        return f.count_copied, f.failed_revisions
1846
 
 
1847
 
 
1848
 
class InterWeaveRepo(InterSameDataRepository):
 
1631
class InterWeaveRepo(InterRepository):
1849
1632
    """Optimised code paths between Weave based repositories."""
1850
1633
 
1851
1634
    _matching_repo_format = RepositoryFormat7()
1937
1720
        # - RBC 20060209
1938
1721
        if revision_id is not None:
1939
1722
            source_ids = self.source.get_ancestry(revision_id)
1940
 
            assert source_ids[0] is None
 
1723
            assert source_ids[0] == None
1941
1724
            source_ids.pop(0)
1942
1725
        else:
1943
1726
            source_ids = self.source._all_possible_ids()
1963
1746
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1964
1747
 
1965
1748
 
1966
 
class InterKnitRepo(InterSameDataRepository):
 
1749
class InterKnitRepo(InterRepository):
1967
1750
    """Optimised code paths between Knit based repositories."""
1968
1751
 
1969
1752
    _matching_repo_format = RepositoryFormatKnit1()
2000
1783
        """See InterRepository.missing_revision_ids()."""
2001
1784
        if revision_id is not None:
2002
1785
            source_ids = self.source.get_ancestry(revision_id)
2003
 
            assert source_ids[0] is None
 
1786
            assert source_ids[0] == None
2004
1787
            source_ids.pop(0)
2005
1788
        else:
2006
1789
            source_ids = self.source._all_possible_ids()
2025
1808
            # that against the revision records.
2026
1809
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
2027
1810
 
2028
 
 
2029
 
class InterModel1and2(InterRepository):
2030
 
 
2031
 
    _matching_repo_format = None
2032
 
 
2033
 
    @staticmethod
2034
 
    def is_compatible(source, target):
2035
 
        if not isinstance(source, Repository):
2036
 
            return False
2037
 
        if not isinstance(target, Repository):
2038
 
            return False
2039
 
        if not source._format.rich_root_data and target._format.rich_root_data:
2040
 
            return True
2041
 
        else:
2042
 
            return False
2043
 
 
2044
 
    @needs_write_lock
2045
 
    def fetch(self, revision_id=None, pb=None):
2046
 
        """See InterRepository.fetch()."""
2047
 
        from bzrlib.fetch import Model1toKnit2Fetcher
2048
 
        f = Model1toKnit2Fetcher(to_repository=self.target,
2049
 
                                 from_repository=self.source,
2050
 
                                 last_revision=revision_id,
2051
 
                                 pb=pb)
2052
 
        return f.count_copied, f.failed_revisions
2053
 
 
2054
 
    @needs_write_lock
2055
 
    def copy_content(self, revision_id=None, basis=None):
2056
 
        """Make a complete copy of the content in self into destination.
2057
 
        
2058
 
        This is a destructive operation! Do not use it on existing 
2059
 
        repositories.
2060
 
 
2061
 
        :param revision_id: Only copy the content needed to construct
2062
 
                            revision_id and its parents.
2063
 
        :param basis: Copy the needed data preferentially from basis.
2064
 
        """
2065
 
        try:
2066
 
            self.target.set_make_working_trees(self.source.make_working_trees())
2067
 
        except NotImplementedError:
2068
 
            pass
2069
 
        # grab the basis available data
2070
 
        if basis is not None:
2071
 
            self.target.fetch(basis, revision_id=revision_id)
2072
 
        # but don't bother fetching if we have the needed data now.
2073
 
        if (revision_id not in (None, NULL_REVISION) and 
2074
 
            self.target.has_revision(revision_id)):
2075
 
            return
2076
 
        self.target.fetch(self.source, revision_id=revision_id)
2077
 
 
2078
 
 
2079
 
class InterKnit1and2(InterKnitRepo):
2080
 
 
2081
 
    _matching_repo_format = None
2082
 
 
2083
 
    @staticmethod
2084
 
    def is_compatible(source, target):
2085
 
        """Be compatible with Knit1 source and Knit2 target"""
2086
 
        try:
2087
 
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
2088
 
                    isinstance(target._format, (RepositoryFormatKnit2)))
2089
 
        except AttributeError:
2090
 
            return False
2091
 
 
2092
 
    @needs_write_lock
2093
 
    def fetch(self, revision_id=None, pb=None):
2094
 
        """See InterRepository.fetch()."""
2095
 
        from bzrlib.fetch import Knit1to2Fetcher
2096
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2097
 
               self.source, self.source._format, self.target, 
2098
 
               self.target._format)
2099
 
        f = Knit1to2Fetcher(to_repository=self.target,
2100
 
                            from_repository=self.source,
2101
 
                            last_revision=revision_id,
2102
 
                            pb=pb)
2103
 
        return f.count_copied, f.failed_revisions
2104
 
 
2105
 
 
2106
 
InterRepository.register_optimiser(InterSameDataRepository)
2107
1811
InterRepository.register_optimiser(InterWeaveRepo)
2108
1812
InterRepository.register_optimiser(InterKnitRepo)
2109
 
InterRepository.register_optimiser(InterModel1and2)
2110
 
InterRepository.register_optimiser(InterKnit1and2)
2111
1813
 
2112
1814
 
2113
1815
class RepositoryTestProviderAdapter(object):
2178
1880
        # default format.
2179
1881
        # XXX: robertc 20060220 reinstate this when there are two supported
2180
1882
        # formats which do not have an optimal code path between them.
2181
 
        #result.append((InterRepository,
2182
 
        #               RepositoryFormat6(),
2183
 
        #               RepositoryFormatKnit1()))
 
1883
        result.append((InterRepository,
 
1884
                       RepositoryFormat6(),
 
1885
                       RepositoryFormatKnit1()))
2184
1886
        for optimiser in InterRepository._optimisers:
2185
 
            if optimiser._matching_repo_format is not None:
2186
 
                result.append((optimiser,
2187
 
                               optimiser._matching_repo_format,
2188
 
                               optimiser._matching_repo_format
2189
 
                               ))
 
1887
            result.append((optimiser,
 
1888
                           optimiser._matching_repo_format,
 
1889
                           optimiser._matching_repo_format
 
1890
                           ))
2190
1891
        # if there are specific combinations we want to use, we can add them 
2191
1892
        # here.
2192
 
        result.append((InterModel1and2, RepositoryFormat5(),
2193
 
                       RepositoryFormatKnit2()))
2194
 
        result.append((InterKnit1and2, RepositoryFormatKnit1(),
2195
 
                       RepositoryFormatKnit2()))
2196
1893
        return result
2197
1894
 
2198
1895
 
2225
1922
        self.step('Moving repository to repository.backup')
2226
1923
        self.repo_dir.transport.move('repository', 'repository.backup')
2227
1924
        backup_transport =  self.repo_dir.transport.clone('repository.backup')
2228
 
        repo._format.check_conversion_target(self.target_format)
2229
1925
        self.source_repo = repo._format.open(self.repo_dir,
2230
1926
            _found=True,
2231
1927
            _override_transport=backup_transport)
2254
1950
    This allows describing a tree to be committed without needing to 
2255
1951
    know the internals of the format of the repository.
2256
1952
    """
2257
 
    
2258
 
    record_root_entry = False
2259
1953
    def __init__(self, repository, parents, config, timestamp=None, 
2260
1954
                 timezone=None, committer=None, revprops=None, 
2261
1955
                 revision_id=None):
2278
1972
            assert isinstance(committer, basestring), type(committer)
2279
1973
            self._committer = committer
2280
1974
 
2281
 
        self.new_inventory = Inventory(None)
 
1975
        self.new_inventory = Inventory()
2282
1976
        self._new_revision_id = revision_id
2283
1977
        self.parents = parents
2284
1978
        self.repository = repository
2316
2010
            self.new_inventory, self._config)
2317
2011
        return self._new_revision_id
2318
2012
 
2319
 
    def revision_tree(self):
2320
 
        """Return the tree that was just committed.
2321
 
 
2322
 
        After calling commit() this can be called to get a RevisionTree
2323
 
        representing the newly committed tree. This is preferred to
2324
 
        calling Repository.revision_tree() because that may require
2325
 
        deserializing the inventory, while we already have a copy in
2326
 
        memory.
2327
 
        """
2328
 
        return RevisionTree(self.repository, self.new_inventory,
2329
 
                            self._new_revision_id)
2330
 
 
2331
2013
    def finish_inventory(self):
2332
2014
        """Tell the builder that the inventory is finished."""
2333
 
        if self.new_inventory.root is None:
2334
 
            symbol_versioning.warn('Root entry should be supplied to'
2335
 
                ' record_entry_contents, as of bzr 0.10.',
2336
 
                 DeprecationWarning, stacklevel=2)
2337
 
            self.new_inventory.add(InventoryDirectory(ROOT_ID, '', None))
2338
2015
        self.new_inventory.revision_id = self._new_revision_id
2339
2016
        self.inv_sha1 = self.repository.add_inventory(
2340
2017
            self._new_revision_id,
2364
2041
    def record_entry_contents(self, ie, parent_invs, path, tree):
2365
2042
        """Record the content of ie from tree into the commit if needed.
2366
2043
 
2367
 
        Side effect: sets ie.revision when unchanged
2368
 
 
2369
2044
        :param ie: An inventory entry present in the commit.
2370
2045
        :param parent_invs: The inventories of the parent revisions of the
2371
2046
            commit.
2373
2048
        :param tree: The tree which contains this entry and should be used to 
2374
2049
        obtain content.
2375
2050
        """
2376
 
        if self.new_inventory.root is None and ie.parent_id is not None:
2377
 
            symbol_versioning.warn('Root entry should be supplied to'
2378
 
                ' record_entry_contents, as of bzr 0.10.',
2379
 
                 DeprecationWarning, stacklevel=2)
2380
 
            self.record_entry_contents(tree.inventory.root.copy(), parent_invs,
2381
 
                                       '', tree)
2382
2051
        self.new_inventory.add(ie)
2383
2052
 
2384
2053
        # ie.revision is always None if the InventoryEntry is considered
2386
2055
        # which may be the sole parent if it is untouched.
2387
2056
        if ie.revision is not None:
2388
2057
            return
2389
 
 
2390
 
        # In this revision format, root entries have no knit or weave
2391
 
        if ie is self.new_inventory.root:
2392
 
            # When serializing out to disk and back in
2393
 
            # root.revision is always _new_revision_id
2394
 
            ie.revision = self._new_revision_id
2395
 
            return
2396
2058
        previous_entries = ie.find_previous_heads(
2397
2059
            parent_invs,
2398
2060
            self.repository.weave_store,
2458
2120
        versionedfile.clear_cache()
2459
2121
 
2460
2122
 
2461
 
class _CommitBuilder(CommitBuilder):
2462
 
    """Temporary class so old CommitBuilders are detected properly
2463
 
    
2464
 
    Note: CommitBuilder works whether or not root entry is recorded.
2465
 
    """
2466
 
 
2467
 
    record_root_entry = True
2468
 
 
2469
 
 
2470
 
class RootCommitBuilder(CommitBuilder):
2471
 
    """This commitbuilder actually records the root id"""
2472
 
    
2473
 
    record_root_entry = True
2474
 
 
2475
 
    def record_entry_contents(self, ie, parent_invs, path, tree):
2476
 
        """Record the content of ie from tree into the commit if needed.
2477
 
 
2478
 
        Side effect: sets ie.revision when unchanged
2479
 
 
2480
 
        :param ie: An inventory entry present in the commit.
2481
 
        :param parent_invs: The inventories of the parent revisions of the
2482
 
            commit.
2483
 
        :param path: The path the entry is at in the tree.
2484
 
        :param tree: The tree which contains this entry and should be used to 
2485
 
        obtain content.
2486
 
        """
2487
 
        assert self.new_inventory.root is not None or ie.parent_id is None
2488
 
        self.new_inventory.add(ie)
2489
 
 
2490
 
        # ie.revision is always None if the InventoryEntry is considered
2491
 
        # for committing. ie.snapshot will record the correct revision 
2492
 
        # which may be the sole parent if it is untouched.
2493
 
        if ie.revision is not None:
2494
 
            return
2495
 
 
2496
 
        previous_entries = ie.find_previous_heads(
2497
 
            parent_invs,
2498
 
            self.repository.weave_store,
2499
 
            self.repository.get_transaction())
2500
 
        # we are creating a new revision for ie in the history store
2501
 
        # and inventory.
2502
 
        ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
2503
 
 
2504
 
 
2505
2123
_unescape_map = {
2506
2124
    'apos':"'",
2507
2125
    'quot':'"',