~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:
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(), """
17
21
from binascii import hexlify
18
22
from copy import deepcopy
19
 
from cStringIO import StringIO
20
23
import re
21
24
import time
22
 
from unittest import TestSuite
23
 
 
24
 
from bzrlib import bzrdir, check, delta, gpg, errors, xml5, ui, transactions, osutils
 
25
import unittest
 
26
 
 
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
    )
 
52
from bzrlib.revisiontree import RevisionTree
 
53
from bzrlib.store.versioned import VersionedFileStore
 
54
from bzrlib.store.text import TextStore
 
55
from bzrlib.testament import Testament
 
56
""")
 
57
 
25
58
from bzrlib.decorators import needs_read_lock, needs_write_lock
26
 
from bzrlib.errors import InvalidRevisionId
27
 
from bzrlib.graph import Graph
28
59
from bzrlib.inter import InterObject
29
60
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
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
36
 
from bzrlib.revisiontree import RevisionTree
37
 
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
38
 
from bzrlib.store.text import TextStore
39
 
from bzrlib import symbol_versioning
40
 
from bzrlib.symbol_versioning import (deprecated_method,
41
 
        zero_nine, 
 
61
from bzrlib.symbol_versioning import (
 
62
        deprecated_method,
 
63
        zero_nine,
42
64
        )
43
 
from bzrlib.testament import Testament
44
65
from bzrlib.trace import mutter, note, warning
45
 
from bzrlib.tsort import topo_sort
46
 
from bzrlib.weave import WeaveFile
47
66
 
48
67
 
49
68
# Old formats display a warning, but only once
75
94
            "Mismatch between inventory revision" \
76
95
            " id and insertion revid (%r, %r)" % (inv.revision_id, revid)
77
96
        assert inv.root is not None
78
 
        inv_text = xml5.serializer_v5.write_inventory_to_string(inv)
 
97
        inv_text = self.serialise_inventory(inv)
79
98
        inv_sha1 = osutils.sha_string(inv_text)
80
99
        inv_vf = self.control_weaves.get_weave('inventory',
81
100
                                               self.get_transaction())
195
214
        # TODO: make sure to construct the right store classes, etc, depending
196
215
        # on whether escaping is required.
197
216
        self._warn_if_deprecated()
 
217
        self._serializer = xml5.serializer_v5
198
218
 
199
219
    def __repr__(self):
200
220
        return '%s(%r)' % (self.__class__.__name__, 
305
325
        or testing the revision graph.
306
326
        """
307
327
        if not revision_id or not isinstance(revision_id, basestring):
308
 
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
 
328
            raise errors.InvalidRevisionId(revision_id=revision_id,
 
329
                                           branch=self)
309
330
        return self._revision_store.get_revisions([revision_id],
310
331
                                                  self.get_transaction())[0]
311
332
    @needs_read_lock
399
420
        revision_ids. Each altered file-ids has the exact revision_ids that
400
421
        altered it listed explicitly.
401
422
        """
402
 
        assert isinstance(self._format, (RepositoryFormat5,
403
 
                                         RepositoryFormat6,
404
 
                                         RepositoryFormat7,
405
 
                                         RepositoryFormatKnit1)), \
 
423
        assert self._serializer.support_altered_by_hack, \
406
424
            ("fileids_altered_by_revision_ids only supported for branches " 
407
425
             "which store inventory as unnested xml, not on %r" % self)
408
426
        selected_revision_ids = set(revision_ids)
417
435
        # revisions. We don't need to see all lines in the inventory because
418
436
        # only those added in an inventory in rev X can contain a revision=X
419
437
        # line.
420
 
        for line in w.iter_lines_added_or_present_in_versions(selected_revision_ids):
421
 
            start = line.find('file_id="')+9
422
 
            if start < 9: continue
423
 
            end = line.find('"', start)
424
 
            assert end>= 0
425
 
            file_id = _unescape_xml(line[start:end])
 
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])
426
447
 
427
 
            start = line.find('revision="')+10
428
 
            if start < 10: continue
429
 
            end = line.find('"', start)
430
 
            assert end>= 0
431
 
            revision_id = _unescape_xml(line[start:end])
432
 
            if revision_id in selected_revision_ids:
433
 
                result.setdefault(file_id, set()).add(revision_id)
 
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()
434
457
        return result
435
458
 
436
459
    @needs_read_lock
450
473
        :param revision_id: The expected revision id of the inventory.
451
474
        :param xml: A serialised inventory.
452
475
        """
453
 
        result = xml5.serializer_v5.read_inventory_from_string(xml)
 
476
        result = self._serializer.read_inventory_from_string(xml)
454
477
        result.root.revision = revision_id
455
478
        return result
456
479
 
 
480
    def serialise_inventory(self, inv):
 
481
        return self._serializer.write_inventory_to_string(inv)
 
482
 
457
483
    @needs_read_lock
458
484
    def get_inventory_xml(self, revision_id):
459
485
        """Get inventory XML as a file object."""
480
506
        :return: a dictionary of revision_id->revision_parents_list.
481
507
        """
482
508
        # special case NULL_REVISION
483
 
        if revision_id == NULL_REVISION:
 
509
        if revision_id == _mod_revision.NULL_REVISION:
484
510
            return {}
485
 
        weave = self.get_inventory_weave()
486
 
        all_revisions = self._eliminate_revisions_not_present(weave.versions())
487
 
        entire_graph = dict([(node, weave.get_parents(node)) for 
 
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 
488
515
                             node in all_revisions])
489
516
        if revision_id is None:
490
517
            return entire_graph
509
536
        :param revision_ids: an iterable of revisions to graph or None for all.
510
537
        :return: a Graph object with the graph reachable from revision_ids.
511
538
        """
512
 
        result = Graph()
 
539
        result = graph.Graph()
513
540
        if not revision_ids:
514
541
            pending = set(self.all_revision_ids())
515
542
            required = set([])
516
543
        else:
517
544
            pending = set(revision_ids)
518
545
            # special case NULL_REVISION
519
 
            if NULL_REVISION in pending:
520
 
                pending.remove(NULL_REVISION)
 
546
            if _mod_revision.NULL_REVISION in pending:
 
547
                pending.remove(_mod_revision.NULL_REVISION)
521
548
            required = set(pending)
522
549
        done = set([])
523
550
        while len(pending):
577
604
        """
578
605
        # TODO: refactor this to use an existing revision object
579
606
        # so we don't need to read it in twice.
580
 
        if revision_id is None or revision_id == NULL_REVISION:
581
 
            return RevisionTree(self, Inventory(), NULL_REVISION)
 
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)
582
610
        else:
583
611
            inv = self.get_revision_inventory(revision_id)
584
612
            return RevisionTree(self, inv, revision_id)
589
617
 
590
618
        `revision_id` may not be None or 'null:'"""
591
619
        assert None not in revision_ids
592
 
        assert NULL_REVISION not in revision_ids
 
620
        assert _mod_revision.NULL_REVISION not in revision_ids
593
621
        texts = self.get_inventory_weave().get_texts(revision_ids)
594
622
        for text, revision_id in zip(texts, revision_ids):
595
623
            inv = self.deserialise_inventory(revision_id, text)
697
725
        warning("Format %s for %s is deprecated - please use 'bzr upgrade' to get better performance"
698
726
                % (self._format, self.bzrdir.transport.base))
699
727
 
 
728
    def supports_rich_root(self):
 
729
        return self._format.rich_root_data
 
730
 
700
731
 
701
732
class AllInOneRepository(Repository):
702
733
    """Legacy support - the repository behaviour for all-in-one branches."""
765
796
            parent_trees[p_id] = repository.revision_tree(None)
766
797
 
767
798
    inv = revision_tree.inventory
768
 
    
 
799
    entries = inv.iter_entries()
769
800
    # backwards compatability hack: skip the root id.
770
 
    entries = inv.iter_entries()
771
 
    entries.next()
 
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))
772
805
    # Add the texts that are not already present
773
806
    for path, ie in entries:
774
807
        w = repository.weave_store.get_weave_or_empty(ie.file_id,
911
944
        :return: a dictionary of revision_id->revision_parents_list.
912
945
        """
913
946
        # special case NULL_REVISION
914
 
        if revision_id == NULL_REVISION:
 
947
        if revision_id == _mod_revision.NULL_REVISION:
915
948
            return {}
916
 
        weave = self._get_revision_vf()
917
 
        entire_graph = weave.get_graph()
 
949
        a_weave = self._get_revision_vf()
 
950
        entire_graph = a_weave.get_graph()
918
951
        if revision_id is None:
919
 
            return weave.get_graph()
920
 
        elif revision_id not in weave:
 
952
            return a_weave.get_graph()
 
953
        elif revision_id not in a_weave:
921
954
            raise errors.NoSuchRevision(self, revision_id)
922
955
        else:
923
956
            # add what can be reached from revision_id
925
958
            pending = set([revision_id])
926
959
            while len(pending) > 0:
927
960
                node = pending.pop()
928
 
                result[node] = weave.get_parents(node)
 
961
                result[node] = a_weave.get_parents(node)
929
962
                for revision_id in result[node]:
930
963
                    if revision_id not in result:
931
964
                        pending.add(revision_id)
938
971
        :param revision_ids: an iterable of revisions to graph or None for all.
939
972
        :return: a Graph object with the graph reachable from revision_ids.
940
973
        """
941
 
        result = Graph()
 
974
        result = graph.Graph()
942
975
        vf = self._get_revision_vf()
943
976
        versions = set(vf.versions())
944
977
        if not revision_ids:
947
980
        else:
948
981
            pending = set(revision_ids)
949
982
            # special case NULL_REVISION
950
 
            if NULL_REVISION in pending:
951
 
                pending.remove(NULL_REVISION)
 
983
            if _mod_revision.NULL_REVISION in pending:
 
984
                pending.remove(_mod_revision.NULL_REVISION)
952
985
            required = set(pending)
953
986
        done = set([])
954
987
        while len(pending):
989
1022
        return self._get_revision_vf().get_parents(revision_id)
990
1023
 
991
1024
 
 
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
 
992
1071
class RepositoryFormat(object):
993
1072
    """A repository format.
994
1073
 
1087
1166
                                  transport,
1088
1167
                                  control_files,
1089
1168
                                  prefixed=True,
1090
 
                                  versionedfile_class=WeaveFile,
 
1169
                                  versionedfile_class=weave.WeaveFile,
 
1170
                                  versionedfile_kwargs={},
1091
1171
                                  escaped=False):
1092
1172
        weave_transport = control_files._transport.clone(name)
1093
1173
        dir_mode = control_files._dir_mode
1096
1176
                                  dir_mode=dir_mode,
1097
1177
                                  file_mode=file_mode,
1098
1178
                                  versionedfile_class=versionedfile_class,
 
1179
                                  versionedfile_kwargs=versionedfile_kwargs,
1099
1180
                                  escaped=escaped)
1100
1181
 
1101
1182
    def initialize(self, a_bzrdir, shared=False):
1117
1198
        """
1118
1199
        return True
1119
1200
 
 
1201
    def check_conversion_target(self, target_format):
 
1202
        raise NotImplementedError(self.check_conversion_target)
 
1203
 
1120
1204
    def open(self, a_bzrdir, _found=False):
1121
1205
        """Return an instance of this format for the bzrdir a_bzrdir.
1122
1206
        
1141
1225
class PreSplitOutRepositoryFormat(RepositoryFormat):
1142
1226
    """Base class for the pre split out repository formats."""
1143
1227
 
 
1228
    rich_root_data = False
 
1229
 
1144
1230
    def initialize(self, a_bzrdir, shared=False, _internal=False):
1145
1231
        """Create a weave repository.
1146
1232
        
1147
1233
        TODO: when creating split out bzr branch formats, move this to a common
1148
1234
        base for Format5, Format6. or something like that.
1149
1235
        """
1150
 
        from bzrlib.weavefile import write_weave_v5
1151
 
        from bzrlib.weave import Weave
1152
 
 
1153
1236
        if shared:
1154
1237
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
1155
1238
 
1159
1242
        
1160
1243
        # Create an empty weave
1161
1244
        sio = StringIO()
1162
 
        write_weave_v5(Weave(), sio)
 
1245
        weavefile.write_weave_v5(weave.Weave(), sio)
1163
1246
        empty_weave = sio.getvalue()
1164
1247
 
1165
1248
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1169
1252
        
1170
1253
        # FIXME: RBC 20060125 don't peek under the covers
1171
1254
        # NB: no need to escape relative paths that are url safe.
1172
 
        control_files = LockableFiles(a_bzrdir.transport, 'branch-lock',
1173
 
                                      TransportLock)
 
1255
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
 
1256
                                'branch-lock', lockable_files.TransportLock)
1174
1257
        control_files.create_lock()
1175
1258
        control_files.lock_write()
1176
1259
        control_files._transport.mkdir_multi(dirs,
1210
1293
                                  control_store=control_store,
1211
1294
                                  text_store=text_store)
1212
1295
 
 
1296
    def check_conversion_target(self, target_format):
 
1297
        pass
 
1298
 
1213
1299
 
1214
1300
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1215
1301
    """Bzr repository format 4.
1326
1412
class MetaDirRepositoryFormat(RepositoryFormat):
1327
1413
    """Common base class for the new repositories using the metadir layout."""
1328
1414
 
 
1415
    rich_root_data = False
 
1416
 
1329
1417
    def __init__(self):
1330
1418
        super(MetaDirRepositoryFormat, self).__init__()
1331
1419
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1335
1423
        # FIXME: RBC 20060125 don't peek under the covers
1336
1424
        # NB: no need to escape relative paths that are url safe.
1337
1425
        repository_transport = a_bzrdir.get_repository_transport(self)
1338
 
        control_files = LockableFiles(repository_transport, 'lock', LockDir)
 
1426
        control_files = lockable_files.LockableFiles(repository_transport,
 
1427
                                'lock', lockdir.LockDir)
1339
1428
        control_files.create_lock()
1340
1429
        return control_files
1341
1430
 
1383
1472
        """See RepositoryFormat.get_format_description()."""
1384
1473
        return "Weave repository format 7"
1385
1474
 
 
1475
    def check_conversion_target(self, target_format):
 
1476
        pass
 
1477
 
1386
1478
    def _get_revision_store(self, repo_transport, control_files):
1387
1479
        """See RepositoryFormat._get_revision_store()."""
1388
1480
        return self._get_text_rev_store(repo_transport,
1404
1496
        :param shared: If true the repository will be initialized as a shared
1405
1497
                       repository.
1406
1498
        """
1407
 
        from bzrlib.weavefile import write_weave_v5
1408
 
        from bzrlib.weave import Weave
1409
 
 
1410
1499
        # Create an empty weave
1411
1500
        sio = StringIO()
1412
 
        write_weave_v5(Weave(), sio)
 
1501
        weavefile.write_weave_v5(weave.Weave(), sio)
1413
1502
        empty_weave = sio.getvalue()
1414
1503
 
1415
1504
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1435
1524
            repo_transport = _override_transport
1436
1525
        else:
1437
1526
            repo_transport = a_bzrdir.get_repository_transport(None)
1438
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1527
        control_files = lockable_files.LockableFiles(repo_transport,
 
1528
                                'lock', lockdir.LockDir)
1439
1529
        text_store = self._get_text_store(repo_transport, control_files)
1440
1530
        control_store = self._get_control_store(repo_transport, control_files)
1441
1531
        _revision_store = self._get_revision_store(repo_transport, control_files)
1447
1537
                                 text_store=text_store)
1448
1538
 
1449
1539
 
1450
 
class RepositoryFormatKnit1(MetaDirRepositoryFormat):
1451
 
    """Bzr repository knit format 1.
 
1540
class RepositoryFormatKnit(MetaDirRepositoryFormat):
 
1541
    """Bzr repository knit format (generalized). 
1452
1542
 
1453
1543
    This repository format has:
1454
1544
     - knits for file texts and inventory
1459
1549
     - an optional 'shared-storage' flag
1460
1550
     - an optional 'no-working-trees' flag
1461
1551
     - a LockDir lock
1462
 
 
1463
 
    This format was introduced in bzr 0.8.
1464
1552
    """
1465
1553
 
1466
1554
    def _get_control_store(self, repo_transport, control_files):
1469
1557
            repo_transport,
1470
1558
            prefixed=False,
1471
1559
            file_mode=control_files._file_mode,
1472
 
            versionedfile_class=KnitVersionedFile,
1473
 
            versionedfile_kwargs={'factory':KnitPlainFactory()},
 
1560
            versionedfile_class=knit.KnitVersionedFile,
 
1561
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1474
1562
            )
1475
1563
 
1476
 
    def get_format_string(self):
1477
 
        """See RepositoryFormat.get_format_string()."""
1478
 
        return "Bazaar-NG Knit Repository Format 1"
1479
 
 
1480
 
    def get_format_description(self):
1481
 
        """See RepositoryFormat.get_format_description()."""
1482
 
        return "Knit repository format 1"
1483
 
 
1484
1564
    def _get_revision_store(self, repo_transport, control_files):
1485
1565
        """See RepositoryFormat._get_revision_store()."""
1486
1566
        from bzrlib.store.revision.knit import KnitRevisionStore
1489
1569
            file_mode=control_files._file_mode,
1490
1570
            prefixed=False,
1491
1571
            precious=True,
1492
 
            versionedfile_class=KnitVersionedFile,
1493
 
            versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory()},
 
1572
            versionedfile_class=knit.KnitVersionedFile,
 
1573
            versionedfile_kwargs={'delta':False,
 
1574
                                  'factory':knit.KnitPlainFactory(),
 
1575
                                 },
1494
1576
            escaped=True,
1495
1577
            )
1496
1578
        return KnitRevisionStore(versioned_file_store)
1498
1580
    def _get_text_store(self, transport, control_files):
1499
1581
        """See RepositoryFormat._get_text_store()."""
1500
1582
        return self._get_versioned_file_store('knits',
1501
 
                                              transport,
1502
 
                                              control_files,
1503
 
                                              versionedfile_class=KnitVersionedFile,
1504
 
                                              escaped=True)
 
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)
1505
1592
 
1506
1593
    def initialize(self, a_bzrdir, shared=False):
1507
1594
        """Create a knit format 1 repository.
1518
1605
        
1519
1606
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1520
1607
        repo_transport = a_bzrdir.get_repository_transport(None)
1521
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1608
        control_files = lockable_files.LockableFiles(repo_transport,
 
1609
                                'lock', lockdir.LockDir)
1522
1610
        control_store = self._get_control_store(repo_transport, control_files)
1523
1611
        transaction = transactions.WriteTransaction()
1524
1612
        # trigger a write of the inventory store.
1542
1630
            repo_transport = _override_transport
1543
1631
        else:
1544
1632
            repo_transport = a_bzrdir.get_repository_transport(None)
1545
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1633
        control_files = lockable_files.LockableFiles(repo_transport,
 
1634
                                'lock', lockdir.LockDir)
1546
1635
        text_store = self._get_text_store(repo_transport, control_files)
1547
1636
        control_store = self._get_control_store(repo_transport, control_files)
1548
1637
        _revision_store = self._get_revision_store(repo_transport, control_files)
1554
1643
                              text_store=text_store)
1555
1644
 
1556
1645
 
 
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
 
1557
1733
# formats which have no format string are not discoverable
1558
1734
# and not independently creatable, so are not registered.
1559
1735
RepositoryFormat.register_format(RepositoryFormat7())
1560
1736
_default_format = RepositoryFormatKnit1()
1561
1737
RepositoryFormat.register_format(_default_format)
 
1738
RepositoryFormat.register_format(RepositoryFormatKnit2())
1562
1739
RepositoryFormat.set_default_format(_default_format)
1563
1740
_legacy_formats = [RepositoryFormat4(),
1564
1741
                   RepositoryFormat5(),
1577
1754
    InterRepository.get(other).method_name(parameters).
1578
1755
    """
1579
1756
 
1580
 
    _optimisers = set()
 
1757
    _optimisers = []
1581
1758
    """The available optimised InterRepository types."""
1582
1759
 
1583
 
    @needs_write_lock
1584
1760
    def copy_content(self, revision_id=None, basis=None):
1585
 
        """Make a complete copy of the content in self into destination.
1586
 
        
1587
 
        This is a destructive operation! Do not use it on existing 
1588
 
        repositories.
1589
 
 
1590
 
        :param revision_id: Only copy the content needed to construct
1591
 
                            revision_id and its parents.
1592
 
        :param basis: Copy the needed data preferentially from basis.
1593
 
        """
1594
 
        try:
1595
 
            self.target.set_make_working_trees(self.source.make_working_trees())
1596
 
        except NotImplementedError:
1597
 
            pass
1598
 
        # grab the basis available data
1599
 
        if basis is not None:
1600
 
            self.target.fetch(basis, revision_id=revision_id)
1601
 
        # but don't bother fetching if we have the needed data now.
1602
 
        if (revision_id not in (None, NULL_REVISION) and 
1603
 
            self.target.has_revision(revision_id)):
1604
 
            return
1605
 
        self.target.fetch(self.source, revision_id=revision_id)
1606
 
 
1607
 
    @needs_write_lock
 
1761
        raise NotImplementedError(self.copy_content)
 
1762
 
1608
1763
    def fetch(self, revision_id=None, pb=None):
1609
1764
        """Fetch the content required to construct revision_id.
1610
1765
 
1611
 
        The content is copied from source to target.
 
1766
        The content is copied from self.source to self.target.
1612
1767
 
1613
1768
        :param revision_id: if None all content is copied, if NULL_REVISION no
1614
1769
                            content is copied.
1618
1773
        Returns the copied revision count and the failed revisions in a tuple:
1619
1774
        (copied, failures).
1620
1775
        """
1621
 
        from bzrlib.fetch import GenericRepoFetcher
1622
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1623
 
               self.source, self.source._format, self.target, self.target._format)
1624
 
        f = GenericRepoFetcher(to_repository=self.target,
1625
 
                               from_repository=self.source,
1626
 
                               last_revision=revision_id,
1627
 
                               pb=pb)
1628
 
        return f.count_copied, f.failed_revisions
1629
 
 
 
1776
        raise NotImplementedError(self.fetch)
 
1777
   
1630
1778
    @needs_read_lock
1631
1779
    def missing_revision_ids(self, revision_id=None):
1632
1780
        """Return the revision ids that source has that target does not.
1640
1788
        target_ids = set(self.target.all_revision_ids())
1641
1789
        if revision_id is not None:
1642
1790
            source_ids = self.source.get_ancestry(revision_id)
1643
 
            assert source_ids[0] == None
 
1791
            assert source_ids[0] is None
1644
1792
            source_ids.pop(0)
1645
1793
        else:
1646
1794
            source_ids = self.source.all_revision_ids()
1651
1799
        return [rev_id for rev_id in source_ids if rev_id in result_set]
1652
1800
 
1653
1801
 
1654
 
class InterWeaveRepo(InterRepository):
 
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):
1655
1861
    """Optimised code paths between Weave based repositories."""
1656
1862
 
1657
1863
    _matching_repo_format = RepositoryFormat7()
1743
1949
        # - RBC 20060209
1744
1950
        if revision_id is not None:
1745
1951
            source_ids = self.source.get_ancestry(revision_id)
1746
 
            assert source_ids[0] == None
 
1952
            assert source_ids[0] is None
1747
1953
            source_ids.pop(0)
1748
1954
        else:
1749
1955
            source_ids = self.source._all_possible_ids()
1769
1975
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1770
1976
 
1771
1977
 
1772
 
class InterKnitRepo(InterRepository):
 
1978
class InterKnitRepo(InterSameDataRepository):
1773
1979
    """Optimised code paths between Knit based repositories."""
1774
1980
 
1775
1981
    _matching_repo_format = RepositoryFormatKnit1()
1806
2012
        """See InterRepository.missing_revision_ids()."""
1807
2013
        if revision_id is not None:
1808
2014
            source_ids = self.source.get_ancestry(revision_id)
1809
 
            assert source_ids[0] == None
 
2015
            assert source_ids[0] is None
1810
2016
            source_ids.pop(0)
1811
2017
        else:
1812
2018
            source_ids = self.source._all_possible_ids()
1831
2037
            # that against the revision records.
1832
2038
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1833
2039
 
 
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)
1834
2119
InterRepository.register_optimiser(InterWeaveRepo)
1835
2120
InterRepository.register_optimiser(InterKnitRepo)
 
2121
InterRepository.register_optimiser(InterModel1and2)
 
2122
InterRepository.register_optimiser(InterKnit1and2)
1836
2123
 
1837
2124
 
1838
2125
class RepositoryTestProviderAdapter(object):
1850
2137
        self._formats = formats
1851
2138
    
1852
2139
    def adapt(self, test):
1853
 
        result = TestSuite()
 
2140
        result = unittest.TestSuite()
1854
2141
        for repository_format, bzrdir_format in self._formats:
1855
2142
            new_test = deepcopy(test)
1856
2143
            new_test.transport_server = self._transport_server
1880
2167
        self._formats = formats
1881
2168
    
1882
2169
    def adapt(self, test):
1883
 
        result = TestSuite()
 
2170
        result = unittest.TestSuite()
1884
2171
        for interrepo_class, repository_format, repository_format_to in self._formats:
1885
2172
            new_test = deepcopy(test)
1886
2173
            new_test.transport_server = self._transport_server
1903
2190
        # default format.
1904
2191
        # XXX: robertc 20060220 reinstate this when there are two supported
1905
2192
        # formats which do not have an optimal code path between them.
1906
 
        result.append((InterRepository,
1907
 
                       RepositoryFormat6(),
1908
 
                       RepositoryFormatKnit1()))
 
2193
        #result.append((InterRepository,
 
2194
        #               RepositoryFormat6(),
 
2195
        #               RepositoryFormatKnit1()))
1909
2196
        for optimiser in InterRepository._optimisers:
1910
 
            result.append((optimiser,
1911
 
                           optimiser._matching_repo_format,
1912
 
                           optimiser._matching_repo_format
1913
 
                           ))
 
2197
            if optimiser._matching_repo_format is not None:
 
2198
                result.append((optimiser,
 
2199
                               optimiser._matching_repo_format,
 
2200
                               optimiser._matching_repo_format
 
2201
                               ))
1914
2202
        # if there are specific combinations we want to use, we can add them 
1915
2203
        # here.
 
2204
        result.append((InterModel1and2, RepositoryFormat5(),
 
2205
                       RepositoryFormatKnit2()))
 
2206
        result.append((InterKnit1and2, RepositoryFormatKnit1(),
 
2207
                       RepositoryFormatKnit2()))
1916
2208
        return result
1917
2209
 
1918
2210
 
1945
2237
        self.step('Moving repository to repository.backup')
1946
2238
        self.repo_dir.transport.move('repository', 'repository.backup')
1947
2239
        backup_transport =  self.repo_dir.transport.clone('repository.backup')
 
2240
        repo._format.check_conversion_target(self.target_format)
1948
2241
        self.source_repo = repo._format.open(self.repo_dir,
1949
2242
            _found=True,
1950
2243
            _override_transport=backup_transport)
2023
2316
 
2024
2317
        :return: The revision id of the recorded revision.
2025
2318
        """
2026
 
        rev = Revision(timestamp=self._timestamp,
 
2319
        rev = _mod_revision.Revision(
 
2320
                       timestamp=self._timestamp,
2027
2321
                       timezone=self._timezone,
2028
2322
                       committer=self._committer,
2029
2323
                       message=message,
2035
2329
            self.new_inventory, self._config)
2036
2330
        return self._new_revision_id
2037
2331
 
 
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
 
2038
2344
    def finish_inventory(self):
2039
2345
        """Tell the builder that the inventory is finished."""
2040
2346
        if self.new_inventory.root is None:
2096
2402
 
2097
2403
        # In this revision format, root entries have no knit or weave
2098
2404
        if ie is self.new_inventory.root:
2099
 
            if len(parent_invs):
2100
 
                ie.revision = parent_invs[0].root.revision
2101
 
            else:
2102
 
                ie.revision = None
 
2405
            # When serializing out to disk and back in
 
2406
            # root.revision is always _new_revision_id
 
2407
            ie.revision = self._new_revision_id
2103
2408
            return
2104
2409
        previous_entries = ie.find_previous_heads(
2105
2410
            parent_invs,
2175
2480
    record_root_entry = True
2176
2481
 
2177
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
 
2178
2518
_unescape_map = {
2179
2519
    'apos':"'",
2180
2520
    'quot':'"',