~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

Merge bzr.dev

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 bzrdir, check, delta, gpg, errors, xml5, ui, transactions, osutils
 
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
    )
25
36
from bzrlib.decorators import needs_read_lock, needs_write_lock
26
37
from bzrlib.errors import InvalidRevisionId
27
38
from bzrlib.graph import Graph
75
86
            "Mismatch between inventory revision" \
76
87
            " id and insertion revid (%r, %r)" % (inv.revision_id, revid)
77
88
        assert inv.root is not None
78
 
        inv_text = xml5.serializer_v5.write_inventory_to_string(inv)
 
89
        inv_text = self.serialise_inventory(inv)
79
90
        inv_sha1 = osutils.sha_string(inv_text)
80
91
        inv_vf = self.control_weaves.get_weave('inventory',
81
92
                                               self.get_transaction())
195
206
        # TODO: make sure to construct the right store classes, etc, depending
196
207
        # on whether escaping is required.
197
208
        self._warn_if_deprecated()
 
209
        self._serializer = xml5.serializer_v5
198
210
 
199
211
    def __repr__(self):
200
212
        return '%s(%r)' % (self.__class__.__name__, 
399
411
        revision_ids. Each altered file-ids has the exact revision_ids that
400
412
        altered it listed explicitly.
401
413
        """
402
 
        assert isinstance(self._format, (RepositoryFormat5,
403
 
                                         RepositoryFormat6,
404
 
                                         RepositoryFormat7,
405
 
                                         RepositoryFormatKnit1)), \
 
414
        assert self._serializer.support_altered_by_hack, \
406
415
            ("fileids_altered_by_revision_ids only supported for branches " 
407
416
             "which store inventory as unnested xml, not on %r" % self)
408
417
        selected_revision_ids = set(revision_ids)
450
459
        :param revision_id: The expected revision id of the inventory.
451
460
        :param xml: A serialised inventory.
452
461
        """
453
 
        result = xml5.serializer_v5.read_inventory_from_string(xml)
 
462
        result = self._serializer.read_inventory_from_string(xml)
454
463
        result.root.revision = revision_id
455
464
        return result
456
465
 
 
466
    def serialise_inventory(self, inv):
 
467
        return self._serializer.write_inventory_to_string(inv)
 
468
 
457
469
    @needs_read_lock
458
470
    def get_inventory_xml(self, revision_id):
459
471
        """Get inventory XML as a file object."""
989
1001
        return self._get_revision_vf().get_parents(revision_id)
990
1002
 
991
1003
 
 
1004
class KnitRepository2(KnitRepository):
 
1005
    """"""
 
1006
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
 
1007
                 control_store, text_store):
 
1008
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
 
1009
                              _revision_store, control_store, text_store)
 
1010
        self._serializer = xml6.serializer_v6
 
1011
 
 
1012
    def deserialise_inventory(self, revision_id, xml):
 
1013
        """Transform the xml into an inventory object. 
 
1014
 
 
1015
        :param revision_id: The expected revision id of the inventory.
 
1016
        :param xml: A serialised inventory.
 
1017
        """
 
1018
        result = self._serializer.read_inventory_from_string(xml)
 
1019
        assert result.root.revision is not None
 
1020
        return result
 
1021
 
 
1022
    def serialise_inventory(self, inv):
 
1023
        """Transform the inventory object into XML text.
 
1024
 
 
1025
        :param revision_id: The expected revision id of the inventory.
 
1026
        :param xml: A serialised inventory.
 
1027
        """
 
1028
        assert inv.revision_id is not None
 
1029
        assert inv.root.revision is not None
 
1030
        return KnitRepository.serialise_inventory(self, inv)
 
1031
 
 
1032
    def get_commit_builder(self, branch, parents, config, timestamp=None, 
 
1033
                           timezone=None, committer=None, revprops=None, 
 
1034
                           revision_id=None):
 
1035
        """Obtain a CommitBuilder for this repository.
 
1036
        
 
1037
        :param branch: Branch to commit to.
 
1038
        :param parents: Revision ids of the parents of the new revision.
 
1039
        :param config: Configuration to use.
 
1040
        :param timestamp: Optional timestamp recorded for commit.
 
1041
        :param timezone: Optional timezone for timestamp.
 
1042
        :param committer: Optional committer to set for commit.
 
1043
        :param revprops: Optional dictionary of revision properties.
 
1044
        :param revision_id: Optional revision id.
 
1045
        """
 
1046
        return RootCommitBuilder(self, parents, config, timestamp, timezone,
 
1047
                                 committer, revprops, revision_id)
 
1048
 
 
1049
 
992
1050
class RepositoryFormat(object):
993
1051
    """A repository format.
994
1052
 
1119
1177
        """
1120
1178
        return True
1121
1179
 
 
1180
    def check_conversion_target(self, target_format):
 
1181
        raise NotImplementedError(self.check_conversion_target)
 
1182
 
1122
1183
    def open(self, a_bzrdir, _found=False):
1123
1184
        """Return an instance of this format for the bzrdir a_bzrdir.
1124
1185
        
1143
1204
class PreSplitOutRepositoryFormat(RepositoryFormat):
1144
1205
    """Base class for the pre split out repository formats."""
1145
1206
 
 
1207
    rich_root_data = False
 
1208
 
1146
1209
    def initialize(self, a_bzrdir, shared=False, _internal=False):
1147
1210
        """Create a weave repository.
1148
1211
        
1212
1275
                                  control_store=control_store,
1213
1276
                                  text_store=text_store)
1214
1277
 
 
1278
    def check_conversion_target(self, target_format):
 
1279
        pass
 
1280
 
1215
1281
 
1216
1282
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1217
1283
    """Bzr repository format 4.
1328
1394
class MetaDirRepositoryFormat(RepositoryFormat):
1329
1395
    """Common base class for the new repositories using the metadir layout."""
1330
1396
 
 
1397
    rich_root_data = False
 
1398
 
1331
1399
    def __init__(self):
1332
1400
        super(MetaDirRepositoryFormat, self).__init__()
1333
1401
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1385
1453
        """See RepositoryFormat.get_format_description()."""
1386
1454
        return "Weave repository format 7"
1387
1455
 
 
1456
    def check_conversion_target(self, target_format):
 
1457
        pass
 
1458
 
1388
1459
    def _get_revision_store(self, repo_transport, control_files):
1389
1460
        """See RepositoryFormat._get_revision_store()."""
1390
1461
        return self._get_text_rev_store(repo_transport,
1449
1520
                                 text_store=text_store)
1450
1521
 
1451
1522
 
1452
 
class RepositoryFormatKnit1(MetaDirRepositoryFormat):
1453
 
    """Bzr repository knit format 1.
 
1523
class RepositoryFormatKnit(MetaDirRepositoryFormat):
 
1524
    """Bzr repository knit format (generalized). 
1454
1525
 
1455
1526
    This repository format has:
1456
1527
     - knits for file texts and inventory
1461
1532
     - an optional 'shared-storage' flag
1462
1533
     - an optional 'no-working-trees' flag
1463
1534
     - a LockDir lock
1464
 
 
1465
 
    This format was introduced in bzr 0.8.
1466
1535
    """
1467
1536
 
1468
1537
    def _get_control_store(self, repo_transport, control_files):
1475
1544
            versionedfile_kwargs={'factory':KnitPlainFactory()},
1476
1545
            )
1477
1546
 
1478
 
    def get_format_string(self):
1479
 
        """See RepositoryFormat.get_format_string()."""
1480
 
        return "Bazaar-NG Knit Repository Format 1"
1481
 
 
1482
 
    def get_format_description(self):
1483
 
        """See RepositoryFormat.get_format_description()."""
1484
 
        return "Knit repository format 1"
1485
 
 
1486
1547
    def _get_revision_store(self, repo_transport, control_files):
1487
1548
        """See RepositoryFormat._get_revision_store()."""
1488
1549
        from bzrlib.store.revision.knit import KnitRevisionStore
1561
1622
                              text_store=text_store)
1562
1623
 
1563
1624
 
 
1625
class RepositoryFormatKnit1(RepositoryFormatKnit):
 
1626
    """Bzr repository knit format 1.
 
1627
 
 
1628
    This repository format has:
 
1629
     - knits for file texts and inventory
 
1630
     - hash subdirectory based stores.
 
1631
     - knits for revisions and signatures
 
1632
     - TextStores for revisions and signatures.
 
1633
     - a format marker of its own
 
1634
     - an optional 'shared-storage' flag
 
1635
     - an optional 'no-working-trees' flag
 
1636
     - a LockDir lock
 
1637
 
 
1638
    This format was introduced in bzr 0.8.
 
1639
    """
 
1640
    def get_format_string(self):
 
1641
        """See RepositoryFormat.get_format_string()."""
 
1642
        return "Bazaar-NG Knit Repository Format 1"
 
1643
 
 
1644
    def get_format_description(self):
 
1645
        """See RepositoryFormat.get_format_description()."""
 
1646
        return "Knit repository format 1"
 
1647
 
 
1648
    def check_conversion_target(self, target_format):
 
1649
        pass
 
1650
 
 
1651
 
 
1652
class RepositoryFormatKnit2(RepositoryFormatKnit):
 
1653
    """Bzr repository knit format 2.
 
1654
 
 
1655
    THIS FORMAT IS EXPERIMENTAL
 
1656
    This repository format has:
 
1657
     - knits for file texts and inventory
 
1658
     - hash subdirectory based stores.
 
1659
     - knits for revisions and signatures
 
1660
     - TextStores for revisions and signatures.
 
1661
     - a format marker of its own
 
1662
     - an optional 'shared-storage' flag
 
1663
     - an optional 'no-working-trees' flag
 
1664
     - a LockDir lock
 
1665
     - Support for recording full info about the tree root
 
1666
 
 
1667
    """
 
1668
    
 
1669
    rich_root_data = True
 
1670
 
 
1671
    def get_format_string(self):
 
1672
        """See RepositoryFormat.get_format_string()."""
 
1673
        return "Bazaar Knit Repository Format 2\n"
 
1674
 
 
1675
    def get_format_description(self):
 
1676
        """See RepositoryFormat.get_format_description()."""
 
1677
        return "Knit repository format 2"
 
1678
 
 
1679
    def check_conversion_target(self, target_format):
 
1680
        if not target_format.rich_root_data:
 
1681
            raise errors.BadConversionTarget(
 
1682
                'Does not support rich root data.', target_format)
 
1683
 
 
1684
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
1685
        """See RepositoryFormat.open().
 
1686
        
 
1687
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
1688
                                    repository at a slightly different url
 
1689
                                    than normal. I.e. during 'upgrade'.
 
1690
        """
 
1691
        if not _found:
 
1692
            format = RepositoryFormat.find_format(a_bzrdir)
 
1693
            assert format.__class__ ==  self.__class__
 
1694
        if _override_transport is not None:
 
1695
            repo_transport = _override_transport
 
1696
        else:
 
1697
            repo_transport = a_bzrdir.get_repository_transport(None)
 
1698
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1699
        text_store = self._get_text_store(repo_transport, control_files)
 
1700
        control_store = self._get_control_store(repo_transport, control_files)
 
1701
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
1702
        return KnitRepository2(_format=self,
 
1703
                               a_bzrdir=a_bzrdir,
 
1704
                               control_files=control_files,
 
1705
                               _revision_store=_revision_store,
 
1706
                               control_store=control_store,
 
1707
                               text_store=text_store)
 
1708
 
 
1709
 
 
1710
 
1564
1711
# formats which have no format string are not discoverable
1565
1712
# and not independently creatable, so are not registered.
1566
1713
RepositoryFormat.register_format(RepositoryFormat7())
1567
1714
_default_format = RepositoryFormatKnit1()
1568
1715
RepositoryFormat.register_format(_default_format)
 
1716
RepositoryFormat.register_format(RepositoryFormatKnit2())
1569
1717
RepositoryFormat.set_default_format(_default_format)
1570
1718
_legacy_formats = [RepositoryFormat4(),
1571
1719
                   RepositoryFormat5(),
1584
1732
    InterRepository.get(other).method_name(parameters).
1585
1733
    """
1586
1734
 
1587
 
    _optimisers = set()
 
1735
    _optimisers = []
1588
1736
    """The available optimised InterRepository types."""
1589
1737
 
1590
 
    @needs_write_lock
1591
1738
    def copy_content(self, revision_id=None, basis=None):
1592
 
        """Make a complete copy of the content in self into destination.
1593
 
        
1594
 
        This is a destructive operation! Do not use it on existing 
1595
 
        repositories.
1596
 
 
1597
 
        :param revision_id: Only copy the content needed to construct
1598
 
                            revision_id and its parents.
1599
 
        :param basis: Copy the needed data preferentially from basis.
1600
 
        """
1601
 
        try:
1602
 
            self.target.set_make_working_trees(self.source.make_working_trees())
1603
 
        except NotImplementedError:
1604
 
            pass
1605
 
        # grab the basis available data
1606
 
        if basis is not None:
1607
 
            self.target.fetch(basis, revision_id=revision_id)
1608
 
        # but don't bother fetching if we have the needed data now.
1609
 
        if (revision_id not in (None, NULL_REVISION) and 
1610
 
            self.target.has_revision(revision_id)):
1611
 
            return
1612
 
        self.target.fetch(self.source, revision_id=revision_id)
1613
 
 
1614
 
    @needs_write_lock
 
1739
        raise NotImplementedError(self.copy_content)
 
1740
 
1615
1741
    def fetch(self, revision_id=None, pb=None):
1616
 
        """Fetch the content required to construct revision_id.
1617
 
 
1618
 
        The content is copied from source to target.
1619
 
 
1620
 
        :param revision_id: if None all content is copied, if NULL_REVISION no
1621
 
                            content is copied.
1622
 
        :param pb: optional progress bar to use for progress reports. If not
1623
 
                   provided a default one will be created.
1624
 
 
1625
 
        Returns the copied revision count and the failed revisions in a tuple:
1626
 
        (copied, failures).
1627
 
        """
1628
 
        from bzrlib.fetch import GenericRepoFetcher
1629
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1630
 
               self.source, self.source._format, self.target, self.target._format)
1631
 
        f = GenericRepoFetcher(to_repository=self.target,
1632
 
                               from_repository=self.source,
1633
 
                               last_revision=revision_id,
1634
 
                               pb=pb)
1635
 
        return f.count_copied, f.failed_revisions
1636
 
 
 
1742
        raise NotImplementedError(self.fetch)
 
1743
   
1637
1744
    @needs_read_lock
1638
1745
    def missing_revision_ids(self, revision_id=None):
1639
1746
        """Return the revision ids that source has that target does not.
1658
1765
        return [rev_id for rev_id in source_ids if rev_id in result_set]
1659
1766
 
1660
1767
 
1661
 
class InterWeaveRepo(InterRepository):
 
1768
class InterSameDataRepository(InterRepository):
 
1769
    """Code for converting between repositories that represent the same data.
 
1770
    
 
1771
    Data format and model must match for this to work.
 
1772
    """
 
1773
 
 
1774
    _matching_repo_format = RepositoryFormat4()
 
1775
    """Repository format for testing with."""
 
1776
 
 
1777
    @staticmethod
 
1778
    def is_compatible(source, target):
 
1779
        if not isinstance(source, Repository):
 
1780
            return False
 
1781
        if not isinstance(target, Repository):
 
1782
            return False
 
1783
        if source._format.rich_root_data == target._format.rich_root_data:
 
1784
            return True
 
1785
        else:
 
1786
            return False
 
1787
 
 
1788
    @needs_write_lock
 
1789
    def copy_content(self, revision_id=None, basis=None):
 
1790
        """Make a complete copy of the content in self into destination.
 
1791
        
 
1792
        This is a destructive operation! Do not use it on existing 
 
1793
        repositories.
 
1794
 
 
1795
        :param revision_id: Only copy the content needed to construct
 
1796
                            revision_id and its parents.
 
1797
        :param basis: Copy the needed data preferentially from basis.
 
1798
        """
 
1799
        try:
 
1800
            self.target.set_make_working_trees(self.source.make_working_trees())
 
1801
        except NotImplementedError:
 
1802
            pass
 
1803
        # grab the basis available data
 
1804
        if basis is not None:
 
1805
            self.target.fetch(basis, revision_id=revision_id)
 
1806
        # but don't bother fetching if we have the needed data now.
 
1807
        if (revision_id not in (None, NULL_REVISION) and 
 
1808
            self.target.has_revision(revision_id)):
 
1809
            return
 
1810
        self.target.fetch(self.source, revision_id=revision_id)
 
1811
 
 
1812
    @needs_write_lock
 
1813
    def fetch(self, revision_id=None, pb=None):
 
1814
        """Fetch the content required to construct revision_id.
 
1815
 
 
1816
        The content is copied from source to target.
 
1817
 
 
1818
        :param revision_id: if None all content is copied, if NULL_REVISION no
 
1819
                            content is copied.
 
1820
        :param pb: optional progress bar to use for progress reports. If not
 
1821
                   provided a default one will be created.
 
1822
 
 
1823
        Returns the copied revision count and the failed revisions in a tuple:
 
1824
        (copied, failures).
 
1825
        """
 
1826
        from bzrlib.fetch import GenericRepoFetcher
 
1827
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
 
1828
               self.source, self.source._format, self.target, 
 
1829
               self.target._format)
 
1830
        f = GenericRepoFetcher(to_repository=self.target,
 
1831
                               from_repository=self.source,
 
1832
                               last_revision=revision_id,
 
1833
                               pb=pb)
 
1834
        return f.count_copied, f.failed_revisions
 
1835
 
 
1836
 
 
1837
class InterWeaveRepo(InterSameDataRepository):
1662
1838
    """Optimised code paths between Weave based repositories."""
1663
1839
 
1664
1840
    _matching_repo_format = RepositoryFormat7()
1776
1952
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1777
1953
 
1778
1954
 
1779
 
class InterKnitRepo(InterRepository):
 
1955
class InterKnitRepo(InterSameDataRepository):
1780
1956
    """Optimised code paths between Knit based repositories."""
1781
1957
 
1782
1958
    _matching_repo_format = RepositoryFormatKnit1()
1838
2014
            # that against the revision records.
1839
2015
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1840
2016
 
 
2017
 
 
2018
class InterModel1and2(InterRepository):
 
2019
 
 
2020
    _matching_repo_format = None
 
2021
 
 
2022
    @staticmethod
 
2023
    def is_compatible(source, target):
 
2024
        if not isinstance(source, Repository):
 
2025
            return False
 
2026
        if not isinstance(target, Repository):
 
2027
            return False
 
2028
        if not source._format.rich_root_data and target._format.rich_root_data:
 
2029
            return True
 
2030
        else:
 
2031
            return False
 
2032
 
 
2033
    @needs_write_lock
 
2034
    def fetch(self, revision_id=None, pb=None):
 
2035
        """See InterRepository.fetch()."""
 
2036
        from bzrlib.fetch import Model1toKnit2Fetcher
 
2037
        f = Model1toKnit2Fetcher(to_repository=self.target,
 
2038
                                 from_repository=self.source,
 
2039
                                 last_revision=revision_id,
 
2040
                                 pb=pb)
 
2041
        return f.count_copied, f.failed_revisions
 
2042
 
 
2043
    @needs_write_lock
 
2044
    def copy_content(self, revision_id=None, basis=None):
 
2045
        """Make a complete copy of the content in self into destination.
 
2046
        
 
2047
        This is a destructive operation! Do not use it on existing 
 
2048
        repositories.
 
2049
 
 
2050
        :param revision_id: Only copy the content needed to construct
 
2051
                            revision_id and its parents.
 
2052
        :param basis: Copy the needed data preferentially from basis.
 
2053
        """
 
2054
        try:
 
2055
            self.target.set_make_working_trees(self.source.make_working_trees())
 
2056
        except NotImplementedError:
 
2057
            pass
 
2058
        # grab the basis available data
 
2059
        if basis is not None:
 
2060
            self.target.fetch(basis, revision_id=revision_id)
 
2061
        # but don't bother fetching if we have the needed data now.
 
2062
        if (revision_id not in (None, NULL_REVISION) and 
 
2063
            self.target.has_revision(revision_id)):
 
2064
            return
 
2065
        self.target.fetch(self.source, revision_id=revision_id)
 
2066
 
 
2067
 
 
2068
class InterKnit1and2(InterKnitRepo):
 
2069
 
 
2070
    _matching_repo_format = None
 
2071
 
 
2072
    @staticmethod
 
2073
    def is_compatible(source, target):
 
2074
        """Be compatible with Knit1 source and Knit2 target"""
 
2075
        try:
 
2076
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
 
2077
                    isinstance(target._format, (RepositoryFormatKnit2)))
 
2078
        except AttributeError:
 
2079
            return False
 
2080
 
 
2081
    @needs_write_lock
 
2082
    def fetch(self, revision_id=None, pb=None):
 
2083
        """See InterRepository.fetch()."""
 
2084
        from bzrlib.fetch import Knit1to2Fetcher
 
2085
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
 
2086
               self.source, self.source._format, self.target, 
 
2087
               self.target._format)
 
2088
        f = Knit1to2Fetcher(to_repository=self.target,
 
2089
                            from_repository=self.source,
 
2090
                            last_revision=revision_id,
 
2091
                            pb=pb)
 
2092
        return f.count_copied, f.failed_revisions
 
2093
 
 
2094
 
 
2095
InterRepository.register_optimiser(InterSameDataRepository)
1841
2096
InterRepository.register_optimiser(InterWeaveRepo)
1842
2097
InterRepository.register_optimiser(InterKnitRepo)
 
2098
InterRepository.register_optimiser(InterModel1and2)
 
2099
InterRepository.register_optimiser(InterKnit1and2)
1843
2100
 
1844
2101
 
1845
2102
class RepositoryTestProviderAdapter(object):
1910
2167
        # default format.
1911
2168
        # XXX: robertc 20060220 reinstate this when there are two supported
1912
2169
        # formats which do not have an optimal code path between them.
1913
 
        result.append((InterRepository,
1914
 
                       RepositoryFormat6(),
1915
 
                       RepositoryFormatKnit1()))
 
2170
        #result.append((InterRepository,
 
2171
        #               RepositoryFormat6(),
 
2172
        #               RepositoryFormatKnit1()))
1916
2173
        for optimiser in InterRepository._optimisers:
1917
 
            result.append((optimiser,
1918
 
                           optimiser._matching_repo_format,
1919
 
                           optimiser._matching_repo_format
1920
 
                           ))
 
2174
            if optimiser._matching_repo_format is not None:
 
2175
                result.append((optimiser,
 
2176
                               optimiser._matching_repo_format,
 
2177
                               optimiser._matching_repo_format
 
2178
                               ))
1921
2179
        # if there are specific combinations we want to use, we can add them 
1922
2180
        # here.
 
2181
        result.append((InterModel1and2, RepositoryFormat5(),
 
2182
                       RepositoryFormatKnit2()))
 
2183
        result.append((InterKnit1and2, RepositoryFormatKnit1(),
 
2184
                       RepositoryFormatKnit2()))
1923
2185
        return result
1924
2186
 
1925
2187
 
1952
2214
        self.step('Moving repository to repository.backup')
1953
2215
        self.repo_dir.transport.move('repository', 'repository.backup')
1954
2216
        backup_transport =  self.repo_dir.transport.clone('repository.backup')
 
2217
        repo._format.check_conversion_target(self.target_format)
1955
2218
        self.source_repo = repo._format.open(self.repo_dir,
1956
2219
            _found=True,
1957
2220
            _override_transport=backup_transport)
2182
2445
    record_root_entry = True
2183
2446
 
2184
2447
 
 
2448
class RootCommitBuilder(CommitBuilder):
 
2449
    """This commitbuilder actually records the root id"""
 
2450
    
 
2451
    record_root_entry = True
 
2452
 
 
2453
    def record_entry_contents(self, ie, parent_invs, path, tree):
 
2454
        """Record the content of ie from tree into the commit if needed.
 
2455
 
 
2456
        Side effect: sets ie.revision when unchanged
 
2457
 
 
2458
        :param ie: An inventory entry present in the commit.
 
2459
        :param parent_invs: The inventories of the parent revisions of the
 
2460
            commit.
 
2461
        :param path: The path the entry is at in the tree.
 
2462
        :param tree: The tree which contains this entry and should be used to 
 
2463
        obtain content.
 
2464
        """
 
2465
        assert self.new_inventory.root is not None or ie.parent_id is None
 
2466
        self.new_inventory.add(ie)
 
2467
 
 
2468
        # ie.revision is always None if the InventoryEntry is considered
 
2469
        # for committing. ie.snapshot will record the correct revision 
 
2470
        # which may be the sole parent if it is untouched.
 
2471
        if ie.revision is not None:
 
2472
            return
 
2473
 
 
2474
        previous_entries = ie.find_previous_heads(
 
2475
            parent_invs,
 
2476
            self.repository.weave_store,
 
2477
            self.repository.get_transaction())
 
2478
        # we are creating a new revision for ie in the history store
 
2479
        # and inventory.
 
2480
        ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
 
2481
 
 
2482
 
2185
2483
_unescape_map = {
2186
2484
    'apos':"'",
2187
2485
    'quot':'"',