~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Martin
  • Date: 2011-04-15 21:22:57 UTC
  • mto: This revision was merged to the branch mainline in revision 5797.
  • Revision ID: gzlist@googlemail.com-20110415212257-jgtovwwp4be7egd9
Add release notes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
16
16
 
17
17
from bzrlib.lazy_import import lazy_import
18
18
lazy_import(globals(), """
19
 
import cStringIO
20
 
import re
21
19
import time
22
20
 
23
21
from bzrlib import (
24
22
    bzrdir,
25
23
    check,
26
 
    chk_map,
27
24
    config,
28
25
    controldir,
29
26
    debug,
32
29
    generate_ids,
33
30
    gpg,
34
31
    graph,
35
 
    inventory,
36
32
    inventory_delta,
37
 
    lazy_regex,
38
33
    lockable_files,
39
34
    lockdir,
40
35
    lru_cache,
41
36
    osutils,
42
 
    pyutils,
43
37
    revision as _mod_revision,
44
38
    static_tuple,
45
 
    symbol_versioning,
46
 
    trace,
47
39
    tsort,
48
40
    versionedfile,
49
41
    )
50
42
from bzrlib.bundle import serializer
 
43
from bzrlib.recordcounter import RecordCounter
51
44
from bzrlib.revisiontree import RevisionTree
52
45
from bzrlib.store.versioned import VersionedFileStore
53
46
from bzrlib.testament import Testament
54
47
""")
55
48
 
56
 
import sys
57
49
from bzrlib import (
58
50
    errors,
59
51
    registry,
 
52
    symbol_versioning,
60
53
    ui,
61
54
    )
62
55
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
67
60
    ROOT_ID,
68
61
    entry_factory,
69
62
    )
70
 
from bzrlib.recordcounter import RecordCounter
71
63
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
72
64
from bzrlib.trace import (
73
65
    log_exception_quietly, note, mutter, mutter_callsite, warning)
96
88
    record_root_entry = True
97
89
    # the default CommitBuilder does not manage trees whose root is versioned.
98
90
    _versioned_root = False
 
91
    # this commit builder supports the record_entry_contents interface
 
92
    supports_record_entry_contents = True
99
93
 
100
94
    def __init__(self, repository, parents, config, timestamp=None,
101
95
                 timezone=None, committer=None, revprops=None,
104
98
 
105
99
        :param repository: Repository to commit to.
106
100
        :param parents: Revision ids of the parents of the new revision.
107
 
        :param config: Configuration to use.
108
101
        :param timestamp: Optional timestamp recorded for commit.
109
102
        :param timezone: Optional timezone for timestamp.
110
103
        :param committer: Optional committer to set for commit.
176
169
            self._validate_unicode_text(value,
177
170
                                        'revision property (%s)' % (key,))
178
171
 
 
172
    def _ensure_fallback_inventories(self):
 
173
        """Ensure that appropriate inventories are available.
 
174
 
 
175
        This only applies to repositories that are stacked, and is about
 
176
        enusring the stacking invariants. Namely, that for any revision that is
 
177
        present, we either have all of the file content, or we have the parent
 
178
        inventory and the delta file content.
 
179
        """
 
180
        if not self.repository._fallback_repositories:
 
181
            return
 
182
        if not self.repository._format.supports_chks:
 
183
            raise errors.BzrError("Cannot commit directly to a stacked branch"
 
184
                " in pre-2a formats. See "
 
185
                "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
 
186
        # This is a stacked repo, we need to make sure we have the parent
 
187
        # inventories for the parents.
 
188
        parent_keys = [(p,) for p in self.parents]
 
189
        parent_map = self.repository.inventories._index.get_parent_map(parent_keys)
 
190
        missing_parent_keys = set([pk for pk in parent_keys
 
191
                                       if pk not in parent_map])
 
192
        fallback_repos = list(reversed(self.repository._fallback_repositories))
 
193
        missing_keys = [('inventories', pk[0])
 
194
                        for pk in missing_parent_keys]
 
195
        resume_tokens = []
 
196
        while missing_keys and fallback_repos:
 
197
            fallback_repo = fallback_repos.pop()
 
198
            source = fallback_repo._get_source(self.repository._format)
 
199
            sink = self.repository._get_sink()
 
200
            stream = source.get_stream_for_missing_keys(missing_keys)
 
201
            missing_keys = sink.insert_stream_without_locking(stream,
 
202
                self.repository._format)
 
203
        if missing_keys:
 
204
            raise errors.BzrError('Unable to fill in parent inventories for a'
 
205
                                  ' stacked branch')
 
206
 
179
207
    def commit(self, message):
180
208
        """Make the actual commit.
181
209
 
193
221
        rev.parent_ids = self.parents
194
222
        self.repository.add_revision(self._new_revision_id, rev,
195
223
            self.new_inventory, self._config)
 
224
        self._ensure_fallback_inventories()
196
225
        self.repository.commit_write_group()
197
226
        return self._new_revision_id
198
227
 
953
982
    # in a Repository class subclass rather than to override
954
983
    # get_commit_builder.
955
984
    _commit_builder_class = CommitBuilder
956
 
    # The search regex used by xml based repositories to determine what things
957
 
    # where changed in a single commit.
958
 
    _file_ids_altered_regex = lazy_regex.lazy_compile(
959
 
        r'file_id="(?P<file_id>[^"]+)"'
960
 
        r'.* revision="(?P<revision_id>[^"]+)"'
961
 
        )
962
985
 
963
986
    def abort_write_group(self, suppress_errors=False):
964
987
        """Commit the contents accrued within the current write group.
1561
1584
        return ret
1562
1585
 
1563
1586
    @needs_read_lock
1564
 
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
 
1587
    def search_missing_revision_ids(self, other,
 
1588
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
1589
            find_ghosts=True, revision_ids=None, if_present_ids=None):
1565
1590
        """Return the revision ids that other has that this does not.
1566
1591
 
1567
1592
        These are returned in topological order.
1568
1593
 
1569
1594
        revision_id: only return revision ids included by revision_id.
1570
1595
        """
 
1596
        if symbol_versioning.deprecated_passed(revision_id):
 
1597
            symbol_versioning.warn(
 
1598
                'search_missing_revision_ids(revision_id=...) was '
 
1599
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
 
1600
                DeprecationWarning, stacklevel=3)
 
1601
            if revision_ids is not None:
 
1602
                raise AssertionError(
 
1603
                    'revision_ids is mutually exclusive with revision_id')
 
1604
            if revision_id is not None:
 
1605
                revision_ids = [revision_id]
1571
1606
        return InterRepository.get(other, self).search_missing_revision_ids(
1572
 
            revision_id, find_ghosts)
 
1607
            find_ghosts=find_ghosts, revision_ids=revision_ids,
 
1608
            if_present_ids=if_present_ids)
1573
1609
 
1574
1610
    @staticmethod
1575
1611
    def open(base):
1697
1733
    def _resume_write_group(self, tokens):
1698
1734
        raise errors.UnsuspendableWriteGroup(self)
1699
1735
 
1700
 
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
 
1736
    def fetch(self, source, revision_id=None, find_ghosts=False,
1701
1737
            fetch_spec=None):
1702
1738
        """Fetch the content required to construct revision_id from source.
1703
1739
 
1737
1773
                not _mod_revision.is_null(revision_id)):
1738
1774
                self.get_revision(revision_id)
1739
1775
            return 0, []
1740
 
        # if there is no specific appropriate InterRepository, this will get
1741
 
        # the InterRepository base class, which raises an
1742
 
        # IncompatibleRepositories when asked to fetch.
1743
1776
        inter = InterRepository.get(source, self)
1744
 
        return inter.fetch(revision_id=revision_id, pb=pb,
 
1777
        return inter.fetch(revision_id=revision_id,
1745
1778
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1746
1779
 
1747
1780
    def create_bundle(self, target, base, fileobj, format=None):
1761
1794
        :param revprops: Optional dictionary of revision properties.
1762
1795
        :param revision_id: Optional revision id.
1763
1796
        """
1764
 
        if self._fallback_repositories:
1765
 
            raise errors.BzrError("Cannot commit from a lightweight checkout "
1766
 
                "to a stacked branch. See "
 
1797
        if self._fallback_repositories and not self._format.supports_chks:
 
1798
            raise errors.BzrError("Cannot commit directly to a stacked branch"
 
1799
                " in pre-2a formats. See "
1767
1800
                "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1768
1801
        result = self._commit_builder_class(self, parents, config,
1769
1802
            timestamp, timezone, committer, revprops, revision_id)
2018
2051
        w = self.inventories
2019
2052
        pb = ui.ui_factory.nested_progress_bar()
2020
2053
        try:
2021
 
            return self._find_text_key_references_from_xml_inventory_lines(
 
2054
            return self._serializer._find_text_key_references(
2022
2055
                w.iter_lines_added_or_present_in_keys(revision_keys, pb=pb))
2023
2056
        finally:
2024
2057
            pb.finished()
2025
2058
 
2026
 
    def _find_text_key_references_from_xml_inventory_lines(self,
2027
 
        line_iterator):
2028
 
        """Core routine for extracting references to texts from inventories.
2029
 
 
2030
 
        This performs the translation of xml lines to revision ids.
2031
 
 
2032
 
        :param line_iterator: An iterator of lines, origin_version_id
2033
 
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
2034
 
            to whether they were referred to by the inventory of the
2035
 
            revision_id that they contain. Note that if that revision_id was
2036
 
            not part of the line_iterator's output then False will be given -
2037
 
            even though it may actually refer to that key.
2038
 
        """
2039
 
        if not self._serializer.support_altered_by_hack:
2040
 
            raise AssertionError(
2041
 
                "_find_text_key_references_from_xml_inventory_lines only "
2042
 
                "supported for branches which store inventory as unnested xml"
2043
 
                ", not on %r" % self)
2044
 
        result = {}
2045
 
 
2046
 
        # this code needs to read every new line in every inventory for the
2047
 
        # inventories [revision_ids]. Seeing a line twice is ok. Seeing a line
2048
 
        # not present in one of those inventories is unnecessary but not
2049
 
        # harmful because we are filtering by the revision id marker in the
2050
 
        # inventory lines : we only select file ids altered in one of those
2051
 
        # revisions. We don't need to see all lines in the inventory because
2052
 
        # only those added in an inventory in rev X can contain a revision=X
2053
 
        # line.
2054
 
        unescape_revid_cache = {}
2055
 
        unescape_fileid_cache = {}
2056
 
 
2057
 
        # jam 20061218 In a big fetch, this handles hundreds of thousands
2058
 
        # of lines, so it has had a lot of inlining and optimizing done.
2059
 
        # Sorry that it is a little bit messy.
2060
 
        # Move several functions to be local variables, since this is a long
2061
 
        # running loop.
2062
 
        search = self._file_ids_altered_regex.search
2063
 
        unescape = _unescape_xml
2064
 
        setdefault = result.setdefault
2065
 
        for line, line_key in line_iterator:
2066
 
            match = search(line)
2067
 
            if match is None:
2068
 
                continue
2069
 
            # One call to match.group() returning multiple items is quite a
2070
 
            # bit faster than 2 calls to match.group() each returning 1
2071
 
            file_id, revision_id = match.group('file_id', 'revision_id')
2072
 
 
2073
 
            # Inlining the cache lookups helps a lot when you make 170,000
2074
 
            # lines and 350k ids, versus 8.4 unique ids.
2075
 
            # Using a cache helps in 2 ways:
2076
 
            #   1) Avoids unnecessary decoding calls
2077
 
            #   2) Re-uses cached strings, which helps in future set and
2078
 
            #      equality checks.
2079
 
            # (2) is enough that removing encoding entirely along with
2080
 
            # the cache (so we are using plain strings) results in no
2081
 
            # performance improvement.
2082
 
            try:
2083
 
                revision_id = unescape_revid_cache[revision_id]
2084
 
            except KeyError:
2085
 
                unescaped = unescape(revision_id)
2086
 
                unescape_revid_cache[revision_id] = unescaped
2087
 
                revision_id = unescaped
2088
 
 
2089
 
            # Note that unconditionally unescaping means that we deserialise
2090
 
            # every fileid, which for general 'pull' is not great, but we don't
2091
 
            # really want to have some many fulltexts that this matters anyway.
2092
 
            # RBC 20071114.
2093
 
            try:
2094
 
                file_id = unescape_fileid_cache[file_id]
2095
 
            except KeyError:
2096
 
                unescaped = unescape(file_id)
2097
 
                unescape_fileid_cache[file_id] = unescaped
2098
 
                file_id = unescaped
2099
 
 
2100
 
            key = (file_id, revision_id)
2101
 
            setdefault(key, False)
2102
 
            if revision_id == line_key[-1]:
2103
 
                result[key] = True
2104
 
        return result
2105
 
 
2106
2059
    def _inventory_xml_lines_for_keys(self, keys):
2107
2060
        """Get a line iterator of the sort needed for findind references.
2108
2061
 
2138
2091
        revision_ids. Each altered file-ids has the exact revision_ids that
2139
2092
        altered it listed explicitly.
2140
2093
        """
2141
 
        seen = set(self._find_text_key_references_from_xml_inventory_lines(
 
2094
        seen = set(self._serializer._find_text_key_references(
2142
2095
                line_iterator).iterkeys())
2143
2096
        parent_keys = self._find_parent_keys_of_revisions(revision_keys)
2144
 
        parent_seen = set(self._find_text_key_references_from_xml_inventory_lines(
 
2097
        parent_seen = set(self._serializer._find_text_key_references(
2145
2098
            self._inventory_xml_lines_for_keys(parent_keys)))
2146
2099
        new_keys = seen - parent_seen
2147
2100
        result = {}
2775
2728
        return result
2776
2729
 
2777
2730
    def _warn_if_deprecated(self, branch=None):
 
2731
        if not self._format.is_deprecated():
 
2732
            return
2778
2733
        global _deprecation_warning_done
2779
2734
        if _deprecation_warning_done:
2780
2735
            return
2810
2765
                except UnicodeDecodeError:
2811
2766
                    raise errors.NonAsciiRevisionId(method, self)
2812
2767
 
2813
 
    def revision_graph_can_have_wrong_parents(self):
2814
 
        """Is it possible for this repository to have a revision graph with
2815
 
        incorrect parents?
2816
 
 
2817
 
        If True, then this repository must also implement
2818
 
        _find_inconsistent_revision_parents so that check and reconcile can
2819
 
        check for inconsistencies before proceeding with other checks that may
2820
 
        depend on the revision index being consistent.
2821
 
        """
2822
 
        raise NotImplementedError(self.revision_graph_can_have_wrong_parents)
2823
 
 
2824
 
 
2825
 
# remove these delegates a while after bzr 0.15
2826
 
def __make_delegated(name, from_module):
2827
 
    def _deprecated_repository_forwarder():
2828
 
        symbol_versioning.warn('%s moved to %s in bzr 0.15'
2829
 
            % (name, from_module),
2830
 
            DeprecationWarning,
2831
 
            stacklevel=2)
2832
 
        try:
2833
 
            return pyutils.get_named_object(from_module, name)
2834
 
        except AttributeError:
2835
 
            raise AttributeError('module %s has no name %s'
2836
 
                    % (sys.modules[from_module], name))
2837
 
    globals()[name] = _deprecated_repository_forwarder
2838
 
 
2839
 
for _name in [
2840
 
        'AllInOneRepository',
2841
 
        'WeaveMetaDirRepository',
2842
 
        'PreSplitOutRepositoryFormat',
2843
 
        'RepositoryFormat4',
2844
 
        'RepositoryFormat5',
2845
 
        'RepositoryFormat6',
2846
 
        'RepositoryFormat7',
2847
 
        ]:
2848
 
    __make_delegated(_name, 'bzrlib.repofmt.weaverepo')
2849
 
 
2850
 
for _name in [
2851
 
        'KnitRepository',
2852
 
        'RepositoryFormatKnit',
2853
 
        'RepositoryFormatKnit1',
2854
 
        ]:
2855
 
    __make_delegated(_name, 'bzrlib.repofmt.knitrepo')
2856
 
 
2857
2768
 
2858
2769
def install_revision(repository, rev, revision_tree):
2859
2770
    """Install all revision data into a repository."""
2992
2903
            control_files)
2993
2904
 
2994
2905
 
 
2906
class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
 
2907
    """Repository format registry."""
 
2908
 
 
2909
    def get_default(self):
 
2910
        """Return the current default format."""
 
2911
        from bzrlib import bzrdir
 
2912
        return bzrdir.format_registry.make_bzrdir('default').repository_format
 
2913
 
 
2914
 
2995
2915
network_format_registry = registry.FormatRegistry()
2996
2916
"""Registry of formats indexed by their network name.
2997
2917
 
3001
2921
"""
3002
2922
 
3003
2923
 
3004
 
format_registry = registry.FormatRegistry(network_format_registry)
 
2924
format_registry = RepositoryFormatRegistry(network_format_registry)
3005
2925
"""Registry of formats, indexed by their BzrDirMetaFormat format string.
3006
2926
 
3007
2927
This can contain either format instances themselves, or classes/factories that
3012
2932
#####################################################################
3013
2933
# Repository Formats
3014
2934
 
3015
 
class RepositoryFormat(object):
 
2935
class RepositoryFormat(controldir.ControlComponentFormat):
3016
2936
    """A repository format.
3017
2937
 
3018
2938
    Formats provide four things:
3079
2999
    supports_tree_reference = None
3080
3000
    # Is the format experimental ?
3081
3001
    experimental = False
 
3002
    # Does this repository format escape funky characters, or does it create files with
 
3003
    # similar names as the versioned files in its contents on disk ?
 
3004
    supports_funky_characters = None
 
3005
    # Does this repository format support leaving locks?
 
3006
    supports_leaving_lock = None
 
3007
    # Does this format support the full VersionedFiles interface?
 
3008
    supports_full_versioned_files = None
 
3009
    # Does this format support signing revision signatures?
 
3010
    supports_revision_signatures = True
 
3011
    # Can the revision graph have incorrect parents?
 
3012
    revision_graph_can_have_wrong_parents = None
3082
3013
 
3083
3014
    def __repr__(self):
3084
3015
        return "%s()" % self.__class__.__name__
3109
3040
                                            kind='repository')
3110
3041
 
3111
3042
    @classmethod
 
3043
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
3112
3044
    def register_format(klass, format):
3113
 
        format_registry.register(format.get_format_string(), format)
 
3045
        format_registry.register(format)
3114
3046
 
3115
3047
    @classmethod
 
3048
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
3116
3049
    def unregister_format(klass, format):
3117
 
        format_registry.remove(format.get_format_string())
 
3050
        format_registry.remove(format)
3118
3051
 
3119
3052
    @classmethod
 
3053
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
3120
3054
    def get_default_format(klass):
3121
3055
        """Return the current default format."""
3122
 
        from bzrlib import bzrdir
3123
 
        return bzrdir.format_registry.make_bzrdir('default').repository_format
 
3056
        return format_registry.get_default()
3124
3057
 
3125
3058
    def get_format_string(self):
3126
3059
        """Return the ASCII format string that identifies this format.
3177
3110
        """
3178
3111
        return True
3179
3112
 
 
3113
    def is_deprecated(self):
 
3114
        """Is this format deprecated?
 
3115
 
 
3116
        Deprecated formats may trigger a user-visible warning recommending
 
3117
        the user to upgrade. They are still fully supported.
 
3118
        """
 
3119
        return False
 
3120
 
3180
3121
    def network_name(self):
3181
3122
        """A simple byte string uniquely identifying this format for RPC calls.
3182
3123
 
3221
3162
    rich_root_data = False
3222
3163
    supports_tree_reference = False
3223
3164
    supports_external_lookups = False
 
3165
    supports_leaving_lock = True
3224
3166
 
3225
3167
    @property
3226
3168
    def _matchingbzrdir(self):
3264
3206
        return self.get_format_string()
3265
3207
 
3266
3208
 
3267
 
# Pre-0.8 formats that don't have a disk format string (because they are
3268
 
# versioned by the matching control directory). We use the control directories
3269
 
# disk format string as a key for the network_name because they meet the
3270
 
# constraints (simple string, unique, immutable).
3271
 
network_format_registry.register_lazy(
3272
 
    "Bazaar-NG branch, format 5\n",
3273
 
    'bzrlib.repofmt.weaverepo',
3274
 
    'RepositoryFormat5',
3275
 
)
3276
 
network_format_registry.register_lazy(
3277
 
    "Bazaar-NG branch, format 6\n",
3278
 
    'bzrlib.repofmt.weaverepo',
3279
 
    'RepositoryFormat6',
3280
 
)
3281
 
 
3282
3209
# formats which have no format string are not discoverable or independently
3283
3210
# creatable on disk, so are not registered in format_registry.  They're
3284
 
# all in bzrlib.repofmt.weaverepo now.  When an instance of one of these is
 
3211
# all in bzrlib.repofmt.knitreponow.  When an instance of one of these is
3285
3212
# needed, it's constructed directly by the BzrDir.  Non-native formats where
3286
3213
# the repository is not separately opened are similar.
3287
3214
 
3288
3215
format_registry.register_lazy(
3289
 
    'Bazaar-NG Repository format 7',
3290
 
    'bzrlib.repofmt.weaverepo',
3291
 
    'RepositoryFormat7'
3292
 
    )
3293
 
 
3294
 
format_registry.register_lazy(
3295
3216
    'Bazaar-NG Knit Repository Format 1',
3296
3217
    'bzrlib.repofmt.knitrepo',
3297
3218
    'RepositoryFormatKnit1',
3314
3235
# NOTE: These are experimental in 0.92. Stable in 1.0 and above
3315
3236
format_registry.register_lazy(
3316
3237
    'Bazaar pack repository format 1 (needs bzr 0.92)\n',
3317
 
    'bzrlib.repofmt.pack_repo',
 
3238
    'bzrlib.repofmt.knitpack_repo',
3318
3239
    'RepositoryFormatKnitPack1',
3319
3240
    )
3320
3241
format_registry.register_lazy(
3321
3242
    'Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n',
3322
 
    'bzrlib.repofmt.pack_repo',
 
3243
    'bzrlib.repofmt.knitpack_repo',
3323
3244
    'RepositoryFormatKnitPack3',
3324
3245
    )
3325
3246
format_registry.register_lazy(
3326
3247
    'Bazaar pack repository format 1 with rich root (needs bzr 1.0)\n',
3327
 
    'bzrlib.repofmt.pack_repo',
 
3248
    'bzrlib.repofmt.knitpack_repo',
3328
3249
    'RepositoryFormatKnitPack4',
3329
3250
    )
3330
3251
format_registry.register_lazy(
3331
3252
    'Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n',
3332
 
    'bzrlib.repofmt.pack_repo',
 
3253
    'bzrlib.repofmt.knitpack_repo',
3333
3254
    'RepositoryFormatKnitPack5',
3334
3255
    )
3335
3256
format_registry.register_lazy(
3336
3257
    'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n',
3337
 
    'bzrlib.repofmt.pack_repo',
 
3258
    'bzrlib.repofmt.knitpack_repo',
3338
3259
    'RepositoryFormatKnitPack5RichRoot',
3339
3260
    )
3340
3261
format_registry.register_lazy(
3341
3262
    'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n',
3342
 
    'bzrlib.repofmt.pack_repo',
 
3263
    'bzrlib.repofmt.knitpack_repo',
3343
3264
    'RepositoryFormatKnitPack5RichRootBroken',
3344
3265
    )
3345
3266
format_registry.register_lazy(
3346
3267
    'Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n',
3347
 
    'bzrlib.repofmt.pack_repo',
 
3268
    'bzrlib.repofmt.knitpack_repo',
3348
3269
    'RepositoryFormatKnitPack6',
3349
3270
    )
3350
3271
format_registry.register_lazy(
3351
3272
    'Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n',
3352
 
    'bzrlib.repofmt.pack_repo',
 
3273
    'bzrlib.repofmt.knitpack_repo',
3353
3274
    'RepositoryFormatKnitPack6RichRoot',
3354
3275
    )
 
3276
format_registry.register_lazy(
 
3277
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
3278
    'bzrlib.repofmt.groupcompress_repo',
 
3279
    'RepositoryFormat2a',
 
3280
    )
3355
3281
 
3356
3282
# Development formats.
3357
 
# Obsolete but kept pending a CHK based subtree format.
 
3283
# Check their docstrings to see if/when they are obsolete.
3358
3284
format_registry.register_lazy(
3359
3285
    ("Bazaar development format 2 with subtree support "
3360
3286
        "(needs bzr.dev from before 1.8)\n"),
3361
 
    'bzrlib.repofmt.pack_repo',
 
3287
    'bzrlib.repofmt.knitpack_repo',
3362
3288
    'RepositoryFormatPackDevelopment2Subtree',
3363
3289
    )
3364
 
 
3365
 
# 1.14->1.16 go below here
3366
 
format_registry.register_lazy(
3367
 
    'Bazaar development format - group compression and chk inventory'
3368
 
        ' (needs bzr.dev from 1.14)\n',
3369
 
    'bzrlib.repofmt.groupcompress_repo',
3370
 
    'RepositoryFormatCHK1',
3371
 
    )
3372
 
 
3373
 
format_registry.register_lazy(
3374
 
    'Bazaar development format - chk repository with bencode revision '
3375
 
        'serialization (needs bzr.dev from 1.16)\n',
3376
 
    'bzrlib.repofmt.groupcompress_repo',
3377
 
    'RepositoryFormatCHK2',
3378
 
    )
3379
 
format_registry.register_lazy(
3380
 
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
3381
 
    'bzrlib.repofmt.groupcompress_repo',
3382
 
    'RepositoryFormat2a',
3383
 
    )
3384
3290
format_registry.register_lazy(
3385
3291
    'Bazaar development format 8\n',
3386
3292
    'bzrlib.repofmt.groupcompress_repo',
3421
3327
        self.target.fetch(self.source, revision_id=revision_id)
3422
3328
 
3423
3329
    @needs_write_lock
3424
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
 
3330
    def fetch(self, revision_id=None, find_ghosts=False,
3425
3331
            fetch_spec=None):
3426
3332
        """Fetch the content required to construct revision_id.
3427
3333
 
3429
3335
 
3430
3336
        :param revision_id: if None all content is copied, if NULL_REVISION no
3431
3337
                            content is copied.
3432
 
        :param pb: ignored.
3433
3338
        :return: None.
3434
3339
        """
3435
3340
        ui.ui_factory.warn_experimental_format_fetch(self)
3445
3350
                               fetch_spec=fetch_spec,
3446
3351
                               find_ghosts=find_ghosts)
3447
3352
 
3448
 
    def _walk_to_common_revisions(self, revision_ids):
 
3353
    def _walk_to_common_revisions(self, revision_ids, if_present_ids=None):
3449
3354
        """Walk out from revision_ids in source to revisions target has.
3450
3355
 
3451
3356
        :param revision_ids: The start point for the search.
3453
3358
        """
3454
3359
        target_graph = self.target.get_graph()
3455
3360
        revision_ids = frozenset(revision_ids)
 
3361
        if if_present_ids:
 
3362
            all_wanted_revs = revision_ids.union(if_present_ids)
 
3363
        else:
 
3364
            all_wanted_revs = revision_ids
3456
3365
        missing_revs = set()
3457
3366
        source_graph = self.source.get_graph()
3458
3367
        # ensure we don't pay silly lookup costs.
3459
 
        searcher = source_graph._make_breadth_first_searcher(revision_ids)
 
3368
        searcher = source_graph._make_breadth_first_searcher(all_wanted_revs)
3460
3369
        null_set = frozenset([_mod_revision.NULL_REVISION])
3461
3370
        searcher_exhausted = False
3462
3371
        while True:
3498
3407
        return searcher.get_result()
3499
3408
 
3500
3409
    @needs_read_lock
3501
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
3410
    def search_missing_revision_ids(self,
 
3411
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
3412
            find_ghosts=True, revision_ids=None, if_present_ids=None):
3502
3413
        """Return the revision ids that source has that target does not.
3503
3414
 
3504
3415
        :param revision_id: only return revision ids included by this
3505
 
                            revision_id.
 
3416
            revision_id.
 
3417
        :param revision_ids: return revision ids included by these
 
3418
            revision_ids.  NoSuchRevision will be raised if any of these
 
3419
            revisions are not present.
 
3420
        :param if_present_ids: like revision_ids, but will not cause
 
3421
            NoSuchRevision if any of these are absent, instead they will simply
 
3422
            not be in the result.  This is useful for e.g. finding revisions
 
3423
            to fetch for tags, which may reference absent revisions.
3506
3424
        :param find_ghosts: If True find missing revisions in deep history
3507
3425
            rather than just finding the surface difference.
3508
3426
        :return: A bzrlib.graph.SearchResult.
3509
3427
        """
 
3428
        if symbol_versioning.deprecated_passed(revision_id):
 
3429
            symbol_versioning.warn(
 
3430
                'search_missing_revision_ids(revision_id=...) was '
 
3431
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
 
3432
                DeprecationWarning, stacklevel=2)
 
3433
            if revision_ids is not None:
 
3434
                raise AssertionError(
 
3435
                    'revision_ids is mutually exclusive with revision_id')
 
3436
            if revision_id is not None:
 
3437
                revision_ids = [revision_id]
 
3438
        del revision_id
3510
3439
        # stop searching at found target revisions.
3511
 
        if not find_ghosts and revision_id is not None:
3512
 
            return self._walk_to_common_revisions([revision_id])
 
3440
        if not find_ghosts and (revision_ids is not None or if_present_ids is
 
3441
                not None):
 
3442
            return self._walk_to_common_revisions(revision_ids,
 
3443
                    if_present_ids=if_present_ids)
3513
3444
        # generic, possibly worst case, slow code path.
3514
3445
        target_ids = set(self.target.all_revision_ids())
3515
 
        if revision_id is not None:
3516
 
            source_ids = self.source.get_ancestry(revision_id)
3517
 
            if source_ids[0] is not None:
3518
 
                raise AssertionError()
3519
 
            source_ids.pop(0)
3520
 
        else:
3521
 
            source_ids = self.source.all_revision_ids()
 
3446
        source_ids = self._present_source_revisions_for(
 
3447
            revision_ids, if_present_ids)
3522
3448
        result_set = set(source_ids).difference(target_ids)
3523
3449
        return self.source.revision_ids_to_search_result(result_set)
3524
3450
 
 
3451
    def _present_source_revisions_for(self, revision_ids, if_present_ids=None):
 
3452
        """Returns set of all revisions in ancestry of revision_ids present in
 
3453
        the source repo.
 
3454
 
 
3455
        :param revision_ids: if None, all revisions in source are returned.
 
3456
        :param if_present_ids: like revision_ids, but if any/all of these are
 
3457
            absent no error is raised.
 
3458
        """
 
3459
        if revision_ids is not None or if_present_ids is not None:
 
3460
            # First, ensure all specified revisions exist.  Callers expect
 
3461
            # NoSuchRevision when they pass absent revision_ids here.
 
3462
            if revision_ids is None:
 
3463
                revision_ids = set()
 
3464
            if if_present_ids is None:
 
3465
                if_present_ids = set()
 
3466
            revision_ids = set(revision_ids)
 
3467
            if_present_ids = set(if_present_ids)
 
3468
            all_wanted_ids = revision_ids.union(if_present_ids)
 
3469
            graph = self.source.get_graph()
 
3470
            present_revs = set(graph.get_parent_map(all_wanted_ids))
 
3471
            missing = revision_ids.difference(present_revs)
 
3472
            if missing:
 
3473
                raise errors.NoSuchRevision(self.source, missing.pop())
 
3474
            found_ids = all_wanted_ids.intersection(present_revs)
 
3475
            source_ids = [rev_id for (rev_id, parents) in
 
3476
                          graph.iter_ancestry(found_ids)
 
3477
                          if rev_id != _mod_revision.NULL_REVISION
 
3478
                          and parents is not None]
 
3479
        else:
 
3480
            source_ids = self.source.all_revision_ids()
 
3481
        return set(source_ids)
 
3482
 
3525
3483
    @staticmethod
3526
3484
    def _same_model(source, target):
3527
3485
        """True if source and target have the same data representation.
3568
3526
        return InterRepository._same_model(source, target)
3569
3527
 
3570
3528
 
3571
 
class InterWeaveRepo(InterSameDataRepository):
3572
 
    """Optimised code paths between Weave based repositories.
3573
 
 
3574
 
    This should be in bzrlib/repofmt/weaverepo.py but we have not yet
3575
 
    implemented lazy inter-object optimisation.
3576
 
    """
3577
 
 
3578
 
    @classmethod
3579
 
    def _get_repo_format_to_test(self):
3580
 
        from bzrlib.repofmt import weaverepo
3581
 
        return weaverepo.RepositoryFormat7()
3582
 
 
3583
 
    @staticmethod
3584
 
    def is_compatible(source, target):
3585
 
        """Be compatible with known Weave formats.
3586
 
 
3587
 
        We don't test for the stores being of specific types because that
3588
 
        could lead to confusing results, and there is no need to be
3589
 
        overly general.
3590
 
        """
3591
 
        from bzrlib.repofmt.weaverepo import (
3592
 
                RepositoryFormat5,
3593
 
                RepositoryFormat6,
3594
 
                RepositoryFormat7,
3595
 
                )
3596
 
        try:
3597
 
            return (isinstance(source._format, (RepositoryFormat5,
3598
 
                                                RepositoryFormat6,
3599
 
                                                RepositoryFormat7)) and
3600
 
                    isinstance(target._format, (RepositoryFormat5,
3601
 
                                                RepositoryFormat6,
3602
 
                                                RepositoryFormat7)))
3603
 
        except AttributeError:
3604
 
            return False
3605
 
 
3606
 
    @needs_write_lock
3607
 
    def copy_content(self, revision_id=None):
3608
 
        """See InterRepository.copy_content()."""
3609
 
        # weave specific optimised path:
3610
 
        try:
3611
 
            self.target.set_make_working_trees(self.source.make_working_trees())
3612
 
        except (errors.RepositoryUpgradeRequired, NotImplemented):
3613
 
            pass
3614
 
        # FIXME do not peek!
3615
 
        if self.source._transport.listable():
3616
 
            pb = ui.ui_factory.nested_progress_bar()
3617
 
            try:
3618
 
                self.target.texts.insert_record_stream(
3619
 
                    self.source.texts.get_record_stream(
3620
 
                        self.source.texts.keys(), 'topological', False))
3621
 
                pb.update('Copying inventory', 0, 1)
3622
 
                self.target.inventories.insert_record_stream(
3623
 
                    self.source.inventories.get_record_stream(
3624
 
                        self.source.inventories.keys(), 'topological', False))
3625
 
                self.target.signatures.insert_record_stream(
3626
 
                    self.source.signatures.get_record_stream(
3627
 
                        self.source.signatures.keys(),
3628
 
                        'unordered', True))
3629
 
                self.target.revisions.insert_record_stream(
3630
 
                    self.source.revisions.get_record_stream(
3631
 
                        self.source.revisions.keys(),
3632
 
                        'topological', True))
3633
 
            finally:
3634
 
                pb.finished()
3635
 
        else:
3636
 
            self.target.fetch(self.source, revision_id=revision_id)
3637
 
 
3638
 
    @needs_read_lock
3639
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3640
 
        """See InterRepository.missing_revision_ids()."""
3641
 
        # we want all revisions to satisfy revision_id in source.
3642
 
        # but we don't want to stat every file here and there.
3643
 
        # we want then, all revisions other needs to satisfy revision_id
3644
 
        # checked, but not those that we have locally.
3645
 
        # so the first thing is to get a subset of the revisions to
3646
 
        # satisfy revision_id in source, and then eliminate those that
3647
 
        # we do already have.
3648
 
        # this is slow on high latency connection to self, but as this
3649
 
        # disk format scales terribly for push anyway due to rewriting
3650
 
        # inventory.weave, this is considered acceptable.
3651
 
        # - RBC 20060209
3652
 
        if revision_id is not None:
3653
 
            source_ids = self.source.get_ancestry(revision_id)
3654
 
            if source_ids[0] is not None:
3655
 
                raise AssertionError()
3656
 
            source_ids.pop(0)
3657
 
        else:
3658
 
            source_ids = self.source._all_possible_ids()
3659
 
        source_ids_set = set(source_ids)
3660
 
        # source_ids is the worst possible case we may need to pull.
3661
 
        # now we want to filter source_ids against what we actually
3662
 
        # have in target, but don't try to check for existence where we know
3663
 
        # we do not have a revision as that would be pointless.
3664
 
        target_ids = set(self.target._all_possible_ids())
3665
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
3666
 
        actually_present_revisions = set(
3667
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
3668
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
3669
 
        if revision_id is not None:
3670
 
            # we used get_ancestry to determine source_ids then we are assured all
3671
 
            # revisions referenced are present as they are installed in topological order.
3672
 
            # and the tip revision was validated by get_ancestry.
3673
 
            result_set = required_revisions
3674
 
        else:
3675
 
            # if we just grabbed the possibly available ids, then
3676
 
            # we only have an estimate of whats available and need to validate
3677
 
            # that against the revision records.
3678
 
            result_set = set(
3679
 
                self.source._eliminate_revisions_not_present(required_revisions))
3680
 
        return self.source.revision_ids_to_search_result(result_set)
3681
 
 
3682
 
 
3683
 
class InterKnitRepo(InterSameDataRepository):
3684
 
    """Optimised code paths between Knit based repositories."""
3685
 
 
3686
 
    @classmethod
3687
 
    def _get_repo_format_to_test(self):
3688
 
        from bzrlib.repofmt import knitrepo
3689
 
        return knitrepo.RepositoryFormatKnit1()
3690
 
 
3691
 
    @staticmethod
3692
 
    def is_compatible(source, target):
3693
 
        """Be compatible with known Knit formats.
3694
 
 
3695
 
        We don't test for the stores being of specific types because that
3696
 
        could lead to confusing results, and there is no need to be
3697
 
        overly general.
3698
 
        """
3699
 
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit
3700
 
        try:
3701
 
            are_knits = (isinstance(source._format, RepositoryFormatKnit) and
3702
 
                isinstance(target._format, RepositoryFormatKnit))
3703
 
        except AttributeError:
3704
 
            return False
3705
 
        return are_knits and InterRepository._same_model(source, target)
3706
 
 
3707
 
    @needs_read_lock
3708
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3709
 
        """See InterRepository.missing_revision_ids()."""
3710
 
        if revision_id is not None:
3711
 
            source_ids = self.source.get_ancestry(revision_id)
3712
 
            if source_ids[0] is not None:
3713
 
                raise AssertionError()
3714
 
            source_ids.pop(0)
3715
 
        else:
3716
 
            source_ids = self.source.all_revision_ids()
3717
 
        source_ids_set = set(source_ids)
3718
 
        # source_ids is the worst possible case we may need to pull.
3719
 
        # now we want to filter source_ids against what we actually
3720
 
        # have in target, but don't try to check for existence where we know
3721
 
        # we do not have a revision as that would be pointless.
3722
 
        target_ids = set(self.target.all_revision_ids())
3723
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
3724
 
        actually_present_revisions = set(
3725
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
3726
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
3727
 
        if revision_id is not None:
3728
 
            # we used get_ancestry to determine source_ids then we are assured all
3729
 
            # revisions referenced are present as they are installed in topological order.
3730
 
            # and the tip revision was validated by get_ancestry.
3731
 
            result_set = required_revisions
3732
 
        else:
3733
 
            # if we just grabbed the possibly available ids, then
3734
 
            # we only have an estimate of whats available and need to validate
3735
 
            # that against the revision records.
3736
 
            result_set = set(
3737
 
                self.source._eliminate_revisions_not_present(required_revisions))
3738
 
        return self.source.revision_ids_to_search_result(result_set)
3739
 
 
3740
 
 
3741
3529
class InterDifferingSerializer(InterRepository):
3742
3530
 
3743
3531
    @classmethod
4011
3799
                  len(revision_ids))
4012
3800
 
4013
3801
    @needs_write_lock
4014
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
 
3802
    def fetch(self, revision_id=None, find_ghosts=False,
4015
3803
            fetch_spec=None):
4016
3804
        """See InterRepository.fetch()."""
4017
3805
        if fetch_spec is not None:
4018
 
            raise AssertionError("Not implemented yet...")
 
3806
            revision_ids = fetch_spec.get_keys()
 
3807
        else:
 
3808
            revision_ids = None
4019
3809
        ui.ui_factory.warn_experimental_format_fetch(self)
4020
3810
        if (not self.source.supports_rich_root()
4021
3811
            and self.target.supports_rich_root()):
4028
3818
            ui.ui_factory.show_user_warning('cross_format_fetch',
4029
3819
                from_format=self.source._format,
4030
3820
                to_format=self.target._format)
4031
 
        revision_ids = self.target.search_missing_revision_ids(self.source,
4032
 
            revision_id, find_ghosts=find_ghosts).get_keys()
 
3821
        if revision_ids is None:
 
3822
            if revision_id:
 
3823
                search_revision_ids = [revision_id]
 
3824
            else:
 
3825
                search_revision_ids = None
 
3826
            revision_ids = self.target.search_missing_revision_ids(self.source,
 
3827
                revision_ids=search_revision_ids,
 
3828
                find_ghosts=find_ghosts).get_keys()
4033
3829
        if not revision_ids:
4034
3830
            return 0, 0
4035
3831
        revision_ids = tsort.topo_sort(
4039
3835
        # Walk though all revisions; get inventory deltas, copy referenced
4040
3836
        # texts that delta references, insert the delta, revision and
4041
3837
        # signature.
4042
 
        if pb is None:
4043
 
            my_pb = ui.ui_factory.nested_progress_bar()
4044
 
            pb = my_pb
4045
 
        else:
4046
 
            symbol_versioning.warn(
4047
 
                symbol_versioning.deprecated_in((1, 14, 0))
4048
 
                % "pb parameter to fetch()")
4049
 
            my_pb = None
 
3838
        pb = ui.ui_factory.nested_progress_bar()
4050
3839
        try:
4051
3840
            self._fetch_all_revisions(revision_ids, pb)
4052
3841
        finally:
4053
 
            if my_pb is not None:
4054
 
                my_pb.finished()
 
3842
            pb.finished()
4055
3843
        return len(revision_ids), 0
4056
3844
 
4057
3845
    def _get_basis(self, first_revision_id):
4079
3867
 
4080
3868
InterRepository.register_optimiser(InterDifferingSerializer)
4081
3869
InterRepository.register_optimiser(InterSameDataRepository)
4082
 
InterRepository.register_optimiser(InterWeaveRepo)
4083
 
InterRepository.register_optimiser(InterKnitRepo)
4084
3870
 
4085
3871
 
4086
3872
class CopyConverter(object):
4131
3917
        pb.finished()
4132
3918
 
4133
3919
 
4134
 
_unescape_map = {
4135
 
    'apos':"'",
4136
 
    'quot':'"',
4137
 
    'amp':'&',
4138
 
    'lt':'<',
4139
 
    'gt':'>'
4140
 
}
4141
 
 
4142
 
 
4143
 
def _unescaper(match, _map=_unescape_map):
4144
 
    code = match.group(1)
4145
 
    try:
4146
 
        return _map[code]
4147
 
    except KeyError:
4148
 
        if not code.startswith('#'):
4149
 
            raise
4150
 
        return unichr(int(code[1:])).encode('utf8')
4151
 
 
4152
 
 
4153
 
_unescape_re = None
4154
 
 
4155
 
 
4156
 
def _unescape_xml(data):
4157
 
    """Unescape predefined XML entities in a string of data."""
4158
 
    global _unescape_re
4159
 
    if _unescape_re is None:
4160
 
        _unescape_re = re.compile('\&([^;]*);')
4161
 
    return _unescape_re.sub(_unescaper, data)
4162
 
 
4163
 
 
4164
3920
class _VersionedFileChecker(object):
4165
3921
 
4166
3922
    def __init__(self, repository, text_key_references=None, ancestors=None):
4225
3981
        return wrong_parents, unused_keys
4226
3982
 
4227
3983
 
4228
 
def _old_get_graph(repository, revision_id):
4229
 
    """DO NOT USE. That is all. I'm serious."""
4230
 
    graph = repository.get_graph()
4231
 
    revision_graph = dict(((key, value) for key, value in
4232
 
        graph.iter_ancestry([revision_id]) if value is not None))
4233
 
    return _strip_NULL_ghosts(revision_graph)
4234
 
 
4235
 
 
4236
3984
def _strip_NULL_ghosts(revision_graph):
4237
3985
    """Also don't use this. more compatibility code for unmigrated clients."""
4238
3986
    # Filter ghosts, and null:
4274
4022
                is_resume = False
4275
4023
            try:
4276
4024
                # locked_insert_stream performs a commit|suspend.
4277
 
                return self._locked_insert_stream(stream, src_format,
4278
 
                    is_resume)
 
4025
                missing_keys = self.insert_stream_without_locking(stream,
 
4026
                                    src_format, is_resume)
 
4027
                if missing_keys:
 
4028
                    # suspend the write group and tell the caller what we is
 
4029
                    # missing. We know we can suspend or else we would not have
 
4030
                    # entered this code path. (All repositories that can handle
 
4031
                    # missing keys can handle suspending a write group).
 
4032
                    write_group_tokens = self.target_repo.suspend_write_group()
 
4033
                    return write_group_tokens, missing_keys
 
4034
                hint = self.target_repo.commit_write_group()
 
4035
                to_serializer = self.target_repo._format._serializer
 
4036
                src_serializer = src_format._serializer
 
4037
                if (to_serializer != src_serializer and
 
4038
                    self.target_repo._format.pack_compresses):
 
4039
                    self.target_repo.pack(hint=hint)
 
4040
                return [], set()
4279
4041
            except:
4280
4042
                self.target_repo.abort_write_group(suppress_errors=True)
4281
4043
                raise
4282
4044
        finally:
4283
4045
            self.target_repo.unlock()
4284
4046
 
4285
 
    def _locked_insert_stream(self, stream, src_format, is_resume):
 
4047
    def insert_stream_without_locking(self, stream, src_format,
 
4048
                                      is_resume=False):
 
4049
        """Insert a stream's content into the target repository.
 
4050
 
 
4051
        This assumes that you already have a locked repository and an active
 
4052
        write group.
 
4053
 
 
4054
        :param src_format: a bzr repository format.
 
4055
        :param is_resume: Passed down to get_missing_parent_inventories to
 
4056
            indicate if we should be checking for missing texts at the same
 
4057
            time.
 
4058
 
 
4059
        :return: A set of keys that are missing.
 
4060
        """
 
4061
        if not self.target_repo.is_write_locked():
 
4062
            raise errors.ObjectNotLocked(self)
 
4063
        if not self.target_repo.is_in_write_group():
 
4064
            raise errors.BzrError('you must already be in a write group')
4286
4065
        to_serializer = self.target_repo._format._serializer
4287
4066
        src_serializer = src_format._serializer
4288
4067
        new_pack = None
4367
4146
            # cannot even attempt suspending, and missing would have failed
4368
4147
            # during stream insertion.
4369
4148
            missing_keys = set()
4370
 
        else:
4371
 
            if missing_keys:
4372
 
                # suspend the write group and tell the caller what we is
4373
 
                # missing. We know we can suspend or else we would not have
4374
 
                # entered this code path. (All repositories that can handle
4375
 
                # missing keys can handle suspending a write group).
4376
 
                write_group_tokens = self.target_repo.suspend_write_group()
4377
 
                return write_group_tokens, missing_keys
4378
 
        hint = self.target_repo.commit_write_group()
4379
 
        if (to_serializer != src_serializer and
4380
 
            self.target_repo._format.pack_compresses):
4381
 
            self.target_repo.pack(hint=hint)
4382
 
        return [], set()
 
4149
        return missing_keys
4383
4150
 
4384
4151
    def _extract_and_insert_inventory_deltas(self, substream, serializer):
4385
4152
        target_rich_root = self.target_repo._format.rich_root_data
4392
4159
                parse_result = deserialiser.parse_text_bytes(
4393
4160
                    inventory_delta_bytes)
4394
4161
            except inventory_delta.IncompatibleInventoryDelta, err:
4395
 
                trace.mutter("Incompatible delta: %s", err.msg)
 
4162
                mutter("Incompatible delta: %s", err.msg)
4396
4163
                raise errors.IncompatibleRevision(self.target_repo._format)
4397
4164
            basis_id, new_id, rich_root, tree_refs, inv_delta = parse_result
4398
4165
            revision_id = new_id
4733
4500
    except StopIteration:
4734
4501
        # No more history
4735
4502
        return
4736