/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

[merge] bzr.dev 2240

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
 
25
import unittest
23
26
 
24
27
from bzrlib import (
25
 
    bzrdir, 
26
 
    check, 
27
 
    delta, 
28
 
    gpg, 
29
 
    errors, 
 
28
    bzrdir,
 
29
    check,
 
30
    delta,
 
31
    errors,
 
32
    generate_ids,
 
33
    gpg,
 
34
    graph,
 
35
    knit,
 
36
    lazy_regex,
 
37
    lockable_files,
 
38
    lockdir,
30
39
    osutils,
 
40
    revision as _mod_revision,
 
41
    symbol_versioning,
31
42
    transactions,
32
 
    ui, 
33
 
    xml5, 
 
43
    ui,
 
44
    weave,
 
45
    weavefile,
 
46
    xml5,
34
47
    xml6,
35
48
    )
 
49
from bzrlib.osutils import (
 
50
    rand_bytes,
 
51
    compact_date, 
 
52
    local_time_offset,
 
53
    )
 
54
from bzrlib.revisiontree import RevisionTree
 
55
from bzrlib.store.versioned import VersionedFileStore
 
56
from bzrlib.store.text import TextStore
 
57
from bzrlib.testament import Testament
 
58
""")
 
59
 
36
60
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
 
from bzrlib.errors import InvalidRevisionId
38
 
from bzrlib.graph import Graph
39
61
from bzrlib.inter import InterObject
40
62
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
41
 
from bzrlib.knit import KnitVersionedFile, KnitPlainFactory
42
 
from bzrlib.lockable_files import LockableFiles, TransportLock
43
 
from bzrlib.lockdir import LockDir
44
 
from bzrlib.osutils import (safe_unicode, rand_bytes, compact_date, 
45
 
                            local_time_offset)
46
 
from bzrlib.revision import NULL_REVISION, Revision
47
 
from bzrlib.revisiontree import RevisionTree
48
 
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
49
 
from bzrlib.store.text import TextStore
50
 
from bzrlib import symbol_versioning
51
 
from bzrlib.symbol_versioning import (deprecated_method,
52
 
        zero_nine, 
 
63
from bzrlib.symbol_versioning import (
 
64
        deprecated_method,
 
65
        zero_nine,
53
66
        )
54
 
from bzrlib.testament import Testament
55
67
from bzrlib.trace import mutter, note, warning
56
 
from bzrlib.tsort import topo_sort
57
 
from bzrlib.weave import WeaveFile
58
68
 
59
69
 
60
70
# Old formats display a warning, but only once
73
83
    remote) disk.
74
84
    """
75
85
 
 
86
    _file_ids_altered_regex = lazy_regex.lazy_compile(
 
87
        r'file_id="(?P<file_id>[^"]+)"'
 
88
        r'.*revision="(?P<revision_id>[^"]+)"'
 
89
        )
 
90
 
76
91
    @needs_write_lock
77
92
    def add_inventory(self, revid, inv, parents):
78
93
        """Add the inventory inv to the repository as revid.
82
97
 
83
98
        returns the sha1 of the serialized inventory.
84
99
        """
 
100
        _mod_revision.check_not_reserved_id(revid)
85
101
        assert inv.revision_id is None or inv.revision_id == revid, \
86
102
            "Mismatch between inventory revision" \
87
103
            " id and insertion revid (%r, %r)" % (inv.revision_id, revid)
113
129
                       If supplied its signature_needed method will be used
114
130
                       to determine if a signature should be made.
115
131
        """
 
132
        _mod_revision.check_not_reserved_id(rev_id)
116
133
        if config is not None and config.signature_needed():
117
134
            if inv is None:
118
135
                inv = self.get_inventory(rev_id)
317
334
        or testing the revision graph.
318
335
        """
319
336
        if not revision_id or not isinstance(revision_id, basestring):
320
 
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
 
337
            raise errors.InvalidRevisionId(revision_id=revision_id,
 
338
                                           branch=self)
321
339
        return self._revision_store.get_revisions([revision_id],
322
340
                                                  self.get_transaction())[0]
323
341
    @needs_read_lock
426
444
        # revisions. We don't need to see all lines in the inventory because
427
445
        # only those added in an inventory in rev X can contain a revision=X
428
446
        # line.
 
447
        unescape_revid_cache = {}
 
448
        unescape_fileid_cache = {}
 
449
 
 
450
        # jam 20061218 In a big fetch, this handles hundreds of thousands
 
451
        # of lines, so it has had a lot of inlining and optimizing done.
 
452
        # Sorry that it is a little bit messy.
 
453
        # Move several functions to be local variables, since this is a long
 
454
        # running loop.
 
455
        search = self._file_ids_altered_regex.search
 
456
        unescape = _unescape_xml
 
457
        setdefault = result.setdefault
429
458
        pb = ui.ui_factory.nested_progress_bar()
430
459
        try:
431
460
            for line in w.iter_lines_added_or_present_in_versions(
432
 
                selected_revision_ids, pb=pb):
433
 
                start = line.find('file_id="')+9
434
 
                if start < 9: continue
435
 
                end = line.find('"', start)
436
 
                assert end>= 0
437
 
                file_id = _unescape_xml(line[start:end])
438
 
 
439
 
                start = line.find('revision="')+10
440
 
                if start < 10: continue
441
 
                end = line.find('"', start)
442
 
                assert end>= 0
443
 
                revision_id = _unescape_xml(line[start:end])
 
461
                                        selected_revision_ids, pb=pb):
 
462
                match = search(line)
 
463
                if match is None:
 
464
                    continue
 
465
                # One call to match.group() returning multiple items is quite a
 
466
                # bit faster than 2 calls to match.group() each returning 1
 
467
                file_id, revision_id = match.group('file_id', 'revision_id')
 
468
 
 
469
                # Inlining the cache lookups helps a lot when you make 170,000
 
470
                # lines and 350k ids, versus 8.4 unique ids.
 
471
                # Using a cache helps in 2 ways:
 
472
                #   1) Avoids unnecessary decoding calls
 
473
                #   2) Re-uses cached strings, which helps in future set and
 
474
                #      equality checks.
 
475
                # (2) is enough that removing encoding entirely along with
 
476
                # the cache (so we are using plain strings) results in no
 
477
                # performance improvement.
 
478
                try:
 
479
                    revision_id = unescape_revid_cache[revision_id]
 
480
                except KeyError:
 
481
                    unescaped = unescape(revision_id)
 
482
                    unescape_revid_cache[revision_id] = unescaped
 
483
                    revision_id = unescaped
 
484
 
444
485
                if revision_id in selected_revision_ids:
445
 
                    result.setdefault(file_id, set()).add(revision_id)
 
486
                    try:
 
487
                        file_id = unescape_fileid_cache[file_id]
 
488
                    except KeyError:
 
489
                        unescaped = unescape(file_id)
 
490
                        unescape_fileid_cache[file_id] = unescaped
 
491
                        file_id = unescaped
 
492
                    setdefault(file_id, set()).add(revision_id)
446
493
        finally:
447
494
            pb.finished()
448
495
        return result
497
544
        :return: a dictionary of revision_id->revision_parents_list.
498
545
        """
499
546
        # special case NULL_REVISION
500
 
        if revision_id == NULL_REVISION:
 
547
        if revision_id == _mod_revision.NULL_REVISION:
501
548
            return {}
502
 
        weave = self.get_inventory_weave()
503
 
        all_revisions = self._eliminate_revisions_not_present(weave.versions())
504
 
        entire_graph = dict([(node, weave.get_parents(node)) for 
 
549
        a_weave = self.get_inventory_weave()
 
550
        all_revisions = self._eliminate_revisions_not_present(
 
551
                                a_weave.versions())
 
552
        entire_graph = dict([(node, a_weave.get_parents(node)) for 
505
553
                             node in all_revisions])
506
554
        if revision_id is None:
507
555
            return entire_graph
526
574
        :param revision_ids: an iterable of revisions to graph or None for all.
527
575
        :return: a Graph object with the graph reachable from revision_ids.
528
576
        """
529
 
        result = Graph()
 
577
        result = graph.Graph()
530
578
        if not revision_ids:
531
579
            pending = set(self.all_revision_ids())
532
580
            required = set([])
533
581
        else:
534
582
            pending = set(revision_ids)
535
583
            # special case NULL_REVISION
536
 
            if NULL_REVISION in pending:
537
 
                pending.remove(NULL_REVISION)
 
584
            if _mod_revision.NULL_REVISION in pending:
 
585
                pending.remove(_mod_revision.NULL_REVISION)
538
586
            required = set(pending)
539
587
        done = set([])
540
588
        while len(pending):
594
642
        """
595
643
        # TODO: refactor this to use an existing revision object
596
644
        # so we don't need to read it in twice.
597
 
        if revision_id is None or revision_id == NULL_REVISION:
598
 
            return RevisionTree(self, Inventory(), NULL_REVISION)
 
645
        if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
 
646
            return RevisionTree(self, Inventory(root_id=None), 
 
647
                                _mod_revision.NULL_REVISION)
599
648
        else:
600
649
            inv = self.get_revision_inventory(revision_id)
601
650
            return RevisionTree(self, inv, revision_id)
606
655
 
607
656
        `revision_id` may not be None or 'null:'"""
608
657
        assert None not in revision_ids
609
 
        assert NULL_REVISION not in revision_ids
 
658
        assert _mod_revision.NULL_REVISION not in revision_ids
610
659
        texts = self.get_inventory_weave().get_texts(revision_ids)
611
660
        for text, revision_id in zip(texts, revision_ids):
612
661
            inv = self.deserialise_inventory(revision_id, text)
717
766
    def supports_rich_root(self):
718
767
        return self._format.rich_root_data
719
768
 
 
769
    def _check_ascii_revisionid(self, revision_id, method):
 
770
        """Private helper for ascii-only repositories."""
 
771
        # weave repositories refuse to store revisionids that are non-ascii.
 
772
        if revision_id is not None:
 
773
            # weaves require ascii revision ids.
 
774
            if isinstance(revision_id, unicode):
 
775
                try:
 
776
                    revision_id.encode('ascii')
 
777
                except UnicodeEncodeError:
 
778
                    raise errors.NonAsciiRevisionId(method, self)
 
779
 
720
780
 
721
781
class AllInOneRepository(Repository):
722
782
    """Legacy support - the repository behaviour for all-in-one branches."""
751
811
            text_store = get_store('text-store')
752
812
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
753
813
 
 
814
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
815
                           timezone=None, committer=None, revprops=None,
 
816
                           revision_id=None):
 
817
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
 
818
        return Repository.get_commit_builder(self, branch, parents, config,
 
819
            timestamp, timezone, committer, revprops, revision_id)
 
820
 
754
821
    @needs_read_lock
755
822
    def is_shared(self):
756
823
        """AllInOne repositories cannot be shared."""
862
929
        return not self.control_files._transport.has('no-working-trees')
863
930
 
864
931
 
 
932
class WeaveMetaDirRepository(MetaDirRepository):
 
933
    """A subclass of MetaDirRepository to set weave specific policy."""
 
934
 
 
935
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
936
                           timezone=None, committer=None, revprops=None,
 
937
                           revision_id=None):
 
938
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
 
939
        return MetaDirRepository.get_commit_builder(self, branch, parents,
 
940
            config, timestamp, timezone, committer, revprops, revision_id)
 
941
 
 
942
 
865
943
class KnitRepository(MetaDirRepository):
866
944
    """Knit format repository."""
867
945
 
933
1011
        :return: a dictionary of revision_id->revision_parents_list.
934
1012
        """
935
1013
        # special case NULL_REVISION
936
 
        if revision_id == NULL_REVISION:
 
1014
        if revision_id == _mod_revision.NULL_REVISION:
937
1015
            return {}
938
 
        weave = self._get_revision_vf()
939
 
        entire_graph = weave.get_graph()
 
1016
        a_weave = self._get_revision_vf()
 
1017
        entire_graph = a_weave.get_graph()
940
1018
        if revision_id is None:
941
 
            return weave.get_graph()
942
 
        elif revision_id not in weave:
 
1019
            return a_weave.get_graph()
 
1020
        elif revision_id not in a_weave:
943
1021
            raise errors.NoSuchRevision(self, revision_id)
944
1022
        else:
945
1023
            # add what can be reached from revision_id
947
1025
            pending = set([revision_id])
948
1026
            while len(pending) > 0:
949
1027
                node = pending.pop()
950
 
                result[node] = weave.get_parents(node)
 
1028
                result[node] = a_weave.get_parents(node)
951
1029
                for revision_id in result[node]:
952
1030
                    if revision_id not in result:
953
1031
                        pending.add(revision_id)
960
1038
        :param revision_ids: an iterable of revisions to graph or None for all.
961
1039
        :return: a Graph object with the graph reachable from revision_ids.
962
1040
        """
963
 
        result = Graph()
 
1041
        result = graph.Graph()
964
1042
        vf = self._get_revision_vf()
965
1043
        versions = set(vf.versions())
966
1044
        if not revision_ids:
969
1047
        else:
970
1048
            pending = set(revision_ids)
971
1049
            # special case NULL_REVISION
972
 
            if NULL_REVISION in pending:
973
 
                pending.remove(NULL_REVISION)
 
1050
            if _mod_revision.NULL_REVISION in pending:
 
1051
                pending.remove(_mod_revision.NULL_REVISION)
974
1052
            required = set(pending)
975
1053
        done = set([])
976
1054
        while len(pending):
1155
1233
                                  transport,
1156
1234
                                  control_files,
1157
1235
                                  prefixed=True,
1158
 
                                  versionedfile_class=WeaveFile,
 
1236
                                  versionedfile_class=weave.WeaveFile,
1159
1237
                                  versionedfile_kwargs={},
1160
1238
                                  escaped=False):
1161
1239
        weave_transport = control_files._transport.clone(name)
1202
1280
        klass._formats[format.get_format_string()] = format
1203
1281
 
1204
1282
    @classmethod
 
1283
    @deprecated_method(symbol_versioning.zero_fourteen)
1205
1284
    def set_default_format(klass, format):
 
1285
        klass._set_default_format(format)
 
1286
 
 
1287
    @classmethod
 
1288
    def _set_default_format(klass, format):
1206
1289
        klass._default_format = format
1207
1290
 
1208
1291
    @classmethod
1222
1305
        TODO: when creating split out bzr branch formats, move this to a common
1223
1306
        base for Format5, Format6. or something like that.
1224
1307
        """
1225
 
        from bzrlib.weavefile import write_weave_v5
1226
 
        from bzrlib.weave import Weave
1227
 
 
1228
1308
        if shared:
1229
1309
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
1230
1310
 
1234
1314
        
1235
1315
        # Create an empty weave
1236
1316
        sio = StringIO()
1237
 
        write_weave_v5(Weave(), sio)
 
1317
        weavefile.write_weave_v5(weave.Weave(), sio)
1238
1318
        empty_weave = sio.getvalue()
1239
1319
 
1240
1320
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1244
1324
        
1245
1325
        # FIXME: RBC 20060125 don't peek under the covers
1246
1326
        # NB: no need to escape relative paths that are url safe.
1247
 
        control_files = LockableFiles(a_bzrdir.transport, 'branch-lock',
1248
 
                                      TransportLock)
 
1327
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
 
1328
                                'branch-lock', lockable_files.TransportLock)
1249
1329
        control_files.create_lock()
1250
1330
        control_files.lock_write()
1251
1331
        control_files._transport.mkdir_multi(dirs,
1415
1495
        # FIXME: RBC 20060125 don't peek under the covers
1416
1496
        # NB: no need to escape relative paths that are url safe.
1417
1497
        repository_transport = a_bzrdir.get_repository_transport(self)
1418
 
        control_files = LockableFiles(repository_transport, 'lock', LockDir)
 
1498
        control_files = lockable_files.LockableFiles(repository_transport,
 
1499
                                'lock', lockdir.LockDir)
1419
1500
        control_files.create_lock()
1420
1501
        return control_files
1421
1502
 
1487
1568
        :param shared: If true the repository will be initialized as a shared
1488
1569
                       repository.
1489
1570
        """
1490
 
        from bzrlib.weavefile import write_weave_v5
1491
 
        from bzrlib.weave import Weave
1492
 
 
1493
1571
        # Create an empty weave
1494
1572
        sio = StringIO()
1495
 
        write_weave_v5(Weave(), sio)
 
1573
        weavefile.write_weave_v5(weave.Weave(), sio)
1496
1574
        empty_weave = sio.getvalue()
1497
1575
 
1498
1576
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1518
1596
            repo_transport = _override_transport
1519
1597
        else:
1520
1598
            repo_transport = a_bzrdir.get_repository_transport(None)
1521
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1599
        control_files = lockable_files.LockableFiles(repo_transport,
 
1600
                                'lock', lockdir.LockDir)
1522
1601
        text_store = self._get_text_store(repo_transport, control_files)
1523
1602
        control_store = self._get_control_store(repo_transport, control_files)
1524
1603
        _revision_store = self._get_revision_store(repo_transport, control_files)
1525
 
        return MetaDirRepository(_format=self,
1526
 
                                 a_bzrdir=a_bzrdir,
1527
 
                                 control_files=control_files,
1528
 
                                 _revision_store=_revision_store,
1529
 
                                 control_store=control_store,
1530
 
                                 text_store=text_store)
 
1604
        return WeaveMetaDirRepository(_format=self,
 
1605
            a_bzrdir=a_bzrdir,
 
1606
            control_files=control_files,
 
1607
            _revision_store=_revision_store,
 
1608
            control_store=control_store,
 
1609
            text_store=text_store)
1531
1610
 
1532
1611
 
1533
1612
class RepositoryFormatKnit(MetaDirRepositoryFormat):
1550
1629
            repo_transport,
1551
1630
            prefixed=False,
1552
1631
            file_mode=control_files._file_mode,
1553
 
            versionedfile_class=KnitVersionedFile,
1554
 
            versionedfile_kwargs={'factory':KnitPlainFactory()},
 
1632
            versionedfile_class=knit.KnitVersionedFile,
 
1633
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1555
1634
            )
1556
1635
 
1557
1636
    def _get_revision_store(self, repo_transport, control_files):
1562
1641
            file_mode=control_files._file_mode,
1563
1642
            prefixed=False,
1564
1643
            precious=True,
1565
 
            versionedfile_class=KnitVersionedFile,
1566
 
            versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory(),},
 
1644
            versionedfile_class=knit.KnitVersionedFile,
 
1645
            versionedfile_kwargs={'delta':False,
 
1646
                                  'factory':knit.KnitPlainFactory(),
 
1647
                                 },
1567
1648
            escaped=True,
1568
1649
            )
1569
1650
        return KnitRevisionStore(versioned_file_store)
1571
1652
    def _get_text_store(self, transport, control_files):
1572
1653
        """See RepositoryFormat._get_text_store()."""
1573
1654
        return self._get_versioned_file_store('knits',
1574
 
                                              transport,
1575
 
                                              control_files,
1576
 
                                              versionedfile_class=KnitVersionedFile,
1577
 
                                              versionedfile_kwargs={
1578
 
                                                  'create_parent_dir':True,
1579
 
                                                  'delay_create':True,
1580
 
                                                  'dir_mode':control_files._dir_mode,
1581
 
                                              },
1582
 
                                              escaped=True)
 
1655
                                  transport,
 
1656
                                  control_files,
 
1657
                                  versionedfile_class=knit.KnitVersionedFile,
 
1658
                                  versionedfile_kwargs={
 
1659
                                      'create_parent_dir':True,
 
1660
                                      'delay_create':True,
 
1661
                                      'dir_mode':control_files._dir_mode,
 
1662
                                  },
 
1663
                                  escaped=True)
1583
1664
 
1584
1665
    def initialize(self, a_bzrdir, shared=False):
1585
1666
        """Create a knit format 1 repository.
1596
1677
        
1597
1678
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1598
1679
        repo_transport = a_bzrdir.get_repository_transport(None)
1599
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1680
        control_files = lockable_files.LockableFiles(repo_transport,
 
1681
                                'lock', lockdir.LockDir)
1600
1682
        control_store = self._get_control_store(repo_transport, control_files)
1601
1683
        transaction = transactions.WriteTransaction()
1602
1684
        # trigger a write of the inventory store.
1603
1685
        control_store.get_weave_or_empty('inventory', transaction)
1604
1686
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
1687
        # the revision id here is irrelevant: it will not be stored, and cannot
 
1688
        # already exist.
1605
1689
        _revision_store.has_revision_id('A', transaction)
1606
1690
        _revision_store.get_signature_file(transaction)
1607
1691
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1620
1704
            repo_transport = _override_transport
1621
1705
        else:
1622
1706
            repo_transport = a_bzrdir.get_repository_transport(None)
1623
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1707
        control_files = lockable_files.LockableFiles(repo_transport,
 
1708
                                'lock', lockdir.LockDir)
1624
1709
        text_store = self._get_text_store(repo_transport, control_files)
1625
1710
        control_store = self._get_control_store(repo_transport, control_files)
1626
1711
        _revision_store = self._get_revision_store(repo_transport, control_files)
1705
1790
            repo_transport = _override_transport
1706
1791
        else:
1707
1792
            repo_transport = a_bzrdir.get_repository_transport(None)
1708
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1793
        control_files = lockable_files.LockableFiles(repo_transport, 'lock',
 
1794
                                                     lockdir.LockDir)
1709
1795
        text_store = self._get_text_store(repo_transport, control_files)
1710
1796
        control_store = self._get_control_store(repo_transport, control_files)
1711
1797
        _revision_store = self._get_revision_store(repo_transport, control_files)
1721
1807
# formats which have no format string are not discoverable
1722
1808
# and not independently creatable, so are not registered.
1723
1809
RepositoryFormat.register_format(RepositoryFormat7())
 
1810
# KEEP in sync with bzrdir.format_registry default
1724
1811
_default_format = RepositoryFormatKnit1()
1725
1812
RepositoryFormat.register_format(_default_format)
1726
1813
RepositoryFormat.register_format(RepositoryFormatKnit2())
1727
 
RepositoryFormat.set_default_format(_default_format)
 
1814
RepositoryFormat._set_default_format(_default_format)
1728
1815
_legacy_formats = [RepositoryFormat4(),
1729
1816
                   RepositoryFormat5(),
1730
1817
                   RepositoryFormat6()]
1826
1913
        if basis is not None:
1827
1914
            self.target.fetch(basis, revision_id=revision_id)
1828
1915
        # but don't bother fetching if we have the needed data now.
1829
 
        if (revision_id not in (None, NULL_REVISION) and 
 
1916
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
1830
1917
            self.target.has_revision(revision_id)):
1831
1918
            return
1832
1919
        self.target.fetch(self.source, revision_id=revision_id)
2070
2157
        if basis is not None:
2071
2158
            self.target.fetch(basis, revision_id=revision_id)
2072
2159
        # but don't bother fetching if we have the needed data now.
2073
 
        if (revision_id not in (None, NULL_REVISION) and 
 
2160
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
2074
2161
            self.target.has_revision(revision_id)):
2075
2162
            return
2076
2163
        self.target.fetch(self.source, revision_id=revision_id)
2125
2212
        self._formats = formats
2126
2213
    
2127
2214
    def adapt(self, test):
2128
 
        result = TestSuite()
 
2215
        result = unittest.TestSuite()
2129
2216
        for repository_format, bzrdir_format in self._formats:
2130
2217
            new_test = deepcopy(test)
2131
2218
            new_test.transport_server = self._transport_server
2155
2242
        self._formats = formats
2156
2243
    
2157
2244
    def adapt(self, test):
2158
 
        result = TestSuite()
 
2245
        result = unittest.TestSuite()
2159
2246
        for interrepo_class, repository_format, repository_format_to in self._formats:
2160
2247
            new_test = deepcopy(test)
2161
2248
            new_test.transport_server = self._transport_server
2304
2391
 
2305
2392
        :return: The revision id of the recorded revision.
2306
2393
        """
2307
 
        rev = Revision(timestamp=self._timestamp,
 
2394
        rev = _mod_revision.Revision(
 
2395
                       timestamp=self._timestamp,
2308
2396
                       timezone=self._timezone,
2309
2397
                       committer=self._committer,
2310
2398
                       message=message,
2344
2432
 
2345
2433
    def _gen_revision_id(self):
2346
2434
        """Return new revision-id."""
2347
 
        s = '%s-%s-' % (self._config.user_email(), 
2348
 
                        compact_date(self._timestamp))
2349
 
        s += hexlify(rand_bytes(8))
2350
 
        return s
 
2435
        return generate_ids.gen_revision_id(self._config.username(),
 
2436
                                            self._timestamp)
2351
2437
 
2352
2438
    def _generate_revision_if_needed(self):
2353
2439
        """Create a revision id if None was supplied.
2354
2440
        
2355
2441
        If the repository can not support user-specified revision ids
2356
 
        they should override this function and raise UnsupportedOperation
 
2442
        they should override this function and raise CannotSetRevisionId
2357
2443
        if _new_revision_id is not None.
2358
2444
 
2359
 
        :raises: UnsupportedOperation
 
2445
        :raises: CannotSetRevisionId
2360
2446
        """
2361
2447
        if self._new_revision_id is None:
2362
2448
            self._new_revision_id = self._gen_revision_id()
2522
2608
    """Unescape predefined XML entities in a string of data."""
2523
2609
    global _unescape_re
2524
2610
    if _unescape_re is None:
2525
 
        _unescape_re = re.compile('\&([^;]*);')
 
2611
        _unescape_re = re.compile('\&([^;]*);')
2526
2612
    return _unescape_re.sub(_unescaper, data)