/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 breezy/repository.py

  • Committer: Jelmer Vernooij
  • Date: 2018-02-18 21:42:57 UTC
  • mto: This revision was merged to the branch mainline in revision 6859.
  • Revision ID: jelmer@jelmer.uk-20180218214257-jpevutp1wa30tz3v
Update TODO to reference Breezy, not Bazaar.

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
 
18
 
17
19
from .lazy_import import lazy_import
18
20
lazy_import(globals(), """
 
21
import itertools
19
22
import time
20
23
 
21
24
from breezy import (
22
25
    config,
23
26
    controldir,
24
27
    debug,
 
28
    generate_ids,
25
29
    graph,
26
30
    osutils,
27
31
    revision as _mod_revision,
 
32
    testament as _mod_testament,
28
33
    gpg,
29
34
    )
 
35
from breezy.bundle import serializer
30
36
from breezy.i18n import gettext
31
37
""")
32
38
 
38
44
from .decorators import only_raises
39
45
from .inter import InterObject
40
46
from .lock import _RelockDebugMixin, LogicalLockResult
 
47
from .sixish import (
 
48
    text_type,
 
49
    viewitems,
 
50
    viewvalues,
 
51
    )
41
52
from .trace import (
42
53
    log_exception_quietly, note, mutter, mutter_callsite, warning)
43
54
 
59
70
    _fmt = "Repository format does not support setting revision ids."
60
71
 
61
72
 
62
 
class FetchResult(object):
63
 
    """Result of a fetch operation.
64
 
 
65
 
    :ivar revidmap: For lossy fetches, map from source revid to target revid.
66
 
    :ivar total_fetched: Number of revisions fetched
67
 
    """
68
 
 
69
 
    def __init__(self, total_fetched=None, revidmap=None):
70
 
        self.total_fetched = total_fetched
71
 
        self.revidmap = revidmap
72
 
 
73
 
 
74
73
class CommitBuilder(object):
75
74
    """Provides an interface to build up a commit.
76
75
 
104
103
 
105
104
        if committer is None:
106
105
            self._committer = self._config_stack.get('email')
107
 
        elif not isinstance(committer, str):
108
 
            self._committer = committer.decode()  # throw if non-ascii
 
106
        elif not isinstance(committer, text_type):
 
107
            self._committer = committer.decode() # throw if non-ascii
109
108
        else:
110
109
            self._committer = committer
111
110
 
141
140
 
142
141
    def _validate_unicode_text(self, text, context):
143
142
        """Verify things like commit messages don't have bogus characters."""
144
 
        # TODO(jelmer): Make this repository-format specific
145
 
        if u'\r' in text:
 
143
        if '\r' in text:
146
144
            raise ValueError('Invalid value for %s: %r' % (context, text))
147
145
 
148
146
    def _validate_revprops(self, revprops):
149
 
        for key, value in revprops.items():
 
147
        for key, value in viewitems(revprops):
150
148
            # We know that the XML serializers do not round trip '\r'
151
149
            # correctly, so refuse to accept them
152
 
            if not isinstance(value, str):
 
150
            if not isinstance(value, (text_type, str)):
153
151
                raise ValueError('revision property (%s) is not a valid'
154
152
                                 ' (unicode) string: %r' % (key, value))
155
 
            # TODO(jelmer): Make this repository-format specific
156
153
            self._validate_unicode_text(value,
157
154
                                        'revision property (%s)' % (key,))
158
155
 
187
184
        """
188
185
        raise NotImplementedError(self.finish_inventory)
189
186
 
 
187
    def _gen_revision_id(self):
 
188
        """Return new revision-id."""
 
189
        return generate_ids.gen_revision_id(self._committer, self._timestamp)
 
190
 
190
191
    def _generate_revision_if_needed(self, revision_id):
191
192
        """Create a revision id if None was supplied.
192
193
 
218
219
            to basis_revision_id. The iterator must not include any items with
219
220
            a current kind of None - missing items must be either filtered out
220
221
            or errored-on beefore record_iter_changes sees the item.
221
 
        :return: A generator of (relpath, fs_hash) tuples for use with
 
222
        :return: A generator of (file_id, relpath, fs_hash) tuples for use with
222
223
            tree._observed_sha1.
223
224
        """
224
225
        raise NotImplementedError(self.record_iter_changes)
238
239
 
239
240
    def __repr__(self):
240
241
        return "RepositoryWriteLockResult(%s, %s)" % (self.repository_token,
241
 
                                                      self.unlock)
242
 
 
243
 
 
244
 
class WriteGroup(object):
245
 
    """Context manager that manages a write group.
246
 
 
247
 
    Raising an exception will result in the write group being aborted.
248
 
    """
249
 
 
250
 
    def __init__(self, repository, suppress_errors=False):
251
 
        self.repository = repository
252
 
        self._suppress_errors = suppress_errors
253
 
 
254
 
    def __enter__(self):
255
 
        self.repository.start_write_group()
256
 
        return self
257
 
 
258
 
    def __exit__(self, exc_type, exc_val, exc_tb):
259
 
        if exc_type:
260
 
            self.repository.abort_write_group(self._suppress_errors)
261
 
            return False
262
 
        else:
263
 
            self.repository.commit_write_group()
 
242
            self.unlock)
264
243
 
265
244
 
266
245
######################################################################
278
257
    base class for most Bazaar repositories.
279
258
    """
280
259
 
281
 
    # Does this repository implementation support random access to
282
 
    # items in the tree, or just bulk fetching/pushing of data?
283
 
    supports_random_access = True
284
 
 
285
260
    def abort_write_group(self, suppress_errors=False):
286
261
        """Commit the contents accrued within the current write group.
287
262
 
295
270
            # has an unlock or relock occured ?
296
271
            if suppress_errors:
297
272
                mutter(
298
 
                    '(suppressed) mismatched lock context and write group. %r, %r',
299
 
                    self._write_group, self.get_transaction())
 
273
                '(suppressed) mismatched lock context and write group. %r, %r',
 
274
                self._write_group, self.get_transaction())
300
275
                return
301
276
            raise errors.BzrError(
302
277
                'mismatched lock context and write group. %r, %r' %
542
517
                if committers:
543
518
                    all_committers = set()
544
519
                revisions = [r for (r, p) in graph.iter_ancestry([revid])
545
 
                             if r != _mod_revision.NULL_REVISION]
 
520
                            if r != _mod_revision.NULL_REVISION]
546
521
                last_revision = None
547
522
                if not committers:
548
523
                    # ignore the revisions in the middle - just grab first and last
556
531
                if committers:
557
532
                    result['committers'] = len(all_committers)
558
533
                result['firstrev'] = (first_revision.timestamp,
559
 
                                      first_revision.timezone)
 
534
                    first_revision.timezone)
560
535
                result['latestrev'] = (last_revision.timestamp,
561
 
                                       last_revision.timezone)
 
536
                    last_revision.timezone)
562
537
            return result
563
538
 
564
539
    def find_branches(self, using=False):
569
544
        :param using: If True, list only branches using this repository.
570
545
        """
571
546
        if using and not self.is_shared():
572
 
            for branch in self.controldir.list_branches():
573
 
                yield branch
574
 
            return
575
 
 
 
547
            return self.controldir.list_branches()
576
548
        class Evaluator(object):
577
549
 
578
550
            def __init__(self):
592
564
                value = (controldir.list_branches(), None)
593
565
                return True, value
594
566
 
 
567
        ret = []
595
568
        for branches, repository in controldir.ControlDir.find_controldirs(
596
569
                self.user_transport, evaluate=Evaluator()):
597
570
            if branches is not None:
598
 
                for branch in branches:
599
 
                    yield branch
 
571
                ret.extend(branches)
600
572
            if not using and repository is not None:
601
 
                for branch in repository.find_branches():
602
 
                    yield branch
 
573
                ret.extend(repository.find_branches())
 
574
        return ret
603
575
 
604
576
    def search_missing_revision_ids(self, other,
605
 
                                    find_ghosts=True, revision_ids=None, if_present_ids=None,
606
 
                                    limit=None):
 
577
            find_ghosts=True, revision_ids=None, if_present_ids=None,
 
578
            limit=None):
607
579
        """Return the revision ids that other has that this does not.
608
580
 
609
581
        These are returned in topological order.
637
609
        """Commit the contents accrued within the current write group.
638
610
 
639
611
        :seealso: start_write_group.
640
 
 
 
612
        
641
613
        :return: it may return an opaque hint that can be passed to 'pack'.
642
614
        """
643
615
        if self._write_group is not self.get_transaction():
644
616
            # has an unlock or relock occured ?
645
617
            raise errors.BzrError('mismatched lock context %r and '
646
 
                                  'write group %r.' %
647
 
                                  (self.get_transaction(), self._write_group))
 
618
                'write group %r.' %
 
619
                (self.get_transaction(), self._write_group))
648
620
        result = self._commit_write_group()
649
621
        self._write_group = None
650
622
        return result
692
664
    def _resume_write_group(self, tokens):
693
665
        raise errors.UnsuspendableWriteGroup(self)
694
666
 
695
 
    def fetch(self, source, revision_id=None, find_ghosts=False, lossy=False):
 
667
    def fetch(self, source, revision_id=None, find_ghosts=False):
696
668
        """Fetch the content required to construct revision_id from source.
697
669
 
698
670
        If revision_id is None, then all content is copied.
707
679
        :param revision_id: If specified, all the content needed for this
708
680
            revision ID will be copied to the target.  Fetch will determine for
709
681
            itself which content needs to be copied.
710
 
        :return: A FetchResult object
711
682
        """
712
683
        if self.is_in_write_group():
713
684
            raise errors.InternalBzrError(
716
687
        # TODO: lift out to somewhere common with RemoteRepository
717
688
        # <https://bugs.launchpad.net/bzr/+bug/401646>
718
689
        if (self.has_same_location(source)
719
 
                and self._has_same_fallbacks(source)):
 
690
            and self._has_same_fallbacks(source)):
720
691
            # check that last_revision is in 'from' and then return a
721
692
            # no-operation.
722
693
            if (revision_id is not None and
723
 
                    not _mod_revision.is_null(revision_id)):
 
694
                not _mod_revision.is_null(revision_id)):
724
695
                self.get_revision(revision_id)
725
696
            return 0, []
726
697
        inter = InterRepository.get(source, self)
727
 
        return inter.fetch(
728
 
            revision_id=revision_id, find_ghosts=find_ghosts, lossy=lossy)
 
698
        return inter.fetch(revision_id=revision_id, find_ghosts=find_ghosts)
 
699
 
 
700
    def create_bundle(self, target, base, fileobj, format=None):
 
701
        return serializer.write_bundle(self, target, base, fileobj, format)
729
702
 
730
703
    def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
731
704
                           timezone=None, committer=None, revprops=None,
825
798
            # created, but on some old all-in-one formats it's not needed
826
799
            try:
827
800
                dest_repo = self._format.initialize(
828
 
                    a_controldir, shared=shared)
 
801
                        a_controldir, shared=shared)
829
802
            except errors.UninitializableFormat:
830
803
                dest_repo = a_controldir.open_repository()
831
804
        return dest_repo
884
857
        """
885
858
        raise NotImplementedError(self.iter_revisions)
886
859
 
887
 
    def get_revision_delta(self, revision_id):
 
860
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
 
861
        """Produce a generator of revision deltas.
 
862
 
 
863
        Note that the input is a sequence of REVISIONS, not revision_ids.
 
864
        Trees will be held in memory until the generator exits.
 
865
        Each delta is relative to the revision's lefthand predecessor.
 
866
 
 
867
        :param specific_fileids: if not None, the result is filtered
 
868
          so that only those file-ids, their parents and their
 
869
          children are included.
 
870
        """
 
871
        raise NotImplementedError(self.get_deltas_for_revisions)
 
872
 
 
873
    def get_revision_delta(self, revision_id, specific_fileids=None):
888
874
        """Return the delta for one revision.
889
875
 
890
876
        The delta is relative to the left-hand predecessor of the
891
877
        revision.
 
878
 
 
879
        :param specific_fileids: if not None, the result is filtered
 
880
          so that only those file-ids, their parents and their
 
881
          children are included.
892
882
        """
893
883
        with self.lock_read():
894
884
            r = self.get_revision(revision_id)
895
 
            return list(self.get_revision_deltas([r]))[0]
896
 
 
897
 
    def get_revision_deltas(self, revisions, specific_files=None):
898
 
        """Produce a generator of revision deltas.
899
 
 
900
 
        Note that the input is a sequence of REVISIONS, not revision ids.
901
 
        Trees will be held in memory until the generator exits.
902
 
        Each delta is relative to the revision's lefthand predecessor.
903
 
 
904
 
        specific_files should exist in the first revision.
905
 
 
906
 
        :param specific_files: if not None, the result is filtered
907
 
          so that only those files, their parents and their
908
 
          children are included.
909
 
        """
910
 
        from .tree import InterTree
911
 
        # Get the revision-ids of interest
912
 
        required_trees = set()
913
 
        for revision in revisions:
914
 
            required_trees.add(revision.revision_id)
915
 
            required_trees.update(revision.parent_ids[:1])
916
 
 
917
 
        trees = {
918
 
            t.get_revision_id(): t
919
 
            for t in self.revision_trees(required_trees)}
920
 
 
921
 
        # Calculate the deltas
922
 
        for revision in revisions:
923
 
            if not revision.parent_ids:
924
 
                old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
925
 
            else:
926
 
                old_tree = trees[revision.parent_ids[0]]
927
 
            intertree = InterTree.get(old_tree, trees[revision.revision_id])
928
 
            yield intertree.compare(specific_files=specific_files)
929
 
            if specific_files is not None:
930
 
                specific_files = [
931
 
                    p for p in intertree.find_source_paths(
932
 
                        specific_files).values()
933
 
                    if p is not None]
 
885
            return list(self.get_deltas_for_revisions(
 
886
                [r], specific_fileids=specific_fileids))[0]
934
887
 
935
888
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
936
 
        raise NotImplementedError(self.store_revision_signature)
 
889
        with self.lock_write():
 
890
            signature = gpg_strategy.sign(plaintext)
 
891
            self.add_signature_text(revision_id, signature)
937
892
 
938
893
    def add_signature_text(self, revision_id, signature):
939
894
        """Store a signature text for a revision.
971
926
        partial_history = [known_revid]
972
927
        distance_from_known = known_revno - revno
973
928
        if distance_from_known < 0:
974
 
            raise errors.RevnoOutOfBounds(revno, (0, known_revno))
 
929
            raise ValueError(
 
930
                'requested revno (%d) is later than given known revno (%d)'
 
931
                % (revno, known_revno))
975
932
        try:
976
933
            _iter_for_revno(
977
934
                self, partial_history, stop_index=distance_from_known)
978
935
        except errors.RevisionNotPresent as err:
979
936
            if err.revision_id == known_revid:
980
937
                # The start revision (known_revid) wasn't found.
981
 
                raise errors.NoSuchRevision(self, known_revid)
 
938
                raise
982
939
            # This is a stacked repository with no fallbacks, or a there's a
983
940
            # left-hand ghost.  Either way, even though the revision named in
984
941
            # the error isn't in this repo, we know it's the next step in this
998
955
 
999
956
    def reconcile(self, other=None, thorough=False):
1000
957
        """Reconcile this repository."""
1001
 
        raise NotImplementedError(self.reconcile)
 
958
        from .reconcile import RepoReconciler
 
959
        with self.lock_write():
 
960
            reconciler = RepoReconciler(self, thorough=thorough)
 
961
            reconciler.reconcile()
 
962
            return reconciler
1002
963
 
1003
964
    def _refresh_data(self):
1004
965
        """Helper called from lock_* to ensure coherency with disk.
1025
986
        """Return Trees for revisions in this repository.
1026
987
 
1027
988
        :param revision_ids: a sequence of revision-ids;
1028
 
          a revision-id may not be None or b'null:'
 
989
          a revision-id may not be None or 'null:'
1029
990
        """
1030
991
        raise NotImplementedError(self.revision_trees)
1031
992
 
1071
1032
            else:
1072
1033
                query_keys.append((revision_id,))
1073
1034
        vf = self.revisions.without_fallbacks()
1074
 
        for (revision_id,), parent_keys in (
1075
 
                vf.get_parent_map(query_keys).items()):
 
1035
        for (revision_id,), parent_keys in viewitems(
 
1036
                vf.get_parent_map(query_keys)):
1076
1037
            if parent_keys:
1077
1038
                result[revision_id] = tuple([parent_revid
1078
 
                                             for (parent_revid,) in parent_keys])
 
1039
                    for (parent_revid,) in parent_keys])
1079
1040
            else:
1080
1041
                result[revision_id] = (_mod_revision.NULL_REVISION,)
1081
1042
        return result
1104
1065
        """Return the graph walker for this repository format"""
1105
1066
        parents_provider = self._make_parents_provider()
1106
1067
        if (other_repository is not None and
1107
 
                not self.has_same_location(other_repository)):
 
1068
            not self.has_same_location(other_repository)):
1108
1069
            parents_provider = graph.StackedParentsProvider(
1109
1070
                [parents_provider, other_repository._make_parents_provider()])
1110
1071
        return graph.Graph(parents_provider)
1125
1086
        raise NotImplementedError(self.make_working_trees)
1126
1087
 
1127
1088
    def sign_revision(self, revision_id, gpg_strategy):
1128
 
        raise NotImplementedError(self.sign_revision)
 
1089
        with self.lock_write():
 
1090
            testament = _mod_testament.Testament.from_revision(self, revision_id)
 
1091
            plaintext = testament.as_short_text()
 
1092
            self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1129
1093
 
1130
1094
    def verify_revision_signature(self, revision_id, gpg_strategy):
1131
1095
        """Verify the signature on a revision.
1135
1099
 
1136
1100
        :return: gpg.SIGNATURE_VALID or a failed SIGNATURE_ value
1137
1101
        """
1138
 
        raise NotImplementedError(self.verify_revision_signature)
 
1102
        with self.lock_read():
 
1103
            if not self.has_signature_for_revision_id(revision_id):
 
1104
                return gpg.SIGNATURE_NOT_SIGNED, None
 
1105
            signature = self.get_signature_text(revision_id)
 
1106
 
 
1107
            testament = _mod_testament.Testament.from_revision(
 
1108
                    self, revision_id)
 
1109
            plaintext = testament.as_short_text()
 
1110
 
 
1111
            return gpg_strategy.verify(signature, plaintext)
1139
1112
 
1140
1113
    def verify_revision_signatures(self, revision_ids, gpg_strategy):
1141
1114
        """Verify revision signatures for a number of revisions.
1167
1140
        :param callback_refs: A dict of check-refs to resolve and callback
1168
1141
            the check/_check method on the items listed as wanting the ref.
1169
1142
            see breezy.check.
1170
 
        :param check_repo: If False do not check the repository contents, just
 
1143
        :param check_repo: If False do not check the repository contents, just 
1171
1144
            calculate the data callback_refs requires and call them back.
1172
1145
        """
1173
1146
        return self._check(revision_ids=revision_ids, callback_refs=callback_refs,
1174
 
                           check_repo=check_repo)
 
1147
            check_repo=check_repo)
1175
1148
 
1176
1149
    def _check(self, revision_ids=None, callback_refs=None, check_repo=True):
1177
1150
        raise NotImplementedError(self.check)
1203
1176
        # weave repositories refuse to store revisionids that are non-ascii.
1204
1177
        if revision_id is not None:
1205
1178
            # weaves require ascii revision ids.
1206
 
            if isinstance(revision_id, str):
 
1179
            if isinstance(revision_id, unicode):
1207
1180
                try:
1208
1181
                    revision_id.encode('ascii')
1209
1182
                except UnicodeEncodeError:
1329
1302
    supports_overriding_transport = True
1330
1303
    # Does the format support setting custom revision properties?
1331
1304
    supports_custom_revision_properties = True
1332
 
    # Does the format record per-file revision metadata?
1333
 
    records_per_file_revision = True
1334
1305
 
1335
1306
    def __repr__(self):
1336
1307
        return "%s()" % self.__class__.__name__
1390
1361
            raise errors.BadConversionTarget(
1391
1362
                'Does not support rich root data.', target_format,
1392
1363
                from_format=self)
1393
 
        if (self.supports_tree_reference
1394
 
                and not getattr(target_format, 'supports_tree_reference', False)):
 
1364
        if (self.supports_tree_reference and 
 
1365
            not getattr(target_format, 'supports_tree_reference', False)):
1395
1366
            raise errors.BadConversionTarget(
1396
1367
                'Does not support nested trees', target_format,
1397
1368
                from_format=self)
1420
1391
# the repository is not separately opened are similar.
1421
1392
 
1422
1393
format_registry.register_lazy(
1423
 
    b'Bazaar-NG Knit Repository Format 1',
 
1394
    'Bazaar-NG Knit Repository Format 1',
1424
1395
    'breezy.bzr.knitrepo',
1425
1396
    'RepositoryFormatKnit1',
1426
1397
    )
1427
1398
 
1428
1399
format_registry.register_lazy(
1429
 
    b'Bazaar Knit Repository Format 3 (bzr 0.15)\n',
 
1400
    'Bazaar Knit Repository Format 3 (bzr 0.15)\n',
1430
1401
    'breezy.bzr.knitrepo',
1431
1402
    'RepositoryFormatKnit3',
1432
1403
    )
1433
1404
 
1434
1405
format_registry.register_lazy(
1435
 
    b'Bazaar Knit Repository Format 4 (bzr 1.0)\n',
 
1406
    'Bazaar Knit Repository Format 4 (bzr 1.0)\n',
1436
1407
    'breezy.bzr.knitrepo',
1437
1408
    'RepositoryFormatKnit4',
1438
1409
    )
1441
1412
# post-subtrees to allow ease of testing.
1442
1413
# NOTE: These are experimental in 0.92. Stable in 1.0 and above
1443
1414
format_registry.register_lazy(
1444
 
    b'Bazaar pack repository format 1 (needs bzr 0.92)\n',
 
1415
    'Bazaar pack repository format 1 (needs bzr 0.92)\n',
1445
1416
    'breezy.bzr.knitpack_repo',
1446
1417
    'RepositoryFormatKnitPack1',
1447
1418
    )
1448
1419
format_registry.register_lazy(
1449
 
    b'Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n',
 
1420
    'Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n',
1450
1421
    'breezy.bzr.knitpack_repo',
1451
1422
    'RepositoryFormatKnitPack3',
1452
1423
    )
1453
1424
format_registry.register_lazy(
1454
 
    b'Bazaar pack repository format 1 with rich root (needs bzr 1.0)\n',
 
1425
    'Bazaar pack repository format 1 with rich root (needs bzr 1.0)\n',
1455
1426
    'breezy.bzr.knitpack_repo',
1456
1427
    'RepositoryFormatKnitPack4',
1457
1428
    )
1458
1429
format_registry.register_lazy(
1459
 
    b'Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n',
 
1430
    'Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n',
1460
1431
    'breezy.bzr.knitpack_repo',
1461
1432
    'RepositoryFormatKnitPack5',
1462
1433
    )
1463
1434
format_registry.register_lazy(
1464
 
    b'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n',
 
1435
    'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n',
1465
1436
    'breezy.bzr.knitpack_repo',
1466
1437
    'RepositoryFormatKnitPack5RichRoot',
1467
1438
    )
1468
1439
format_registry.register_lazy(
1469
 
    b'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n',
 
1440
    'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n',
1470
1441
    'breezy.bzr.knitpack_repo',
1471
1442
    'RepositoryFormatKnitPack5RichRootBroken',
1472
1443
    )
1473
1444
format_registry.register_lazy(
1474
 
    b'Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n',
 
1445
    'Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n',
1475
1446
    'breezy.bzr.knitpack_repo',
1476
1447
    'RepositoryFormatKnitPack6',
1477
1448
    )
1478
1449
format_registry.register_lazy(
1479
 
    b'Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n',
 
1450
    'Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n',
1480
1451
    'breezy.bzr.knitpack_repo',
1481
1452
    'RepositoryFormatKnitPack6RichRoot',
1482
1453
    )
1483
1454
format_registry.register_lazy(
1484
 
    b'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
1455
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
1485
1456
    'breezy.bzr.groupcompress_repo',
1486
1457
    'RepositoryFormat2a',
1487
1458
    )
1489
1460
# Development formats.
1490
1461
# Check their docstrings to see if/when they are obsolete.
1491
1462
format_registry.register_lazy(
1492
 
    (b"Bazaar development format 2 with subtree support "
1493
 
        b"(needs bzr.dev from before 1.8)\n"),
 
1463
    ("Bazaar development format 2 with subtree support "
 
1464
        "(needs bzr.dev from before 1.8)\n"),
1494
1465
    'breezy.bzr.knitpack_repo',
1495
1466
    'RepositoryFormatPackDevelopment2Subtree',
1496
1467
    )
1497
1468
format_registry.register_lazy(
1498
 
    b'Bazaar development format 8\n',
 
1469
    'Bazaar development format 8\n',
1499
1470
    'breezy.bzr.groupcompress_repo',
1500
1471
    'RepositoryFormat2aSubtree',
1501
1472
    )
1529
1500
            try:
1530
1501
                self.target.set_make_working_trees(
1531
1502
                    self.source.make_working_trees())
1532
 
            except (NotImplementedError, errors.RepositoryUpgradeRequired):
 
1503
            except NotImplementedError:
1533
1504
                pass
1534
1505
            self.target.fetch(self.source, revision_id=revision_id)
1535
1506
 
1536
 
    def fetch(self, revision_id=None, find_ghosts=False, lossy=False):
 
1507
    def fetch(self, revision_id=None, find_ghosts=False):
1537
1508
        """Fetch the content required to construct revision_id.
1538
1509
 
1539
1510
        The content is copied from self.source to self.target.
1540
1511
 
1541
1512
        :param revision_id: if None all content is copied, if NULL_REVISION no
1542
1513
                            content is copied.
1543
 
        :return: FetchResult
 
1514
        :return: None.
1544
1515
        """
1545
1516
        raise NotImplementedError(self.fetch)
1546
1517
 
1583
1554
        """
1584
1555
        if source.supports_rich_root() != target.supports_rich_root():
1585
1556
            raise errors.IncompatibleRepositories(source, target,
1586
 
                                                  "different rich-root support")
 
1557
                "different rich-root support")
1587
1558
        if source._serializer != target._serializer:
1588
1559
            raise errors.IncompatibleRepositories(source, target,
1589
 
                                                  "different serializers")
 
1560
                "different serializers")
1590
1561
 
1591
1562
 
1592
1563
class CopyConverter(object):
1608
1579
        :param to_convert: The disk object to convert.
1609
1580
        :param pb: a progress bar to use for progress information.
1610
1581
        """
1611
 
        with ui.ui_factory.nested_progress_bar() as pb:
1612
 
            self.count = 0
1613
 
            self.total = 4
1614
 
            # this is only useful with metadir layouts - separated repo content.
1615
 
            # trigger an assertion if not such
1616
 
            repo._format.get_format_string()
1617
 
            self.repo_dir = repo.controldir
1618
 
            pb.update(gettext('Moving repository to repository.backup'))
1619
 
            self.repo_dir.transport.move('repository', 'repository.backup')
1620
 
            backup_transport = self.repo_dir.transport.clone(
1621
 
                'repository.backup')
1622
 
            repo._format.check_conversion_target(self.target_format)
1623
 
            self.source_repo = repo._format.open(self.repo_dir,
1624
 
                                                 _found=True,
1625
 
                                                 _override_transport=backup_transport)
1626
 
            pb.update(gettext('Creating new repository'))
1627
 
            converted = self.target_format.initialize(self.repo_dir,
1628
 
                                                      self.source_repo.is_shared())
1629
 
            with converted.lock_write():
1630
 
                pb.update(gettext('Copying content'))
1631
 
                self.source_repo.copy_content_into(converted)
1632
 
            pb.update(gettext('Deleting old repository content'))
1633
 
            self.repo_dir.transport.delete_tree('repository.backup')
1634
 
            ui.ui_factory.note(gettext('repository converted'))
 
1582
        pb = ui.ui_factory.nested_progress_bar()
 
1583
        self.count = 0
 
1584
        self.total = 4
 
1585
        # this is only useful with metadir layouts - separated repo content.
 
1586
        # trigger an assertion if not such
 
1587
        repo._format.get_format_string()
 
1588
        self.repo_dir = repo.controldir
 
1589
        pb.update(gettext('Moving repository to repository.backup'))
 
1590
        self.repo_dir.transport.move('repository', 'repository.backup')
 
1591
        backup_transport =  self.repo_dir.transport.clone('repository.backup')
 
1592
        repo._format.check_conversion_target(self.target_format)
 
1593
        self.source_repo = repo._format.open(self.repo_dir,
 
1594
            _found=True,
 
1595
            _override_transport=backup_transport)
 
1596
        pb.update(gettext('Creating new repository'))
 
1597
        converted = self.target_format.initialize(self.repo_dir,
 
1598
                                                  self.source_repo.is_shared())
 
1599
        converted.lock_write()
 
1600
        try:
 
1601
            pb.update(gettext('Copying content'))
 
1602
            self.source_repo.copy_content_into(converted)
 
1603
        finally:
 
1604
            converted.unlock()
 
1605
        pb.update(gettext('Deleting old repository content'))
 
1606
        self.repo_dir.transport.delete_tree('repository.backup')
 
1607
        ui.ui_factory.note(gettext('repository converted'))
 
1608
        pb.finished()
1635
1609
 
1636
1610
 
1637
1611
def _strip_NULL_ghosts(revision_graph):
1639
1613
    # Filter ghosts, and null:
1640
1614
    if _mod_revision.NULL_REVISION in revision_graph:
1641
1615
        del revision_graph[_mod_revision.NULL_REVISION]
1642
 
    for key, parents in revision_graph.items():
 
1616
    for key, parents in viewitems(revision_graph):
1643
1617
        revision_graph[key] = tuple(parent for parent in parents if parent
1644
 
                                    in revision_graph)
 
1618
            in revision_graph)
1645
1619
    return revision_graph
1646
1620
 
1647
1621
 
1662
1636
    start_revision = partial_history_cache[-1]
1663
1637
    graph = repo.get_graph()
1664
1638
    iterator = graph.iter_lefthand_ancestry(start_revision,
1665
 
                                            (_mod_revision.NULL_REVISION,))
 
1639
        (_mod_revision.NULL_REVISION,))
1666
1640
    try:
1667
1641
        # skip the last revision in the list
1668
1642
        next(iterator)
1669
1643
        while True:
1670
1644
            if (stop_index is not None and
1671
 
                    len(partial_history_cache) > stop_index):
 
1645
                len(partial_history_cache) > stop_index):
1672
1646
                break
1673
1647
            if partial_history_cache[-1] == stop_revision:
1674
1648
                break