~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: John Arbash Meinel
  • Date: 2006-10-06 07:25:55 UTC
  • mto: This revision was merged to the branch mainline in revision 2071.
  • Revision ID: john@arbash-meinel.com-20061006072555-b41c9a6f481fd1d6
Make bzrlib.config use lazy importing

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
 
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
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
 
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
36
 
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
37
 
from bzrlib.store.text import TextStore
38
 
from bzrlib.symbol_versioning import (deprecated_method,
39
 
        zero_nine, 
 
60
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
 
61
from bzrlib.symbol_versioning import (
 
62
        deprecated_method,
 
63
        zero_nine,
40
64
        )
41
 
from bzrlib.trace import mutter, note
42
 
from bzrlib.tree import RevisionTree, EmptyTree
43
 
from bzrlib.tsort import topo_sort
44
 
from bzrlib.testament import Testament
45
 
from bzrlib.tree import EmptyTree
46
 
from bzrlib.weave import WeaveFile
 
65
from bzrlib.trace import mutter, note, warning
 
66
 
 
67
 
 
68
# Old formats display a warning, but only once
 
69
_deprecation_warning_done = False
47
70
 
48
71
 
49
72
class Repository(object):
70
93
        assert inv.revision_id is None or inv.revision_id == revid, \
71
94
            "Mismatch between inventory revision" \
72
95
            " id and insertion revid (%r, %r)" % (inv.revision_id, revid)
73
 
        inv_text = xml5.serializer_v5.write_inventory_to_string(inv)
 
96
        assert inv.root is not None
 
97
        inv_text = self.serialise_inventory(inv)
74
98
        inv_sha1 = osutils.sha_string(inv_text)
75
99
        inv_vf = self.control_weaves.get_weave('inventory',
76
100
                                               self.get_transaction())
189
213
        self.control_weaves = control_store
190
214
        # TODO: make sure to construct the right store classes, etc, depending
191
215
        # on whether escaping is required.
 
216
        self._warn_if_deprecated()
 
217
        self._serializer = xml5.serializer_v5
192
218
 
193
219
    def __repr__(self):
194
220
        return '%s(%r)' % (self.__class__.__name__, 
256
282
        :param revprops: Optional dictionary of revision properties.
257
283
        :param revision_id: Optional revision id.
258
284
        """
259
 
        return CommitBuilder(self, parents, config, timestamp, timezone,
260
 
                             committer, revprops, revision_id)
 
285
        return _CommitBuilder(self, parents, config, timestamp, timezone,
 
286
                              committer, revprops, revision_id)
261
287
 
262
288
    def unlock(self):
263
289
        self.control_files.unlock()
299
325
        or testing the revision graph.
300
326
        """
301
327
        if not revision_id or not isinstance(revision_id, basestring):
302
 
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
 
328
            raise errors.InvalidRevisionId(revision_id=revision_id,
 
329
                                           branch=self)
303
330
        return self._revision_store.get_revisions([revision_id],
304
331
                                                  self.get_transaction())[0]
305
332
    @needs_read_lock
347
374
                     t in self.revision_trees(required_trees))
348
375
        for revision in revisions:
349
376
            if not revision.parent_ids:
350
 
                old_tree = EmptyTree()
 
377
                old_tree = self.revision_tree(None)
351
378
            else:
352
379
                old_tree = trees[revision.parent_ids[0]]
353
 
            yield delta.compare_trees(old_tree, trees[revision.revision_id])
 
380
            yield trees[revision.revision_id].changes_from(old_tree)
354
381
 
355
382
    @needs_read_lock
356
383
    def get_revision_delta(self, revision_id):
393
420
        revision_ids. Each altered file-ids has the exact revision_ids that
394
421
        altered it listed explicitly.
395
422
        """
396
 
        assert isinstance(self._format, (RepositoryFormat5,
397
 
                                         RepositoryFormat6,
398
 
                                         RepositoryFormat7,
399
 
                                         RepositoryFormatKnit1)), \
 
423
        assert self._serializer.support_altered_by_hack, \
400
424
            ("fileids_altered_by_revision_ids only supported for branches " 
401
425
             "which store inventory as unnested xml, not on %r" % self)
402
426
        selected_revision_ids = set(revision_ids)
411
435
        # revisions. We don't need to see all lines in the inventory because
412
436
        # only those added in an inventory in rev X can contain a revision=X
413
437
        # line.
414
 
        for line in w.iter_lines_added_or_present_in_versions(selected_revision_ids):
415
 
            start = line.find('file_id="')+9
416
 
            if start < 9: continue
417
 
            end = line.find('"', start)
418
 
            assert end>= 0
419
 
            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])
420
447
 
421
 
            start = line.find('revision="')+10
422
 
            if start < 10: continue
423
 
            end = line.find('"', start)
424
 
            assert end>= 0
425
 
            revision_id = _unescape_xml(line[start:end])
426
 
            if revision_id in selected_revision_ids:
427
 
                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()
428
457
        return result
429
458
 
430
459
    @needs_read_lock
444
473
        :param revision_id: The expected revision id of the inventory.
445
474
        :param xml: A serialised inventory.
446
475
        """
447
 
        return xml5.serializer_v5.read_inventory_from_string(xml)
 
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)
448
482
 
449
483
    @needs_read_lock
450
484
    def get_inventory_xml(self, revision_id):
472
506
        :return: a dictionary of revision_id->revision_parents_list.
473
507
        """
474
508
        # special case NULL_REVISION
475
 
        if revision_id == NULL_REVISION:
 
509
        if revision_id == _mod_revision.NULL_REVISION:
476
510
            return {}
477
 
        weave = self.get_inventory_weave()
478
 
        all_revisions = self._eliminate_revisions_not_present(weave.versions())
479
 
        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 
480
515
                             node in all_revisions])
481
516
        if revision_id is None:
482
517
            return entire_graph
501
536
        :param revision_ids: an iterable of revisions to graph or None for all.
502
537
        :return: a Graph object with the graph reachable from revision_ids.
503
538
        """
504
 
        result = Graph()
 
539
        result = graph.Graph()
505
540
        if not revision_ids:
506
541
            pending = set(self.all_revision_ids())
507
542
            required = set([])
508
543
        else:
509
544
            pending = set(revision_ids)
510
545
            # special case NULL_REVISION
511
 
            if NULL_REVISION in pending:
512
 
                pending.remove(NULL_REVISION)
 
546
            if _mod_revision.NULL_REVISION in pending:
 
547
                pending.remove(_mod_revision.NULL_REVISION)
513
548
            required = set(pending)
514
549
        done = set([])
515
550
        while len(pending):
565
600
    def revision_tree(self, revision_id):
566
601
        """Return Tree for a revision on this branch.
567
602
 
568
 
        `revision_id` may be None for the null revision, in which case
569
 
        an `EmptyTree` is returned."""
 
603
        `revision_id` may be None for the empty tree revision.
 
604
        """
570
605
        # TODO: refactor this to use an existing revision object
571
606
        # so we don't need to read it in twice.
572
 
        if revision_id is None or revision_id == NULL_REVISION:
573
 
            return EmptyTree()
 
607
        if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
 
608
            return RevisionTree(self, Inventory(), _mod_revision.NULL_REVISION)
574
609
        else:
575
610
            inv = self.get_revision_inventory(revision_id)
576
611
            return RevisionTree(self, inv, revision_id)
581
616
 
582
617
        `revision_id` may not be None or 'null:'"""
583
618
        assert None not in revision_ids
584
 
        assert NULL_REVISION not in revision_ids
 
619
        assert _mod_revision.NULL_REVISION not in revision_ids
585
620
        texts = self.get_inventory_weave().get_texts(revision_ids)
586
621
        for text, revision_id in zip(texts, revision_ids):
587
622
            inv = self.deserialise_inventory(revision_id, text)
681
716
        result.check()
682
717
        return result
683
718
 
 
719
    def _warn_if_deprecated(self):
 
720
        global _deprecation_warning_done
 
721
        if _deprecation_warning_done:
 
722
            return
 
723
        _deprecation_warning_done = True
 
724
        warning("Format %s for %s is deprecated - please use 'bzr upgrade' to get better performance"
 
725
                % (self._format, self.bzrdir.transport.base))
 
726
 
 
727
    def supports_rich_root(self):
 
728
        return self._format.rich_root_data
 
729
 
684
730
 
685
731
class AllInOneRepository(Repository):
686
732
    """Legacy support - the repository behaviour for all-in-one branches."""
746
792
            present_parents.append(p_id)
747
793
            parent_trees[p_id] = repository.revision_tree(p_id)
748
794
        else:
749
 
            parent_trees[p_id] = EmptyTree()
 
795
            parent_trees[p_id] = repository.revision_tree(None)
750
796
 
751
797
    inv = revision_tree.inventory
752
 
    
 
798
    entries = inv.iter_entries()
 
799
    # backwards compatability hack: skip the root id.
 
800
    if not repository.supports_rich_root():
 
801
        path, root = entries.next()
 
802
        if root.revision != rev.revision_id:
 
803
            raise errors.IncompatibleRevision(repr(repository))
753
804
    # Add the texts that are not already present
754
 
    for path, ie in inv.iter_entries():
 
805
    for path, ie in entries:
755
806
        w = repository.weave_store.get_weave_or_empty(ie.file_id,
756
807
                repository.get_transaction())
757
808
        if ie.revision not in w:
790
841
                                                _revision_store,
791
842
                                                control_store,
792
843
                                                text_store)
793
 
 
794
844
        dir_mode = self.control_files._dir_mode
795
845
        file_mode = self.control_files._file_mode
796
846
 
825
875
class KnitRepository(MetaDirRepository):
826
876
    """Knit format repository."""
827
877
 
 
878
    def _warn_if_deprecated(self):
 
879
        # This class isn't deprecated
 
880
        pass
 
881
 
828
882
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
829
883
        inv_vf.add_lines_with_ghosts(revid, parents, lines)
830
884
 
889
943
        :return: a dictionary of revision_id->revision_parents_list.
890
944
        """
891
945
        # special case NULL_REVISION
892
 
        if revision_id == NULL_REVISION:
 
946
        if revision_id == _mod_revision.NULL_REVISION:
893
947
            return {}
894
 
        weave = self._get_revision_vf()
895
 
        entire_graph = weave.get_graph()
 
948
        a_weave = self._get_revision_vf()
 
949
        entire_graph = a_weave.get_graph()
896
950
        if revision_id is None:
897
 
            return weave.get_graph()
898
 
        elif revision_id not in weave:
 
951
            return a_weave.get_graph()
 
952
        elif revision_id not in a_weave:
899
953
            raise errors.NoSuchRevision(self, revision_id)
900
954
        else:
901
955
            # add what can be reached from revision_id
903
957
            pending = set([revision_id])
904
958
            while len(pending) > 0:
905
959
                node = pending.pop()
906
 
                result[node] = weave.get_parents(node)
 
960
                result[node] = a_weave.get_parents(node)
907
961
                for revision_id in result[node]:
908
962
                    if revision_id not in result:
909
963
                        pending.add(revision_id)
916
970
        :param revision_ids: an iterable of revisions to graph or None for all.
917
971
        :return: a Graph object with the graph reachable from revision_ids.
918
972
        """
919
 
        result = Graph()
 
973
        result = graph.Graph()
920
974
        vf = self._get_revision_vf()
921
975
        versions = set(vf.versions())
922
976
        if not revision_ids:
925
979
        else:
926
980
            pending = set(revision_ids)
927
981
            # special case NULL_REVISION
928
 
            if NULL_REVISION in pending:
929
 
                pending.remove(NULL_REVISION)
 
982
            if _mod_revision.NULL_REVISION in pending:
 
983
                pending.remove(_mod_revision.NULL_REVISION)
930
984
            required = set(pending)
931
985
        done = set([])
932
986
        while len(pending):
967
1021
        return self._get_revision_vf().get_parents(revision_id)
968
1022
 
969
1023
 
 
1024
class KnitRepository2(KnitRepository):
 
1025
    """"""
 
1026
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
 
1027
                 control_store, text_store):
 
1028
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
 
1029
                              _revision_store, control_store, text_store)
 
1030
        self._serializer = xml6.serializer_v6
 
1031
 
 
1032
    def deserialise_inventory(self, revision_id, xml):
 
1033
        """Transform the xml into an inventory object. 
 
1034
 
 
1035
        :param revision_id: The expected revision id of the inventory.
 
1036
        :param xml: A serialised inventory.
 
1037
        """
 
1038
        result = self._serializer.read_inventory_from_string(xml)
 
1039
        assert result.root.revision is not None
 
1040
        return result
 
1041
 
 
1042
    def serialise_inventory(self, inv):
 
1043
        """Transform the inventory object into XML text.
 
1044
 
 
1045
        :param revision_id: The expected revision id of the inventory.
 
1046
        :param xml: A serialised inventory.
 
1047
        """
 
1048
        assert inv.revision_id is not None
 
1049
        assert inv.root.revision is not None
 
1050
        return KnitRepository.serialise_inventory(self, inv)
 
1051
 
 
1052
    def get_commit_builder(self, branch, parents, config, timestamp=None, 
 
1053
                           timezone=None, committer=None, revprops=None, 
 
1054
                           revision_id=None):
 
1055
        """Obtain a CommitBuilder for this repository.
 
1056
        
 
1057
        :param branch: Branch to commit to.
 
1058
        :param parents: Revision ids of the parents of the new revision.
 
1059
        :param config: Configuration to use.
 
1060
        :param timestamp: Optional timestamp recorded for commit.
 
1061
        :param timezone: Optional timezone for timestamp.
 
1062
        :param committer: Optional committer to set for commit.
 
1063
        :param revprops: Optional dictionary of revision properties.
 
1064
        :param revision_id: Optional revision id.
 
1065
        """
 
1066
        return RootCommitBuilder(self, parents, config, timestamp, timezone,
 
1067
                                 committer, revprops, revision_id)
 
1068
 
 
1069
 
970
1070
class RepositoryFormat(object):
971
1071
    """A repository format.
972
1072
 
997
1097
    _formats = {}
998
1098
    """The known formats."""
999
1099
 
 
1100
    def __str__(self):
 
1101
        return "<%s>" % self.__class__.__name__
 
1102
 
1000
1103
    @classmethod
1001
1104
    def find_format(klass, a_bzrdir):
1002
1105
        """Return the format for the repository object in a_bzrdir."""
1062
1165
                                  transport,
1063
1166
                                  control_files,
1064
1167
                                  prefixed=True,
1065
 
                                  versionedfile_class=WeaveFile,
 
1168
                                  versionedfile_class=weave.WeaveFile,
 
1169
                                  versionedfile_kwargs={},
1066
1170
                                  escaped=False):
1067
1171
        weave_transport = control_files._transport.clone(name)
1068
1172
        dir_mode = control_files._dir_mode
1071
1175
                                  dir_mode=dir_mode,
1072
1176
                                  file_mode=file_mode,
1073
1177
                                  versionedfile_class=versionedfile_class,
 
1178
                                  versionedfile_kwargs=versionedfile_kwargs,
1074
1179
                                  escaped=escaped)
1075
1180
 
1076
1181
    def initialize(self, a_bzrdir, shared=False):
1092
1197
        """
1093
1198
        return True
1094
1199
 
 
1200
    def check_conversion_target(self, target_format):
 
1201
        raise NotImplementedError(self.check_conversion_target)
 
1202
 
1095
1203
    def open(self, a_bzrdir, _found=False):
1096
1204
        """Return an instance of this format for the bzrdir a_bzrdir.
1097
1205
        
1116
1224
class PreSplitOutRepositoryFormat(RepositoryFormat):
1117
1225
    """Base class for the pre split out repository formats."""
1118
1226
 
 
1227
    rich_root_data = False
 
1228
 
1119
1229
    def initialize(self, a_bzrdir, shared=False, _internal=False):
1120
1230
        """Create a weave repository.
1121
1231
        
1122
1232
        TODO: when creating split out bzr branch formats, move this to a common
1123
1233
        base for Format5, Format6. or something like that.
1124
1234
        """
1125
 
        from bzrlib.weavefile import write_weave_v5
1126
 
        from bzrlib.weave import Weave
1127
 
 
1128
1235
        if shared:
1129
1236
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
1130
1237
 
1134
1241
        
1135
1242
        # Create an empty weave
1136
1243
        sio = StringIO()
1137
 
        write_weave_v5(Weave(), sio)
 
1244
        weavefile.write_weave_v5(weave.Weave(), sio)
1138
1245
        empty_weave = sio.getvalue()
1139
1246
 
1140
1247
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1144
1251
        
1145
1252
        # FIXME: RBC 20060125 don't peek under the covers
1146
1253
        # NB: no need to escape relative paths that are url safe.
1147
 
        control_files = LockableFiles(a_bzrdir.transport, 'branch-lock',
1148
 
                                      TransportLock)
 
1254
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
 
1255
                                'branch-lock', lockable_files.TransportLock)
1149
1256
        control_files.create_lock()
1150
1257
        control_files.lock_write()
1151
1258
        control_files._transport.mkdir_multi(dirs,
1185
1292
                                  control_store=control_store,
1186
1293
                                  text_store=text_store)
1187
1294
 
 
1295
    def check_conversion_target(self, target_format):
 
1296
        pass
 
1297
 
1188
1298
 
1189
1299
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1190
1300
    """Bzr repository format 4.
1301
1411
class MetaDirRepositoryFormat(RepositoryFormat):
1302
1412
    """Common base class for the new repositories using the metadir layout."""
1303
1413
 
 
1414
    rich_root_data = False
 
1415
 
1304
1416
    def __init__(self):
1305
1417
        super(MetaDirRepositoryFormat, self).__init__()
1306
1418
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1310
1422
        # FIXME: RBC 20060125 don't peek under the covers
1311
1423
        # NB: no need to escape relative paths that are url safe.
1312
1424
        repository_transport = a_bzrdir.get_repository_transport(self)
1313
 
        control_files = LockableFiles(repository_transport, 'lock', LockDir)
 
1425
        control_files = lockable_files.LockableFiles(repository_transport,
 
1426
                                'lock', lockdir.LockDir)
1314
1427
        control_files.create_lock()
1315
1428
        return control_files
1316
1429
 
1358
1471
        """See RepositoryFormat.get_format_description()."""
1359
1472
        return "Weave repository format 7"
1360
1473
 
 
1474
    def check_conversion_target(self, target_format):
 
1475
        pass
 
1476
 
1361
1477
    def _get_revision_store(self, repo_transport, control_files):
1362
1478
        """See RepositoryFormat._get_revision_store()."""
1363
1479
        return self._get_text_rev_store(repo_transport,
1379
1495
        :param shared: If true the repository will be initialized as a shared
1380
1496
                       repository.
1381
1497
        """
1382
 
        from bzrlib.weavefile import write_weave_v5
1383
 
        from bzrlib.weave import Weave
1384
 
 
1385
1498
        # Create an empty weave
1386
1499
        sio = StringIO()
1387
 
        write_weave_v5(Weave(), sio)
 
1500
        weavefile.write_weave_v5(weave.Weave(), sio)
1388
1501
        empty_weave = sio.getvalue()
1389
1502
 
1390
1503
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1410
1523
            repo_transport = _override_transport
1411
1524
        else:
1412
1525
            repo_transport = a_bzrdir.get_repository_transport(None)
1413
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1526
        control_files = lockable_files.LockableFiles(repo_transport,
 
1527
                                'lock', lockdir.LockDir)
1414
1528
        text_store = self._get_text_store(repo_transport, control_files)
1415
1529
        control_store = self._get_control_store(repo_transport, control_files)
1416
1530
        _revision_store = self._get_revision_store(repo_transport, control_files)
1422
1536
                                 text_store=text_store)
1423
1537
 
1424
1538
 
1425
 
class RepositoryFormatKnit1(MetaDirRepositoryFormat):
1426
 
    """Bzr repository knit format 1.
 
1539
class RepositoryFormatKnit(MetaDirRepositoryFormat):
 
1540
    """Bzr repository knit format (generalized). 
1427
1541
 
1428
1542
    This repository format has:
1429
1543
     - knits for file texts and inventory
1434
1548
     - an optional 'shared-storage' flag
1435
1549
     - an optional 'no-working-trees' flag
1436
1550
     - a LockDir lock
1437
 
 
1438
 
    This format was introduced in bzr 0.8.
1439
1551
    """
1440
1552
 
1441
1553
    def _get_control_store(self, repo_transport, control_files):
1444
1556
            repo_transport,
1445
1557
            prefixed=False,
1446
1558
            file_mode=control_files._file_mode,
1447
 
            versionedfile_class=KnitVersionedFile,
1448
 
            versionedfile_kwargs={'factory':KnitPlainFactory()},
 
1559
            versionedfile_class=knit.KnitVersionedFile,
 
1560
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1449
1561
            )
1450
1562
 
1451
 
    def get_format_string(self):
1452
 
        """See RepositoryFormat.get_format_string()."""
1453
 
        return "Bazaar-NG Knit Repository Format 1"
1454
 
 
1455
 
    def get_format_description(self):
1456
 
        """See RepositoryFormat.get_format_description()."""
1457
 
        return "Knit repository format 1"
1458
 
 
1459
1563
    def _get_revision_store(self, repo_transport, control_files):
1460
1564
        """See RepositoryFormat._get_revision_store()."""
1461
1565
        from bzrlib.store.revision.knit import KnitRevisionStore
1464
1568
            file_mode=control_files._file_mode,
1465
1569
            prefixed=False,
1466
1570
            precious=True,
1467
 
            versionedfile_class=KnitVersionedFile,
1468
 
            versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory()},
 
1571
            versionedfile_class=knit.KnitVersionedFile,
 
1572
            versionedfile_kwargs={'delta':False,
 
1573
                                  'factory':knit.KnitPlainFactory(),
 
1574
                                 },
1469
1575
            escaped=True,
1470
1576
            )
1471
1577
        return KnitRevisionStore(versioned_file_store)
1473
1579
    def _get_text_store(self, transport, control_files):
1474
1580
        """See RepositoryFormat._get_text_store()."""
1475
1581
        return self._get_versioned_file_store('knits',
1476
 
                                              transport,
1477
 
                                              control_files,
1478
 
                                              versionedfile_class=KnitVersionedFile,
1479
 
                                              escaped=True)
 
1582
                                  transport,
 
1583
                                  control_files,
 
1584
                                  versionedfile_class=knit.KnitVersionedFile,
 
1585
                                  versionedfile_kwargs={
 
1586
                                      'create_parent_dir':True,
 
1587
                                      'delay_create':True,
 
1588
                                      'dir_mode':control_files._dir_mode,
 
1589
                                  },
 
1590
                                  escaped=True)
1480
1591
 
1481
1592
    def initialize(self, a_bzrdir, shared=False):
1482
1593
        """Create a knit format 1 repository.
1493
1604
        
1494
1605
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1495
1606
        repo_transport = a_bzrdir.get_repository_transport(None)
1496
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1607
        control_files = lockable_files.LockableFiles(repo_transport,
 
1608
                                'lock', lockdir.LockDir)
1497
1609
        control_store = self._get_control_store(repo_transport, control_files)
1498
1610
        transaction = transactions.WriteTransaction()
1499
1611
        # trigger a write of the inventory store.
1517
1629
            repo_transport = _override_transport
1518
1630
        else:
1519
1631
            repo_transport = a_bzrdir.get_repository_transport(None)
1520
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1632
        control_files = lockable_files.LockableFiles(repo_transport,
 
1633
                                'lock', lockdir.LockDir)
1521
1634
        text_store = self._get_text_store(repo_transport, control_files)
1522
1635
        control_store = self._get_control_store(repo_transport, control_files)
1523
1636
        _revision_store = self._get_revision_store(repo_transport, control_files)
1529
1642
                              text_store=text_store)
1530
1643
 
1531
1644
 
 
1645
class RepositoryFormatKnit1(RepositoryFormatKnit):
 
1646
    """Bzr repository knit format 1.
 
1647
 
 
1648
    This repository format has:
 
1649
     - knits for file texts and inventory
 
1650
     - hash subdirectory based stores.
 
1651
     - knits for revisions and signatures
 
1652
     - TextStores for revisions and signatures.
 
1653
     - a format marker of its own
 
1654
     - an optional 'shared-storage' flag
 
1655
     - an optional 'no-working-trees' flag
 
1656
     - a LockDir lock
 
1657
 
 
1658
    This format was introduced in bzr 0.8.
 
1659
    """
 
1660
    def get_format_string(self):
 
1661
        """See RepositoryFormat.get_format_string()."""
 
1662
        return "Bazaar-NG Knit Repository Format 1"
 
1663
 
 
1664
    def get_format_description(self):
 
1665
        """See RepositoryFormat.get_format_description()."""
 
1666
        return "Knit repository format 1"
 
1667
 
 
1668
    def check_conversion_target(self, target_format):
 
1669
        pass
 
1670
 
 
1671
 
 
1672
class RepositoryFormatKnit2(RepositoryFormatKnit):
 
1673
    """Bzr repository knit format 2.
 
1674
 
 
1675
    THIS FORMAT IS EXPERIMENTAL
 
1676
    This repository format has:
 
1677
     - knits for file texts and inventory
 
1678
     - hash subdirectory based stores.
 
1679
     - knits for revisions and signatures
 
1680
     - TextStores for revisions and signatures.
 
1681
     - a format marker of its own
 
1682
     - an optional 'shared-storage' flag
 
1683
     - an optional 'no-working-trees' flag
 
1684
     - a LockDir lock
 
1685
     - Support for recording full info about the tree root
 
1686
 
 
1687
    """
 
1688
    
 
1689
    rich_root_data = True
 
1690
 
 
1691
    def get_format_string(self):
 
1692
        """See RepositoryFormat.get_format_string()."""
 
1693
        return "Bazaar Knit Repository Format 2\n"
 
1694
 
 
1695
    def get_format_description(self):
 
1696
        """See RepositoryFormat.get_format_description()."""
 
1697
        return "Knit repository format 2"
 
1698
 
 
1699
    def check_conversion_target(self, target_format):
 
1700
        if not target_format.rich_root_data:
 
1701
            raise errors.BadConversionTarget(
 
1702
                'Does not support rich root data.', target_format)
 
1703
 
 
1704
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
1705
        """See RepositoryFormat.open().
 
1706
        
 
1707
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
1708
                                    repository at a slightly different url
 
1709
                                    than normal. I.e. during 'upgrade'.
 
1710
        """
 
1711
        if not _found:
 
1712
            format = RepositoryFormat.find_format(a_bzrdir)
 
1713
            assert format.__class__ ==  self.__class__
 
1714
        if _override_transport is not None:
 
1715
            repo_transport = _override_transport
 
1716
        else:
 
1717
            repo_transport = a_bzrdir.get_repository_transport(None)
 
1718
        control_files = lockable_files.LockableFiles(repo_transport, 'lock',
 
1719
                                                     lockdir.LockDir)
 
1720
        text_store = self._get_text_store(repo_transport, control_files)
 
1721
        control_store = self._get_control_store(repo_transport, control_files)
 
1722
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
1723
        return KnitRepository2(_format=self,
 
1724
                               a_bzrdir=a_bzrdir,
 
1725
                               control_files=control_files,
 
1726
                               _revision_store=_revision_store,
 
1727
                               control_store=control_store,
 
1728
                               text_store=text_store)
 
1729
 
 
1730
 
 
1731
 
1532
1732
# formats which have no format string are not discoverable
1533
1733
# and not independently creatable, so are not registered.
1534
1734
RepositoryFormat.register_format(RepositoryFormat7())
1535
1735
_default_format = RepositoryFormatKnit1()
1536
1736
RepositoryFormat.register_format(_default_format)
 
1737
RepositoryFormat.register_format(RepositoryFormatKnit2())
1537
1738
RepositoryFormat.set_default_format(_default_format)
1538
1739
_legacy_formats = [RepositoryFormat4(),
1539
1740
                   RepositoryFormat5(),
1552
1753
    InterRepository.get(other).method_name(parameters).
1553
1754
    """
1554
1755
 
1555
 
    _optimisers = set()
 
1756
    _optimisers = []
1556
1757
    """The available optimised InterRepository types."""
1557
1758
 
1558
 
    @needs_write_lock
1559
1759
    def copy_content(self, revision_id=None, basis=None):
1560
 
        """Make a complete copy of the content in self into destination.
1561
 
        
1562
 
        This is a destructive operation! Do not use it on existing 
1563
 
        repositories.
1564
 
 
1565
 
        :param revision_id: Only copy the content needed to construct
1566
 
                            revision_id and its parents.
1567
 
        :param basis: Copy the needed data preferentially from basis.
1568
 
        """
1569
 
        try:
1570
 
            self.target.set_make_working_trees(self.source.make_working_trees())
1571
 
        except NotImplementedError:
1572
 
            pass
1573
 
        # grab the basis available data
1574
 
        if basis is not None:
1575
 
            self.target.fetch(basis, revision_id=revision_id)
1576
 
        # but don't bother fetching if we have the needed data now.
1577
 
        if (revision_id not in (None, NULL_REVISION) and 
1578
 
            self.target.has_revision(revision_id)):
1579
 
            return
1580
 
        self.target.fetch(self.source, revision_id=revision_id)
1581
 
 
1582
 
    def _double_lock(self, lock_source, lock_target):
1583
 
        """Take out too locks, rolling back the first if the second throws."""
1584
 
        lock_source()
1585
 
        try:
1586
 
            lock_target()
1587
 
        except Exception:
1588
 
            # we want to ensure that we don't leave source locked by mistake.
1589
 
            # and any error on target should not confuse source.
1590
 
            self.source.unlock()
1591
 
            raise
1592
 
 
1593
 
    @needs_write_lock
 
1760
        raise NotImplementedError(self.copy_content)
 
1761
 
1594
1762
    def fetch(self, revision_id=None, pb=None):
1595
1763
        """Fetch the content required to construct revision_id.
1596
1764
 
1597
 
        The content is copied from source to target.
 
1765
        The content is copied from self.source to self.target.
1598
1766
 
1599
1767
        :param revision_id: if None all content is copied, if NULL_REVISION no
1600
1768
                            content is copied.
1604
1772
        Returns the copied revision count and the failed revisions in a tuple:
1605
1773
        (copied, failures).
1606
1774
        """
1607
 
        from bzrlib.fetch import GenericRepoFetcher
1608
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1609
 
               self.source, self.source._format, self.target, self.target._format)
1610
 
        f = GenericRepoFetcher(to_repository=self.target,
1611
 
                               from_repository=self.source,
1612
 
                               last_revision=revision_id,
1613
 
                               pb=pb)
1614
 
        return f.count_copied, f.failed_revisions
1615
 
 
1616
 
    def lock_read(self):
1617
 
        """Take out a logical read lock.
1618
 
 
1619
 
        This will lock the source branch and the target branch. The source gets
1620
 
        a read lock and the target a read lock.
1621
 
        """
1622
 
        self._double_lock(self.source.lock_read, self.target.lock_read)
1623
 
 
1624
 
    def lock_write(self):
1625
 
        """Take out a logical write lock.
1626
 
 
1627
 
        This will lock the source branch and the target branch. The source gets
1628
 
        a read lock and the target a write lock.
1629
 
        """
1630
 
        self._double_lock(self.source.lock_read, self.target.lock_write)
1631
 
 
 
1775
        raise NotImplementedError(self.fetch)
 
1776
   
1632
1777
    @needs_read_lock
1633
1778
    def missing_revision_ids(self, revision_id=None):
1634
1779
        """Return the revision ids that source has that target does not.
1642
1787
        target_ids = set(self.target.all_revision_ids())
1643
1788
        if revision_id is not None:
1644
1789
            source_ids = self.source.get_ancestry(revision_id)
1645
 
            assert source_ids[0] == None
 
1790
            assert source_ids[0] is None
1646
1791
            source_ids.pop(0)
1647
1792
        else:
1648
1793
            source_ids = self.source.all_revision_ids()
1652
1797
        # that we've decided we need.
1653
1798
        return [rev_id for rev_id in source_ids if rev_id in result_set]
1654
1799
 
1655
 
    def unlock(self):
1656
 
        """Release the locks on source and target."""
 
1800
 
 
1801
class InterSameDataRepository(InterRepository):
 
1802
    """Code for converting between repositories that represent the same data.
 
1803
    
 
1804
    Data format and model must match for this to work.
 
1805
    """
 
1806
 
 
1807
    _matching_repo_format = RepositoryFormat4()
 
1808
    """Repository format for testing with."""
 
1809
 
 
1810
    @staticmethod
 
1811
    def is_compatible(source, target):
 
1812
        if not isinstance(source, Repository):
 
1813
            return False
 
1814
        if not isinstance(target, Repository):
 
1815
            return False
 
1816
        if source._format.rich_root_data == target._format.rich_root_data:
 
1817
            return True
 
1818
        else:
 
1819
            return False
 
1820
 
 
1821
    @needs_write_lock
 
1822
    def copy_content(self, revision_id=None, basis=None):
 
1823
        """Make a complete copy of the content in self into destination.
 
1824
        
 
1825
        This is a destructive operation! Do not use it on existing 
 
1826
        repositories.
 
1827
 
 
1828
        :param revision_id: Only copy the content needed to construct
 
1829
                            revision_id and its parents.
 
1830
        :param basis: Copy the needed data preferentially from basis.
 
1831
        """
1657
1832
        try:
1658
 
            self.target.unlock()
1659
 
        finally:
1660
 
            self.source.unlock()
1661
 
 
1662
 
 
1663
 
class InterWeaveRepo(InterRepository):
 
1833
            self.target.set_make_working_trees(self.source.make_working_trees())
 
1834
        except NotImplementedError:
 
1835
            pass
 
1836
        # grab the basis available data
 
1837
        if basis is not None:
 
1838
            self.target.fetch(basis, revision_id=revision_id)
 
1839
        # but don't bother fetching if we have the needed data now.
 
1840
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
 
1841
            self.target.has_revision(revision_id)):
 
1842
            return
 
1843
        self.target.fetch(self.source, revision_id=revision_id)
 
1844
 
 
1845
    @needs_write_lock
 
1846
    def fetch(self, revision_id=None, pb=None):
 
1847
        """See InterRepository.fetch()."""
 
1848
        from bzrlib.fetch import GenericRepoFetcher
 
1849
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
 
1850
               self.source, self.source._format, self.target, 
 
1851
               self.target._format)
 
1852
        f = GenericRepoFetcher(to_repository=self.target,
 
1853
                               from_repository=self.source,
 
1854
                               last_revision=revision_id,
 
1855
                               pb=pb)
 
1856
        return f.count_copied, f.failed_revisions
 
1857
 
 
1858
 
 
1859
class InterWeaveRepo(InterSameDataRepository):
1664
1860
    """Optimised code paths between Weave based repositories."""
1665
1861
 
1666
1862
    _matching_repo_format = RepositoryFormat7()
1752
1948
        # - RBC 20060209
1753
1949
        if revision_id is not None:
1754
1950
            source_ids = self.source.get_ancestry(revision_id)
1755
 
            assert source_ids[0] == None
 
1951
            assert source_ids[0] is None
1756
1952
            source_ids.pop(0)
1757
1953
        else:
1758
1954
            source_ids = self.source._all_possible_ids()
1778
1974
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1779
1975
 
1780
1976
 
1781
 
class InterKnitRepo(InterRepository):
 
1977
class InterKnitRepo(InterSameDataRepository):
1782
1978
    """Optimised code paths between Knit based repositories."""
1783
1979
 
1784
1980
    _matching_repo_format = RepositoryFormatKnit1()
1815
2011
        """See InterRepository.missing_revision_ids()."""
1816
2012
        if revision_id is not None:
1817
2013
            source_ids = self.source.get_ancestry(revision_id)
1818
 
            assert source_ids[0] == None
 
2014
            assert source_ids[0] is None
1819
2015
            source_ids.pop(0)
1820
2016
        else:
1821
2017
            source_ids = self.source._all_possible_ids()
1840
2036
            # that against the revision records.
1841
2037
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1842
2038
 
 
2039
 
 
2040
class InterModel1and2(InterRepository):
 
2041
 
 
2042
    _matching_repo_format = None
 
2043
 
 
2044
    @staticmethod
 
2045
    def is_compatible(source, target):
 
2046
        if not isinstance(source, Repository):
 
2047
            return False
 
2048
        if not isinstance(target, Repository):
 
2049
            return False
 
2050
        if not source._format.rich_root_data and target._format.rich_root_data:
 
2051
            return True
 
2052
        else:
 
2053
            return False
 
2054
 
 
2055
    @needs_write_lock
 
2056
    def fetch(self, revision_id=None, pb=None):
 
2057
        """See InterRepository.fetch()."""
 
2058
        from bzrlib.fetch import Model1toKnit2Fetcher
 
2059
        f = Model1toKnit2Fetcher(to_repository=self.target,
 
2060
                                 from_repository=self.source,
 
2061
                                 last_revision=revision_id,
 
2062
                                 pb=pb)
 
2063
        return f.count_copied, f.failed_revisions
 
2064
 
 
2065
    @needs_write_lock
 
2066
    def copy_content(self, revision_id=None, basis=None):
 
2067
        """Make a complete copy of the content in self into destination.
 
2068
        
 
2069
        This is a destructive operation! Do not use it on existing 
 
2070
        repositories.
 
2071
 
 
2072
        :param revision_id: Only copy the content needed to construct
 
2073
                            revision_id and its parents.
 
2074
        :param basis: Copy the needed data preferentially from basis.
 
2075
        """
 
2076
        try:
 
2077
            self.target.set_make_working_trees(self.source.make_working_trees())
 
2078
        except NotImplementedError:
 
2079
            pass
 
2080
        # grab the basis available data
 
2081
        if basis is not None:
 
2082
            self.target.fetch(basis, revision_id=revision_id)
 
2083
        # but don't bother fetching if we have the needed data now.
 
2084
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
 
2085
            self.target.has_revision(revision_id)):
 
2086
            return
 
2087
        self.target.fetch(self.source, revision_id=revision_id)
 
2088
 
 
2089
 
 
2090
class InterKnit1and2(InterKnitRepo):
 
2091
 
 
2092
    _matching_repo_format = None
 
2093
 
 
2094
    @staticmethod
 
2095
    def is_compatible(source, target):
 
2096
        """Be compatible with Knit1 source and Knit2 target"""
 
2097
        try:
 
2098
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
 
2099
                    isinstance(target._format, (RepositoryFormatKnit2)))
 
2100
        except AttributeError:
 
2101
            return False
 
2102
 
 
2103
    @needs_write_lock
 
2104
    def fetch(self, revision_id=None, pb=None):
 
2105
        """See InterRepository.fetch()."""
 
2106
        from bzrlib.fetch import Knit1to2Fetcher
 
2107
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
 
2108
               self.source, self.source._format, self.target, 
 
2109
               self.target._format)
 
2110
        f = Knit1to2Fetcher(to_repository=self.target,
 
2111
                            from_repository=self.source,
 
2112
                            last_revision=revision_id,
 
2113
                            pb=pb)
 
2114
        return f.count_copied, f.failed_revisions
 
2115
 
 
2116
 
 
2117
InterRepository.register_optimiser(InterSameDataRepository)
1843
2118
InterRepository.register_optimiser(InterWeaveRepo)
1844
2119
InterRepository.register_optimiser(InterKnitRepo)
 
2120
InterRepository.register_optimiser(InterModel1and2)
 
2121
InterRepository.register_optimiser(InterKnit1and2)
1845
2122
 
1846
2123
 
1847
2124
class RepositoryTestProviderAdapter(object):
1859
2136
        self._formats = formats
1860
2137
    
1861
2138
    def adapt(self, test):
1862
 
        result = TestSuite()
 
2139
        result = unittest.TestSuite()
1863
2140
        for repository_format, bzrdir_format in self._formats:
1864
2141
            new_test = deepcopy(test)
1865
2142
            new_test.transport_server = self._transport_server
1889
2166
        self._formats = formats
1890
2167
    
1891
2168
    def adapt(self, test):
1892
 
        result = TestSuite()
 
2169
        result = unittest.TestSuite()
1893
2170
        for interrepo_class, repository_format, repository_format_to in self._formats:
1894
2171
            new_test = deepcopy(test)
1895
2172
            new_test.transport_server = self._transport_server
1912
2189
        # default format.
1913
2190
        # XXX: robertc 20060220 reinstate this when there are two supported
1914
2191
        # formats which do not have an optimal code path between them.
1915
 
        result.append((InterRepository,
1916
 
                       RepositoryFormat6(),
1917
 
                       RepositoryFormatKnit1()))
 
2192
        #result.append((InterRepository,
 
2193
        #               RepositoryFormat6(),
 
2194
        #               RepositoryFormatKnit1()))
1918
2195
        for optimiser in InterRepository._optimisers:
1919
 
            result.append((optimiser,
1920
 
                           optimiser._matching_repo_format,
1921
 
                           optimiser._matching_repo_format
1922
 
                           ))
 
2196
            if optimiser._matching_repo_format is not None:
 
2197
                result.append((optimiser,
 
2198
                               optimiser._matching_repo_format,
 
2199
                               optimiser._matching_repo_format
 
2200
                               ))
1923
2201
        # if there are specific combinations we want to use, we can add them 
1924
2202
        # here.
 
2203
        result.append((InterModel1and2, RepositoryFormat5(),
 
2204
                       RepositoryFormatKnit2()))
 
2205
        result.append((InterKnit1and2, RepositoryFormatKnit1(),
 
2206
                       RepositoryFormatKnit2()))
1925
2207
        return result
1926
2208
 
1927
2209
 
1954
2236
        self.step('Moving repository to repository.backup')
1955
2237
        self.repo_dir.transport.move('repository', 'repository.backup')
1956
2238
        backup_transport =  self.repo_dir.transport.clone('repository.backup')
 
2239
        repo._format.check_conversion_target(self.target_format)
1957
2240
        self.source_repo = repo._format.open(self.repo_dir,
1958
2241
            _found=True,
1959
2242
            _override_transport=backup_transport)
1982
2265
    This allows describing a tree to be committed without needing to 
1983
2266
    know the internals of the format of the repository.
1984
2267
    """
 
2268
    
 
2269
    record_root_entry = False
1985
2270
    def __init__(self, repository, parents, config, timestamp=None, 
1986
2271
                 timezone=None, committer=None, revprops=None, 
1987
2272
                 revision_id=None):
2004
2289
            assert isinstance(committer, basestring), type(committer)
2005
2290
            self._committer = committer
2006
2291
 
2007
 
        self.new_inventory = Inventory()
 
2292
        self.new_inventory = Inventory(None)
2008
2293
        self._new_revision_id = revision_id
2009
2294
        self.parents = parents
2010
2295
        self.repository = repository
2014
2299
            self._revprops.update(revprops)
2015
2300
 
2016
2301
        if timestamp is None:
2017
 
            self._timestamp = time.time()
2018
 
        else:
2019
 
            self._timestamp = long(timestamp)
 
2302
            timestamp = time.time()
 
2303
        # Restrict resolution to 1ms
 
2304
        self._timestamp = round(timestamp, 3)
2020
2305
 
2021
2306
        if timezone is None:
2022
2307
            self._timezone = local_time_offset()
2030
2315
 
2031
2316
        :return: The revision id of the recorded revision.
2032
2317
        """
2033
 
        rev = Revision(timestamp=self._timestamp,
 
2318
        rev = _mod_revision.Revision(
 
2319
                       timestamp=self._timestamp,
2034
2320
                       timezone=self._timezone,
2035
2321
                       committer=self._committer,
2036
2322
                       message=message,
2042
2328
            self.new_inventory, self._config)
2043
2329
        return self._new_revision_id
2044
2330
 
 
2331
    def revision_tree(self):
 
2332
        """Return the tree that was just committed.
 
2333
 
 
2334
        After calling commit() this can be called to get a RevisionTree
 
2335
        representing the newly committed tree. This is preferred to
 
2336
        calling Repository.revision_tree() because that may require
 
2337
        deserializing the inventory, while we already have a copy in
 
2338
        memory.
 
2339
        """
 
2340
        return RevisionTree(self.repository, self.new_inventory,
 
2341
                            self._new_revision_id)
 
2342
 
2045
2343
    def finish_inventory(self):
2046
2344
        """Tell the builder that the inventory is finished."""
 
2345
        if self.new_inventory.root is None:
 
2346
            symbol_versioning.warn('Root entry should be supplied to'
 
2347
                ' record_entry_contents, as of bzr 0.10.',
 
2348
                 DeprecationWarning, stacklevel=2)
 
2349
            self.new_inventory.add(InventoryDirectory(ROOT_ID, '', None))
2047
2350
        self.new_inventory.revision_id = self._new_revision_id
2048
2351
        self.inv_sha1 = self.repository.add_inventory(
2049
2352
            self._new_revision_id,
2073
2376
    def record_entry_contents(self, ie, parent_invs, path, tree):
2074
2377
        """Record the content of ie from tree into the commit if needed.
2075
2378
 
 
2379
        Side effect: sets ie.revision when unchanged
 
2380
 
2076
2381
        :param ie: An inventory entry present in the commit.
2077
2382
        :param parent_invs: The inventories of the parent revisions of the
2078
2383
            commit.
2080
2385
        :param tree: The tree which contains this entry and should be used to 
2081
2386
        obtain content.
2082
2387
        """
 
2388
        if self.new_inventory.root is None and ie.parent_id is not None:
 
2389
            symbol_versioning.warn('Root entry should be supplied to'
 
2390
                ' record_entry_contents, as of bzr 0.10.',
 
2391
                 DeprecationWarning, stacklevel=2)
 
2392
            self.record_entry_contents(tree.inventory.root.copy(), parent_invs,
 
2393
                                       '', tree)
2083
2394
        self.new_inventory.add(ie)
2084
2395
 
2085
2396
        # ie.revision is always None if the InventoryEntry is considered
2087
2398
        # which may be the sole parent if it is untouched.
2088
2399
        if ie.revision is not None:
2089
2400
            return
 
2401
 
 
2402
        # In this revision format, root entries have no knit or weave
 
2403
        if ie is self.new_inventory.root:
 
2404
            # When serializing out to disk and back in
 
2405
            # root.revision is always _new_revision_id
 
2406
            ie.revision = self._new_revision_id
 
2407
            return
2090
2408
        previous_entries = ie.find_previous_heads(
2091
2409
            parent_invs,
2092
2410
            self.repository.weave_store,
2115
2433
        :param text_sha1: Optional SHA1 of the file contents.
2116
2434
        :param text_size: Optional size of the file contents.
2117
2435
        """
2118
 
        mutter('storing text of file {%s} in revision {%s} into %r',
2119
 
               file_id, self._new_revision_id, self.repository.weave_store)
 
2436
        # mutter('storing text of file {%s} in revision {%s} into %r',
 
2437
        #        file_id, self._new_revision_id, self.repository.weave_store)
2120
2438
        # special case to avoid diffing on renames or 
2121
2439
        # reparenting
2122
2440
        if (len(file_parents) == 1
2152
2470
        versionedfile.clear_cache()
2153
2471
 
2154
2472
 
 
2473
class _CommitBuilder(CommitBuilder):
 
2474
    """Temporary class so old CommitBuilders are detected properly
 
2475
    
 
2476
    Note: CommitBuilder works whether or not root entry is recorded.
 
2477
    """
 
2478
 
 
2479
    record_root_entry = True
 
2480
 
 
2481
 
 
2482
class RootCommitBuilder(CommitBuilder):
 
2483
    """This commitbuilder actually records the root id"""
 
2484
    
 
2485
    record_root_entry = True
 
2486
 
 
2487
    def record_entry_contents(self, ie, parent_invs, path, tree):
 
2488
        """Record the content of ie from tree into the commit if needed.
 
2489
 
 
2490
        Side effect: sets ie.revision when unchanged
 
2491
 
 
2492
        :param ie: An inventory entry present in the commit.
 
2493
        :param parent_invs: The inventories of the parent revisions of the
 
2494
            commit.
 
2495
        :param path: The path the entry is at in the tree.
 
2496
        :param tree: The tree which contains this entry and should be used to 
 
2497
        obtain content.
 
2498
        """
 
2499
        assert self.new_inventory.root is not None or ie.parent_id is None
 
2500
        self.new_inventory.add(ie)
 
2501
 
 
2502
        # ie.revision is always None if the InventoryEntry is considered
 
2503
        # for committing. ie.snapshot will record the correct revision 
 
2504
        # which may be the sole parent if it is untouched.
 
2505
        if ie.revision is not None:
 
2506
            return
 
2507
 
 
2508
        previous_entries = ie.find_previous_heads(
 
2509
            parent_invs,
 
2510
            self.repository.weave_store,
 
2511
            self.repository.get_transaction())
 
2512
        # we are creating a new revision for ie in the history store
 
2513
        # and inventory.
 
2514
        ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
 
2515
 
 
2516
 
2155
2517
_unescape_map = {
2156
2518
    'apos':"'",
2157
2519
    'quot':'"',