~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: John Arbash Meinel
  • Date: 2006-08-14 16:16:53 UTC
  • mto: (1946.2.6 reduce-knit-churn)
  • mto: This revision was merged to the branch mainline in revision 1919.
  • Revision ID: john@arbash-meinel.com-20060814161653-54cdcdadcd4e9003
Remove bogus entry from BRANCH.TODO

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
from cStringIO import StringIO
18
 
 
19
 
from bzrlib.lazy_import import lazy_import
20
 
lazy_import(globals(), """
21
17
from binascii import hexlify
22
18
from copy import deepcopy
 
19
from cStringIO import StringIO
23
20
import re
24
21
import time
25
 
import unittest
 
22
from unittest import TestSuite
26
23
 
27
 
from bzrlib import (
28
 
    bzrdir,
29
 
    check,
30
 
    delta,
31
 
    errors,
32
 
    gpg,
33
 
    graph,
34
 
    knit,
35
 
    lockable_files,
36
 
    lockdir,
37
 
    osutils,
38
 
    revision as _mod_revision,
39
 
    symbol_versioning,
40
 
    transactions,
41
 
    ui,
42
 
    weave,
43
 
    weavefile,
44
 
    xml5,
45
 
    xml6,
46
 
    )
47
 
from bzrlib.osutils import (
48
 
    rand_bytes,
49
 
    compact_date, 
50
 
    local_time_offset,
51
 
    )
 
24
from bzrlib import bzrdir, check, delta, gpg, errors, xml5, ui, transactions, osutils
 
25
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
26
from bzrlib.errors import InvalidRevisionId
 
27
from bzrlib.graph import Graph
 
28
from bzrlib.inter import InterObject
 
29
from bzrlib.inventory import Inventory
 
30
from bzrlib.knit import KnitVersionedFile, KnitPlainFactory
 
31
from bzrlib.lockable_files import LockableFiles, TransportLock
 
32
from bzrlib.lockdir import LockDir
 
33
from bzrlib.osutils import (safe_unicode, rand_bytes, compact_date, 
 
34
                            local_time_offset)
 
35
from bzrlib.revision import NULL_REVISION, Revision
52
36
from bzrlib.revisiontree import RevisionTree
53
 
from bzrlib.store.versioned import VersionedFileStore
 
37
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
54
38
from bzrlib.store.text import TextStore
 
39
from bzrlib.symbol_versioning import (deprecated_method,
 
40
        zero_nine, 
 
41
        )
55
42
from bzrlib.testament import Testament
56
 
""")
57
 
 
58
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
59
 
from bzrlib.inter import InterObject
60
 
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
61
 
from bzrlib.symbol_versioning import (
62
 
        deprecated_method,
63
 
        zero_nine,
64
 
        )
65
43
from bzrlib.trace import mutter, note, warning
 
44
from bzrlib.tsort import topo_sort
 
45
from bzrlib.weave import WeaveFile
66
46
 
67
47
 
68
48
# Old formats display a warning, but only once
93
73
        assert inv.revision_id is None or inv.revision_id == revid, \
94
74
            "Mismatch between inventory revision" \
95
75
            " id and insertion revid (%r, %r)" % (inv.revision_id, revid)
96
 
        assert inv.root is not None
97
 
        inv_text = self.serialise_inventory(inv)
 
76
        inv_text = xml5.serializer_v5.write_inventory_to_string(inv)
98
77
        inv_sha1 = osutils.sha_string(inv_text)
99
78
        inv_vf = self.control_weaves.get_weave('inventory',
100
79
                                               self.get_transaction())
214
193
        # TODO: make sure to construct the right store classes, etc, depending
215
194
        # on whether escaping is required.
216
195
        self._warn_if_deprecated()
217
 
        self._serializer = xml5.serializer_v5
218
196
 
219
197
    def __repr__(self):
220
198
        return '%s(%r)' % (self.__class__.__name__, 
282
260
        :param revprops: Optional dictionary of revision properties.
283
261
        :param revision_id: Optional revision id.
284
262
        """
285
 
        return _CommitBuilder(self, parents, config, timestamp, timezone,
286
 
                              committer, revprops, revision_id)
 
263
        return CommitBuilder(self, parents, config, timestamp, timezone,
 
264
                             committer, revprops, revision_id)
287
265
 
288
266
    def unlock(self):
289
267
        self.control_files.unlock()
325
303
        or testing the revision graph.
326
304
        """
327
305
        if not revision_id or not isinstance(revision_id, basestring):
328
 
            raise errors.InvalidRevisionId(revision_id=revision_id,
329
 
                                           branch=self)
 
306
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
330
307
        return self._revision_store.get_revisions([revision_id],
331
308
                                                  self.get_transaction())[0]
332
309
    @needs_read_lock
420
397
        revision_ids. Each altered file-ids has the exact revision_ids that
421
398
        altered it listed explicitly.
422
399
        """
423
 
        assert self._serializer.support_altered_by_hack, \
 
400
        assert isinstance(self._format, (RepositoryFormat5,
 
401
                                         RepositoryFormat6,
 
402
                                         RepositoryFormat7,
 
403
                                         RepositoryFormatKnit1)), \
424
404
            ("fileids_altered_by_revision_ids only supported for branches " 
425
405
             "which store inventory as unnested xml, not on %r" % self)
426
406
        selected_revision_ids = set(revision_ids)
435
415
        # revisions. We don't need to see all lines in the inventory because
436
416
        # only those added in an inventory in rev X can contain a revision=X
437
417
        # line.
438
 
        pb = ui.ui_factory.nested_progress_bar()
439
 
        try:
440
 
            for line in w.iter_lines_added_or_present_in_versions(
441
 
                selected_revision_ids, pb=pb):
442
 
                start = line.find('file_id="')+9
443
 
                if start < 9: continue
444
 
                end = line.find('"', start)
445
 
                assert end>= 0
446
 
                file_id = _unescape_xml(line[start:end])
 
418
        for line in w.iter_lines_added_or_present_in_versions(selected_revision_ids):
 
419
            start = line.find('file_id="')+9
 
420
            if start < 9: continue
 
421
            end = line.find('"', start)
 
422
            assert end>= 0
 
423
            file_id = _unescape_xml(line[start:end])
447
424
 
448
 
                start = line.find('revision="')+10
449
 
                if start < 10: continue
450
 
                end = line.find('"', start)
451
 
                assert end>= 0
452
 
                revision_id = _unescape_xml(line[start:end])
453
 
                if revision_id in selected_revision_ids:
454
 
                    result.setdefault(file_id, set()).add(revision_id)
455
 
        finally:
456
 
            pb.finished()
 
425
            start = line.find('revision="')+10
 
426
            if start < 10: continue
 
427
            end = line.find('"', start)
 
428
            assert end>= 0
 
429
            revision_id = _unescape_xml(line[start:end])
 
430
            if revision_id in selected_revision_ids:
 
431
                result.setdefault(file_id, set()).add(revision_id)
457
432
        return result
458
433
 
459
434
    @needs_read_lock
473
448
        :param revision_id: The expected revision id of the inventory.
474
449
        :param xml: A serialised inventory.
475
450
        """
476
 
        result = self._serializer.read_inventory_from_string(xml)
477
 
        result.root.revision = revision_id
478
 
        return result
479
 
 
480
 
    def serialise_inventory(self, inv):
481
 
        return self._serializer.write_inventory_to_string(inv)
 
451
        return xml5.serializer_v5.read_inventory_from_string(xml)
482
452
 
483
453
    @needs_read_lock
484
454
    def get_inventory_xml(self, revision_id):
506
476
        :return: a dictionary of revision_id->revision_parents_list.
507
477
        """
508
478
        # special case NULL_REVISION
509
 
        if revision_id == _mod_revision.NULL_REVISION:
 
479
        if revision_id == NULL_REVISION:
510
480
            return {}
511
 
        a_weave = self.get_inventory_weave()
512
 
        all_revisions = self._eliminate_revisions_not_present(
513
 
                                a_weave.versions())
514
 
        entire_graph = dict([(node, a_weave.get_parents(node)) for 
 
481
        weave = self.get_inventory_weave()
 
482
        all_revisions = self._eliminate_revisions_not_present(weave.versions())
 
483
        entire_graph = dict([(node, weave.get_parents(node)) for 
515
484
                             node in all_revisions])
516
485
        if revision_id is None:
517
486
            return entire_graph
536
505
        :param revision_ids: an iterable of revisions to graph or None for all.
537
506
        :return: a Graph object with the graph reachable from revision_ids.
538
507
        """
539
 
        result = graph.Graph()
 
508
        result = Graph()
540
509
        if not revision_ids:
541
510
            pending = set(self.all_revision_ids())
542
511
            required = set([])
543
512
        else:
544
513
            pending = set(revision_ids)
545
514
            # special case NULL_REVISION
546
 
            if _mod_revision.NULL_REVISION in pending:
547
 
                pending.remove(_mod_revision.NULL_REVISION)
 
515
            if NULL_REVISION in pending:
 
516
                pending.remove(NULL_REVISION)
548
517
            required = set(pending)
549
518
        done = set([])
550
519
        while len(pending):
604
573
        """
605
574
        # TODO: refactor this to use an existing revision object
606
575
        # so we don't need to read it in twice.
607
 
        if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
608
 
            return RevisionTree(self, Inventory(root_id=None), 
609
 
                                _mod_revision.NULL_REVISION)
 
576
        if revision_id is None or revision_id == NULL_REVISION:
 
577
            return RevisionTree(self, Inventory(), NULL_REVISION)
610
578
        else:
611
579
            inv = self.get_revision_inventory(revision_id)
612
580
            return RevisionTree(self, inv, revision_id)
617
585
 
618
586
        `revision_id` may not be None or 'null:'"""
619
587
        assert None not in revision_ids
620
 
        assert _mod_revision.NULL_REVISION not in revision_ids
 
588
        assert NULL_REVISION not in revision_ids
621
589
        texts = self.get_inventory_weave().get_texts(revision_ids)
622
590
        for text, revision_id in zip(texts, revision_ids):
623
591
            inv = self.deserialise_inventory(revision_id, text)
725
693
        warning("Format %s for %s is deprecated - please use 'bzr upgrade' to get better performance"
726
694
                % (self._format, self.bzrdir.transport.base))
727
695
 
728
 
    def supports_rich_root(self):
729
 
        return self._format.rich_root_data
730
 
 
731
696
 
732
697
class AllInOneRepository(Repository):
733
698
    """Legacy support - the repository behaviour for all-in-one branches."""
796
761
            parent_trees[p_id] = repository.revision_tree(None)
797
762
 
798
763
    inv = revision_tree.inventory
 
764
    
 
765
    # backwards compatability hack: skip the root id.
799
766
    entries = inv.iter_entries()
800
 
    # backwards compatability hack: skip the root id.
801
 
    if not repository.supports_rich_root():
802
 
        path, root = entries.next()
803
 
        if root.revision != rev.revision_id:
804
 
            raise errors.IncompatibleRevision(repr(repository))
 
767
    entries.next()
805
768
    # Add the texts that are not already present
806
769
    for path, ie in entries:
807
770
        w = repository.weave_store.get_weave_or_empty(ie.file_id,
944
907
        :return: a dictionary of revision_id->revision_parents_list.
945
908
        """
946
909
        # special case NULL_REVISION
947
 
        if revision_id == _mod_revision.NULL_REVISION:
 
910
        if revision_id == NULL_REVISION:
948
911
            return {}
949
 
        a_weave = self._get_revision_vf()
950
 
        entire_graph = a_weave.get_graph()
 
912
        weave = self._get_revision_vf()
 
913
        entire_graph = weave.get_graph()
951
914
        if revision_id is None:
952
 
            return a_weave.get_graph()
953
 
        elif revision_id not in a_weave:
 
915
            return weave.get_graph()
 
916
        elif revision_id not in weave:
954
917
            raise errors.NoSuchRevision(self, revision_id)
955
918
        else:
956
919
            # add what can be reached from revision_id
958
921
            pending = set([revision_id])
959
922
            while len(pending) > 0:
960
923
                node = pending.pop()
961
 
                result[node] = a_weave.get_parents(node)
 
924
                result[node] = weave.get_parents(node)
962
925
                for revision_id in result[node]:
963
926
                    if revision_id not in result:
964
927
                        pending.add(revision_id)
971
934
        :param revision_ids: an iterable of revisions to graph or None for all.
972
935
        :return: a Graph object with the graph reachable from revision_ids.
973
936
        """
974
 
        result = graph.Graph()
 
937
        result = Graph()
975
938
        vf = self._get_revision_vf()
976
939
        versions = set(vf.versions())
977
940
        if not revision_ids:
980
943
        else:
981
944
            pending = set(revision_ids)
982
945
            # special case NULL_REVISION
983
 
            if _mod_revision.NULL_REVISION in pending:
984
 
                pending.remove(_mod_revision.NULL_REVISION)
 
946
            if NULL_REVISION in pending:
 
947
                pending.remove(NULL_REVISION)
985
948
            required = set(pending)
986
949
        done = set([])
987
950
        while len(pending):
1022
985
        return self._get_revision_vf().get_parents(revision_id)
1023
986
 
1024
987
 
1025
 
class KnitRepository2(KnitRepository):
1026
 
    """"""
1027
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1028
 
                 control_store, text_store):
1029
 
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1030
 
                              _revision_store, control_store, text_store)
1031
 
        self._serializer = xml6.serializer_v6
1032
 
 
1033
 
    def deserialise_inventory(self, revision_id, xml):
1034
 
        """Transform the xml into an inventory object. 
1035
 
 
1036
 
        :param revision_id: The expected revision id of the inventory.
1037
 
        :param xml: A serialised inventory.
1038
 
        """
1039
 
        result = self._serializer.read_inventory_from_string(xml)
1040
 
        assert result.root.revision is not None
1041
 
        return result
1042
 
 
1043
 
    def serialise_inventory(self, inv):
1044
 
        """Transform the inventory object into XML text.
1045
 
 
1046
 
        :param revision_id: The expected revision id of the inventory.
1047
 
        :param xml: A serialised inventory.
1048
 
        """
1049
 
        assert inv.revision_id is not None
1050
 
        assert inv.root.revision is not None
1051
 
        return KnitRepository.serialise_inventory(self, inv)
1052
 
 
1053
 
    def get_commit_builder(self, branch, parents, config, timestamp=None, 
1054
 
                           timezone=None, committer=None, revprops=None, 
1055
 
                           revision_id=None):
1056
 
        """Obtain a CommitBuilder for this repository.
1057
 
        
1058
 
        :param branch: Branch to commit to.
1059
 
        :param parents: Revision ids of the parents of the new revision.
1060
 
        :param config: Configuration to use.
1061
 
        :param timestamp: Optional timestamp recorded for commit.
1062
 
        :param timezone: Optional timezone for timestamp.
1063
 
        :param committer: Optional committer to set for commit.
1064
 
        :param revprops: Optional dictionary of revision properties.
1065
 
        :param revision_id: Optional revision id.
1066
 
        """
1067
 
        return RootCommitBuilder(self, parents, config, timestamp, timezone,
1068
 
                                 committer, revprops, revision_id)
1069
 
 
1070
 
 
1071
988
class RepositoryFormat(object):
1072
989
    """A repository format.
1073
990
 
1166
1083
                                  transport,
1167
1084
                                  control_files,
1168
1085
                                  prefixed=True,
1169
 
                                  versionedfile_class=weave.WeaveFile,
1170
 
                                  versionedfile_kwargs={},
 
1086
                                  versionedfile_class=WeaveFile,
1171
1087
                                  escaped=False):
1172
1088
        weave_transport = control_files._transport.clone(name)
1173
1089
        dir_mode = control_files._dir_mode
1176
1092
                                  dir_mode=dir_mode,
1177
1093
                                  file_mode=file_mode,
1178
1094
                                  versionedfile_class=versionedfile_class,
1179
 
                                  versionedfile_kwargs=versionedfile_kwargs,
1180
1095
                                  escaped=escaped)
1181
1096
 
1182
1097
    def initialize(self, a_bzrdir, shared=False):
1198
1113
        """
1199
1114
        return True
1200
1115
 
1201
 
    def check_conversion_target(self, target_format):
1202
 
        raise NotImplementedError(self.check_conversion_target)
1203
 
 
1204
1116
    def open(self, a_bzrdir, _found=False):
1205
1117
        """Return an instance of this format for the bzrdir a_bzrdir.
1206
1118
        
1225
1137
class PreSplitOutRepositoryFormat(RepositoryFormat):
1226
1138
    """Base class for the pre split out repository formats."""
1227
1139
 
1228
 
    rich_root_data = False
1229
 
 
1230
1140
    def initialize(self, a_bzrdir, shared=False, _internal=False):
1231
1141
        """Create a weave repository.
1232
1142
        
1233
1143
        TODO: when creating split out bzr branch formats, move this to a common
1234
1144
        base for Format5, Format6. or something like that.
1235
1145
        """
 
1146
        from bzrlib.weavefile import write_weave_v5
 
1147
        from bzrlib.weave import Weave
 
1148
 
1236
1149
        if shared:
1237
1150
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
1238
1151
 
1242
1155
        
1243
1156
        # Create an empty weave
1244
1157
        sio = StringIO()
1245
 
        weavefile.write_weave_v5(weave.Weave(), sio)
 
1158
        write_weave_v5(Weave(), sio)
1246
1159
        empty_weave = sio.getvalue()
1247
1160
 
1248
1161
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1252
1165
        
1253
1166
        # FIXME: RBC 20060125 don't peek under the covers
1254
1167
        # NB: no need to escape relative paths that are url safe.
1255
 
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
1256
 
                                'branch-lock', lockable_files.TransportLock)
 
1168
        control_files = LockableFiles(a_bzrdir.transport, 'branch-lock',
 
1169
                                      TransportLock)
1257
1170
        control_files.create_lock()
1258
1171
        control_files.lock_write()
1259
1172
        control_files._transport.mkdir_multi(dirs,
1293
1206
                                  control_store=control_store,
1294
1207
                                  text_store=text_store)
1295
1208
 
1296
 
    def check_conversion_target(self, target_format):
1297
 
        pass
1298
 
 
1299
1209
 
1300
1210
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1301
1211
    """Bzr repository format 4.
1412
1322
class MetaDirRepositoryFormat(RepositoryFormat):
1413
1323
    """Common base class for the new repositories using the metadir layout."""
1414
1324
 
1415
 
    rich_root_data = False
1416
 
 
1417
1325
    def __init__(self):
1418
1326
        super(MetaDirRepositoryFormat, self).__init__()
1419
1327
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1423
1331
        # FIXME: RBC 20060125 don't peek under the covers
1424
1332
        # NB: no need to escape relative paths that are url safe.
1425
1333
        repository_transport = a_bzrdir.get_repository_transport(self)
1426
 
        control_files = lockable_files.LockableFiles(repository_transport,
1427
 
                                'lock', lockdir.LockDir)
 
1334
        control_files = LockableFiles(repository_transport, 'lock', LockDir)
1428
1335
        control_files.create_lock()
1429
1336
        return control_files
1430
1337
 
1472
1379
        """See RepositoryFormat.get_format_description()."""
1473
1380
        return "Weave repository format 7"
1474
1381
 
1475
 
    def check_conversion_target(self, target_format):
1476
 
        pass
1477
 
 
1478
1382
    def _get_revision_store(self, repo_transport, control_files):
1479
1383
        """See RepositoryFormat._get_revision_store()."""
1480
1384
        return self._get_text_rev_store(repo_transport,
1496
1400
        :param shared: If true the repository will be initialized as a shared
1497
1401
                       repository.
1498
1402
        """
 
1403
        from bzrlib.weavefile import write_weave_v5
 
1404
        from bzrlib.weave import Weave
 
1405
 
1499
1406
        # Create an empty weave
1500
1407
        sio = StringIO()
1501
 
        weavefile.write_weave_v5(weave.Weave(), sio)
 
1408
        write_weave_v5(Weave(), sio)
1502
1409
        empty_weave = sio.getvalue()
1503
1410
 
1504
1411
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1524
1431
            repo_transport = _override_transport
1525
1432
        else:
1526
1433
            repo_transport = a_bzrdir.get_repository_transport(None)
1527
 
        control_files = lockable_files.LockableFiles(repo_transport,
1528
 
                                'lock', lockdir.LockDir)
 
1434
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
1529
1435
        text_store = self._get_text_store(repo_transport, control_files)
1530
1436
        control_store = self._get_control_store(repo_transport, control_files)
1531
1437
        _revision_store = self._get_revision_store(repo_transport, control_files)
1537
1443
                                 text_store=text_store)
1538
1444
 
1539
1445
 
1540
 
class RepositoryFormatKnit(MetaDirRepositoryFormat):
1541
 
    """Bzr repository knit format (generalized). 
 
1446
class RepositoryFormatKnit1(MetaDirRepositoryFormat):
 
1447
    """Bzr repository knit format 1.
1542
1448
 
1543
1449
    This repository format has:
1544
1450
     - knits for file texts and inventory
1549
1455
     - an optional 'shared-storage' flag
1550
1456
     - an optional 'no-working-trees' flag
1551
1457
     - a LockDir lock
 
1458
 
 
1459
    This format was introduced in bzr 0.8.
1552
1460
    """
1553
1461
 
1554
1462
    def _get_control_store(self, repo_transport, control_files):
1557
1465
            repo_transport,
1558
1466
            prefixed=False,
1559
1467
            file_mode=control_files._file_mode,
1560
 
            versionedfile_class=knit.KnitVersionedFile,
1561
 
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
 
1468
            versionedfile_class=KnitVersionedFile,
 
1469
            versionedfile_kwargs={'factory':KnitPlainFactory()},
1562
1470
            )
1563
1471
 
 
1472
    def get_format_string(self):
 
1473
        """See RepositoryFormat.get_format_string()."""
 
1474
        return "Bazaar-NG Knit Repository Format 1"
 
1475
 
 
1476
    def get_format_description(self):
 
1477
        """See RepositoryFormat.get_format_description()."""
 
1478
        return "Knit repository format 1"
 
1479
 
1564
1480
    def _get_revision_store(self, repo_transport, control_files):
1565
1481
        """See RepositoryFormat._get_revision_store()."""
1566
1482
        from bzrlib.store.revision.knit import KnitRevisionStore
1569
1485
            file_mode=control_files._file_mode,
1570
1486
            prefixed=False,
1571
1487
            precious=True,
1572
 
            versionedfile_class=knit.KnitVersionedFile,
1573
 
            versionedfile_kwargs={'delta':False,
1574
 
                                  'factory':knit.KnitPlainFactory(),
1575
 
                                 },
 
1488
            versionedfile_class=KnitVersionedFile,
 
1489
            versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory()},
1576
1490
            escaped=True,
1577
1491
            )
1578
1492
        return KnitRevisionStore(versioned_file_store)
1580
1494
    def _get_text_store(self, transport, control_files):
1581
1495
        """See RepositoryFormat._get_text_store()."""
1582
1496
        return self._get_versioned_file_store('knits',
1583
 
                                  transport,
1584
 
                                  control_files,
1585
 
                                  versionedfile_class=knit.KnitVersionedFile,
1586
 
                                  versionedfile_kwargs={
1587
 
                                      'create_parent_dir':True,
1588
 
                                      'delay_create':True,
1589
 
                                      'dir_mode':control_files._dir_mode,
1590
 
                                  },
1591
 
                                  escaped=True)
 
1497
                                              transport,
 
1498
                                              control_files,
 
1499
                                              versionedfile_class=KnitVersionedFile,
 
1500
                                              escaped=True)
1592
1501
 
1593
1502
    def initialize(self, a_bzrdir, shared=False):
1594
1503
        """Create a knit format 1 repository.
1605
1514
        
1606
1515
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1607
1516
        repo_transport = a_bzrdir.get_repository_transport(None)
1608
 
        control_files = lockable_files.LockableFiles(repo_transport,
1609
 
                                'lock', lockdir.LockDir)
 
1517
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
1610
1518
        control_store = self._get_control_store(repo_transport, control_files)
1611
1519
        transaction = transactions.WriteTransaction()
1612
1520
        # trigger a write of the inventory store.
1630
1538
            repo_transport = _override_transport
1631
1539
        else:
1632
1540
            repo_transport = a_bzrdir.get_repository_transport(None)
1633
 
        control_files = lockable_files.LockableFiles(repo_transport,
1634
 
                                'lock', lockdir.LockDir)
 
1541
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
1635
1542
        text_store = self._get_text_store(repo_transport, control_files)
1636
1543
        control_store = self._get_control_store(repo_transport, control_files)
1637
1544
        _revision_store = self._get_revision_store(repo_transport, control_files)
1643
1550
                              text_store=text_store)
1644
1551
 
1645
1552
 
1646
 
class RepositoryFormatKnit1(RepositoryFormatKnit):
1647
 
    """Bzr repository knit format 1.
1648
 
 
1649
 
    This repository format has:
1650
 
     - knits for file texts and inventory
1651
 
     - hash subdirectory based stores.
1652
 
     - knits for revisions and signatures
1653
 
     - TextStores for revisions and signatures.
1654
 
     - a format marker of its own
1655
 
     - an optional 'shared-storage' flag
1656
 
     - an optional 'no-working-trees' flag
1657
 
     - a LockDir lock
1658
 
 
1659
 
    This format was introduced in bzr 0.8.
1660
 
    """
1661
 
    def get_format_string(self):
1662
 
        """See RepositoryFormat.get_format_string()."""
1663
 
        return "Bazaar-NG Knit Repository Format 1"
1664
 
 
1665
 
    def get_format_description(self):
1666
 
        """See RepositoryFormat.get_format_description()."""
1667
 
        return "Knit repository format 1"
1668
 
 
1669
 
    def check_conversion_target(self, target_format):
1670
 
        pass
1671
 
 
1672
 
 
1673
 
class RepositoryFormatKnit2(RepositoryFormatKnit):
1674
 
    """Bzr repository knit format 2.
1675
 
 
1676
 
    THIS FORMAT IS EXPERIMENTAL
1677
 
    This repository format has:
1678
 
     - knits for file texts and inventory
1679
 
     - hash subdirectory based stores.
1680
 
     - knits for revisions and signatures
1681
 
     - TextStores for revisions and signatures.
1682
 
     - a format marker of its own
1683
 
     - an optional 'shared-storage' flag
1684
 
     - an optional 'no-working-trees' flag
1685
 
     - a LockDir lock
1686
 
     - Support for recording full info about the tree root
1687
 
 
1688
 
    """
1689
 
    
1690
 
    rich_root_data = True
1691
 
 
1692
 
    def get_format_string(self):
1693
 
        """See RepositoryFormat.get_format_string()."""
1694
 
        return "Bazaar Knit Repository Format 2\n"
1695
 
 
1696
 
    def get_format_description(self):
1697
 
        """See RepositoryFormat.get_format_description()."""
1698
 
        return "Knit repository format 2"
1699
 
 
1700
 
    def check_conversion_target(self, target_format):
1701
 
        if not target_format.rich_root_data:
1702
 
            raise errors.BadConversionTarget(
1703
 
                'Does not support rich root data.', target_format)
1704
 
 
1705
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1706
 
        """See RepositoryFormat.open().
1707
 
        
1708
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1709
 
                                    repository at a slightly different url
1710
 
                                    than normal. I.e. during 'upgrade'.
1711
 
        """
1712
 
        if not _found:
1713
 
            format = RepositoryFormat.find_format(a_bzrdir)
1714
 
            assert format.__class__ ==  self.__class__
1715
 
        if _override_transport is not None:
1716
 
            repo_transport = _override_transport
1717
 
        else:
1718
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1719
 
        control_files = lockable_files.LockableFiles(repo_transport, 'lock',
1720
 
                                                     lockdir.LockDir)
1721
 
        text_store = self._get_text_store(repo_transport, control_files)
1722
 
        control_store = self._get_control_store(repo_transport, control_files)
1723
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1724
 
        return KnitRepository2(_format=self,
1725
 
                               a_bzrdir=a_bzrdir,
1726
 
                               control_files=control_files,
1727
 
                               _revision_store=_revision_store,
1728
 
                               control_store=control_store,
1729
 
                               text_store=text_store)
1730
 
 
1731
 
 
1732
 
 
1733
1553
# formats which have no format string are not discoverable
1734
1554
# and not independently creatable, so are not registered.
1735
1555
RepositoryFormat.register_format(RepositoryFormat7())
1736
1556
_default_format = RepositoryFormatKnit1()
1737
1557
RepositoryFormat.register_format(_default_format)
1738
 
RepositoryFormat.register_format(RepositoryFormatKnit2())
1739
1558
RepositoryFormat.set_default_format(_default_format)
1740
1559
_legacy_formats = [RepositoryFormat4(),
1741
1560
                   RepositoryFormat5(),
1754
1573
    InterRepository.get(other).method_name(parameters).
1755
1574
    """
1756
1575
 
1757
 
    _optimisers = []
 
1576
    _optimisers = set()
1758
1577
    """The available optimised InterRepository types."""
1759
1578
 
 
1579
    @needs_write_lock
1760
1580
    def copy_content(self, revision_id=None, basis=None):
1761
 
        raise NotImplementedError(self.copy_content)
1762
 
 
 
1581
        """Make a complete copy of the content in self into destination.
 
1582
        
 
1583
        This is a destructive operation! Do not use it on existing 
 
1584
        repositories.
 
1585
 
 
1586
        :param revision_id: Only copy the content needed to construct
 
1587
                            revision_id and its parents.
 
1588
        :param basis: Copy the needed data preferentially from basis.
 
1589
        """
 
1590
        try:
 
1591
            self.target.set_make_working_trees(self.source.make_working_trees())
 
1592
        except NotImplementedError:
 
1593
            pass
 
1594
        # grab the basis available data
 
1595
        if basis is not None:
 
1596
            self.target.fetch(basis, revision_id=revision_id)
 
1597
        # but don't bother fetching if we have the needed data now.
 
1598
        if (revision_id not in (None, NULL_REVISION) and 
 
1599
            self.target.has_revision(revision_id)):
 
1600
            return
 
1601
        self.target.fetch(self.source, revision_id=revision_id)
 
1602
 
 
1603
    @needs_write_lock
1763
1604
    def fetch(self, revision_id=None, pb=None):
1764
1605
        """Fetch the content required to construct revision_id.
1765
1606
 
1766
 
        The content is copied from self.source to self.target.
 
1607
        The content is copied from source to target.
1767
1608
 
1768
1609
        :param revision_id: if None all content is copied, if NULL_REVISION no
1769
1610
                            content is copied.
1773
1614
        Returns the copied revision count and the failed revisions in a tuple:
1774
1615
        (copied, failures).
1775
1616
        """
1776
 
        raise NotImplementedError(self.fetch)
1777
 
   
 
1617
        from bzrlib.fetch import GenericRepoFetcher
 
1618
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
 
1619
               self.source, self.source._format, self.target, self.target._format)
 
1620
        f = GenericRepoFetcher(to_repository=self.target,
 
1621
                               from_repository=self.source,
 
1622
                               last_revision=revision_id,
 
1623
                               pb=pb)
 
1624
        return f.count_copied, f.failed_revisions
 
1625
 
1778
1626
    @needs_read_lock
1779
1627
    def missing_revision_ids(self, revision_id=None):
1780
1628
        """Return the revision ids that source has that target does not.
1788
1636
        target_ids = set(self.target.all_revision_ids())
1789
1637
        if revision_id is not None:
1790
1638
            source_ids = self.source.get_ancestry(revision_id)
1791
 
            assert source_ids[0] is None
 
1639
            assert source_ids[0] == None
1792
1640
            source_ids.pop(0)
1793
1641
        else:
1794
1642
            source_ids = self.source.all_revision_ids()
1799
1647
        return [rev_id for rev_id in source_ids if rev_id in result_set]
1800
1648
 
1801
1649
 
1802
 
class InterSameDataRepository(InterRepository):
1803
 
    """Code for converting between repositories that represent the same data.
1804
 
    
1805
 
    Data format and model must match for this to work.
1806
 
    """
1807
 
 
1808
 
    _matching_repo_format = RepositoryFormat4()
1809
 
    """Repository format for testing with."""
1810
 
 
1811
 
    @staticmethod
1812
 
    def is_compatible(source, target):
1813
 
        if not isinstance(source, Repository):
1814
 
            return False
1815
 
        if not isinstance(target, Repository):
1816
 
            return False
1817
 
        if source._format.rich_root_data == target._format.rich_root_data:
1818
 
            return True
1819
 
        else:
1820
 
            return False
1821
 
 
1822
 
    @needs_write_lock
1823
 
    def copy_content(self, revision_id=None, basis=None):
1824
 
        """Make a complete copy of the content in self into destination.
1825
 
        
1826
 
        This is a destructive operation! Do not use it on existing 
1827
 
        repositories.
1828
 
 
1829
 
        :param revision_id: Only copy the content needed to construct
1830
 
                            revision_id and its parents.
1831
 
        :param basis: Copy the needed data preferentially from basis.
1832
 
        """
1833
 
        try:
1834
 
            self.target.set_make_working_trees(self.source.make_working_trees())
1835
 
        except NotImplementedError:
1836
 
            pass
1837
 
        # grab the basis available data
1838
 
        if basis is not None:
1839
 
            self.target.fetch(basis, revision_id=revision_id)
1840
 
        # but don't bother fetching if we have the needed data now.
1841
 
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
1842
 
            self.target.has_revision(revision_id)):
1843
 
            return
1844
 
        self.target.fetch(self.source, revision_id=revision_id)
1845
 
 
1846
 
    @needs_write_lock
1847
 
    def fetch(self, revision_id=None, pb=None):
1848
 
        """See InterRepository.fetch()."""
1849
 
        from bzrlib.fetch import GenericRepoFetcher
1850
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1851
 
               self.source, self.source._format, self.target, 
1852
 
               self.target._format)
1853
 
        f = GenericRepoFetcher(to_repository=self.target,
1854
 
                               from_repository=self.source,
1855
 
                               last_revision=revision_id,
1856
 
                               pb=pb)
1857
 
        return f.count_copied, f.failed_revisions
1858
 
 
1859
 
 
1860
 
class InterWeaveRepo(InterSameDataRepository):
 
1650
class InterWeaveRepo(InterRepository):
1861
1651
    """Optimised code paths between Weave based repositories."""
1862
1652
 
1863
1653
    _matching_repo_format = RepositoryFormat7()
1949
1739
        # - RBC 20060209
1950
1740
        if revision_id is not None:
1951
1741
            source_ids = self.source.get_ancestry(revision_id)
1952
 
            assert source_ids[0] is None
 
1742
            assert source_ids[0] == None
1953
1743
            source_ids.pop(0)
1954
1744
        else:
1955
1745
            source_ids = self.source._all_possible_ids()
1975
1765
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1976
1766
 
1977
1767
 
1978
 
class InterKnitRepo(InterSameDataRepository):
 
1768
class InterKnitRepo(InterRepository):
1979
1769
    """Optimised code paths between Knit based repositories."""
1980
1770
 
1981
1771
    _matching_repo_format = RepositoryFormatKnit1()
2012
1802
        """See InterRepository.missing_revision_ids()."""
2013
1803
        if revision_id is not None:
2014
1804
            source_ids = self.source.get_ancestry(revision_id)
2015
 
            assert source_ids[0] is None
 
1805
            assert source_ids[0] == None
2016
1806
            source_ids.pop(0)
2017
1807
        else:
2018
1808
            source_ids = self.source._all_possible_ids()
2037
1827
            # that against the revision records.
2038
1828
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
2039
1829
 
2040
 
 
2041
 
class InterModel1and2(InterRepository):
2042
 
 
2043
 
    _matching_repo_format = None
2044
 
 
2045
 
    @staticmethod
2046
 
    def is_compatible(source, target):
2047
 
        if not isinstance(source, Repository):
2048
 
            return False
2049
 
        if not isinstance(target, Repository):
2050
 
            return False
2051
 
        if not source._format.rich_root_data and target._format.rich_root_data:
2052
 
            return True
2053
 
        else:
2054
 
            return False
2055
 
 
2056
 
    @needs_write_lock
2057
 
    def fetch(self, revision_id=None, pb=None):
2058
 
        """See InterRepository.fetch()."""
2059
 
        from bzrlib.fetch import Model1toKnit2Fetcher
2060
 
        f = Model1toKnit2Fetcher(to_repository=self.target,
2061
 
                                 from_repository=self.source,
2062
 
                                 last_revision=revision_id,
2063
 
                                 pb=pb)
2064
 
        return f.count_copied, f.failed_revisions
2065
 
 
2066
 
    @needs_write_lock
2067
 
    def copy_content(self, revision_id=None, basis=None):
2068
 
        """Make a complete copy of the content in self into destination.
2069
 
        
2070
 
        This is a destructive operation! Do not use it on existing 
2071
 
        repositories.
2072
 
 
2073
 
        :param revision_id: Only copy the content needed to construct
2074
 
                            revision_id and its parents.
2075
 
        :param basis: Copy the needed data preferentially from basis.
2076
 
        """
2077
 
        try:
2078
 
            self.target.set_make_working_trees(self.source.make_working_trees())
2079
 
        except NotImplementedError:
2080
 
            pass
2081
 
        # grab the basis available data
2082
 
        if basis is not None:
2083
 
            self.target.fetch(basis, revision_id=revision_id)
2084
 
        # but don't bother fetching if we have the needed data now.
2085
 
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
2086
 
            self.target.has_revision(revision_id)):
2087
 
            return
2088
 
        self.target.fetch(self.source, revision_id=revision_id)
2089
 
 
2090
 
 
2091
 
class InterKnit1and2(InterKnitRepo):
2092
 
 
2093
 
    _matching_repo_format = None
2094
 
 
2095
 
    @staticmethod
2096
 
    def is_compatible(source, target):
2097
 
        """Be compatible with Knit1 source and Knit2 target"""
2098
 
        try:
2099
 
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
2100
 
                    isinstance(target._format, (RepositoryFormatKnit2)))
2101
 
        except AttributeError:
2102
 
            return False
2103
 
 
2104
 
    @needs_write_lock
2105
 
    def fetch(self, revision_id=None, pb=None):
2106
 
        """See InterRepository.fetch()."""
2107
 
        from bzrlib.fetch import Knit1to2Fetcher
2108
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2109
 
               self.source, self.source._format, self.target, 
2110
 
               self.target._format)
2111
 
        f = Knit1to2Fetcher(to_repository=self.target,
2112
 
                            from_repository=self.source,
2113
 
                            last_revision=revision_id,
2114
 
                            pb=pb)
2115
 
        return f.count_copied, f.failed_revisions
2116
 
 
2117
 
 
2118
 
InterRepository.register_optimiser(InterSameDataRepository)
2119
1830
InterRepository.register_optimiser(InterWeaveRepo)
2120
1831
InterRepository.register_optimiser(InterKnitRepo)
2121
 
InterRepository.register_optimiser(InterModel1and2)
2122
 
InterRepository.register_optimiser(InterKnit1and2)
2123
1832
 
2124
1833
 
2125
1834
class RepositoryTestProviderAdapter(object):
2137
1846
        self._formats = formats
2138
1847
    
2139
1848
    def adapt(self, test):
2140
 
        result = unittest.TestSuite()
 
1849
        result = TestSuite()
2141
1850
        for repository_format, bzrdir_format in self._formats:
2142
1851
            new_test = deepcopy(test)
2143
1852
            new_test.transport_server = self._transport_server
2167
1876
        self._formats = formats
2168
1877
    
2169
1878
    def adapt(self, test):
2170
 
        result = unittest.TestSuite()
 
1879
        result = TestSuite()
2171
1880
        for interrepo_class, repository_format, repository_format_to in self._formats:
2172
1881
            new_test = deepcopy(test)
2173
1882
            new_test.transport_server = self._transport_server
2190
1899
        # default format.
2191
1900
        # XXX: robertc 20060220 reinstate this when there are two supported
2192
1901
        # formats which do not have an optimal code path between them.
2193
 
        #result.append((InterRepository,
2194
 
        #               RepositoryFormat6(),
2195
 
        #               RepositoryFormatKnit1()))
 
1902
        result.append((InterRepository,
 
1903
                       RepositoryFormat6(),
 
1904
                       RepositoryFormatKnit1()))
2196
1905
        for optimiser in InterRepository._optimisers:
2197
 
            if optimiser._matching_repo_format is not None:
2198
 
                result.append((optimiser,
2199
 
                               optimiser._matching_repo_format,
2200
 
                               optimiser._matching_repo_format
2201
 
                               ))
 
1906
            result.append((optimiser,
 
1907
                           optimiser._matching_repo_format,
 
1908
                           optimiser._matching_repo_format
 
1909
                           ))
2202
1910
        # if there are specific combinations we want to use, we can add them 
2203
1911
        # here.
2204
 
        result.append((InterModel1and2, RepositoryFormat5(),
2205
 
                       RepositoryFormatKnit2()))
2206
 
        result.append((InterKnit1and2, RepositoryFormatKnit1(),
2207
 
                       RepositoryFormatKnit2()))
2208
1912
        return result
2209
1913
 
2210
1914
 
2237
1941
        self.step('Moving repository to repository.backup')
2238
1942
        self.repo_dir.transport.move('repository', 'repository.backup')
2239
1943
        backup_transport =  self.repo_dir.transport.clone('repository.backup')
2240
 
        repo._format.check_conversion_target(self.target_format)
2241
1944
        self.source_repo = repo._format.open(self.repo_dir,
2242
1945
            _found=True,
2243
1946
            _override_transport=backup_transport)
2266
1969
    This allows describing a tree to be committed without needing to 
2267
1970
    know the internals of the format of the repository.
2268
1971
    """
2269
 
    
2270
 
    record_root_entry = False
2271
1972
    def __init__(self, repository, parents, config, timestamp=None, 
2272
1973
                 timezone=None, committer=None, revprops=None, 
2273
1974
                 revision_id=None):
2290
1991
            assert isinstance(committer, basestring), type(committer)
2291
1992
            self._committer = committer
2292
1993
 
2293
 
        self.new_inventory = Inventory(None)
 
1994
        self.new_inventory = Inventory()
2294
1995
        self._new_revision_id = revision_id
2295
1996
        self.parents = parents
2296
1997
        self.repository = repository
2316
2017
 
2317
2018
        :return: The revision id of the recorded revision.
2318
2019
        """
2319
 
        rev = _mod_revision.Revision(
2320
 
                       timestamp=self._timestamp,
 
2020
        rev = Revision(timestamp=self._timestamp,
2321
2021
                       timezone=self._timezone,
2322
2022
                       committer=self._committer,
2323
2023
                       message=message,
2329
2029
            self.new_inventory, self._config)
2330
2030
        return self._new_revision_id
2331
2031
 
2332
 
    def revision_tree(self):
2333
 
        """Return the tree that was just committed.
2334
 
 
2335
 
        After calling commit() this can be called to get a RevisionTree
2336
 
        representing the newly committed tree. This is preferred to
2337
 
        calling Repository.revision_tree() because that may require
2338
 
        deserializing the inventory, while we already have a copy in
2339
 
        memory.
2340
 
        """
2341
 
        return RevisionTree(self.repository, self.new_inventory,
2342
 
                            self._new_revision_id)
2343
 
 
2344
2032
    def finish_inventory(self):
2345
2033
        """Tell the builder that the inventory is finished."""
2346
 
        if self.new_inventory.root is None:
2347
 
            symbol_versioning.warn('Root entry should be supplied to'
2348
 
                ' record_entry_contents, as of bzr 0.10.',
2349
 
                 DeprecationWarning, stacklevel=2)
2350
 
            self.new_inventory.add(InventoryDirectory(ROOT_ID, '', None))
2351
2034
        self.new_inventory.revision_id = self._new_revision_id
2352
2035
        self.inv_sha1 = self.repository.add_inventory(
2353
2036
            self._new_revision_id,
2377
2060
    def record_entry_contents(self, ie, parent_invs, path, tree):
2378
2061
        """Record the content of ie from tree into the commit if needed.
2379
2062
 
2380
 
        Side effect: sets ie.revision when unchanged
2381
 
 
2382
2063
        :param ie: An inventory entry present in the commit.
2383
2064
        :param parent_invs: The inventories of the parent revisions of the
2384
2065
            commit.
2386
2067
        :param tree: The tree which contains this entry and should be used to 
2387
2068
        obtain content.
2388
2069
        """
2389
 
        if self.new_inventory.root is None and ie.parent_id is not None:
2390
 
            symbol_versioning.warn('Root entry should be supplied to'
2391
 
                ' record_entry_contents, as of bzr 0.10.',
2392
 
                 DeprecationWarning, stacklevel=2)
2393
 
            self.record_entry_contents(tree.inventory.root.copy(), parent_invs,
2394
 
                                       '', tree)
2395
2070
        self.new_inventory.add(ie)
2396
2071
 
2397
2072
        # ie.revision is always None if the InventoryEntry is considered
2399
2074
        # which may be the sole parent if it is untouched.
2400
2075
        if ie.revision is not None:
2401
2076
            return
2402
 
 
2403
 
        # In this revision format, root entries have no knit or weave
2404
 
        if ie is self.new_inventory.root:
2405
 
            # When serializing out to disk and back in
2406
 
            # root.revision is always _new_revision_id
2407
 
            ie.revision = self._new_revision_id
2408
 
            return
2409
2077
        previous_entries = ie.find_previous_heads(
2410
2078
            parent_invs,
2411
2079
            self.repository.weave_store,
2471
2139
        versionedfile.clear_cache()
2472
2140
 
2473
2141
 
2474
 
class _CommitBuilder(CommitBuilder):
2475
 
    """Temporary class so old CommitBuilders are detected properly
2476
 
    
2477
 
    Note: CommitBuilder works whether or not root entry is recorded.
2478
 
    """
2479
 
 
2480
 
    record_root_entry = True
2481
 
 
2482
 
 
2483
 
class RootCommitBuilder(CommitBuilder):
2484
 
    """This commitbuilder actually records the root id"""
2485
 
    
2486
 
    record_root_entry = True
2487
 
 
2488
 
    def record_entry_contents(self, ie, parent_invs, path, tree):
2489
 
        """Record the content of ie from tree into the commit if needed.
2490
 
 
2491
 
        Side effect: sets ie.revision when unchanged
2492
 
 
2493
 
        :param ie: An inventory entry present in the commit.
2494
 
        :param parent_invs: The inventories of the parent revisions of the
2495
 
            commit.
2496
 
        :param path: The path the entry is at in the tree.
2497
 
        :param tree: The tree which contains this entry and should be used to 
2498
 
        obtain content.
2499
 
        """
2500
 
        assert self.new_inventory.root is not None or ie.parent_id is None
2501
 
        self.new_inventory.add(ie)
2502
 
 
2503
 
        # ie.revision is always None if the InventoryEntry is considered
2504
 
        # for committing. ie.snapshot will record the correct revision 
2505
 
        # which may be the sole parent if it is untouched.
2506
 
        if ie.revision is not None:
2507
 
            return
2508
 
 
2509
 
        previous_entries = ie.find_previous_heads(
2510
 
            parent_invs,
2511
 
            self.repository.weave_store,
2512
 
            self.repository.get_transaction())
2513
 
        # we are creating a new revision for ie in the history store
2514
 
        # and inventory.
2515
 
        ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
2516
 
 
2517
 
 
2518
2142
_unescape_map = {
2519
2143
    'apos':"'",
2520
2144
    'quot':'"',