~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Andrew Bennetts
  • Date: 2011-02-14 11:57:45 UTC
  • mto: This revision was merged to the branch mainline in revision 5664.
  • Revision ID: andrew.bennetts@canonical.com-20110214115745-gzb0nn2whvs4unp7
Preserve identity of default values in the pretty decorators.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
from bzrlib.lazy_import import lazy_import
18
18
lazy_import(globals(), """
 
19
import cStringIO
 
20
import re
19
21
import time
20
22
 
21
23
from bzrlib import (
22
24
    bzrdir,
23
25
    check,
 
26
    chk_map,
24
27
    config,
25
28
    controldir,
26
29
    debug,
29
32
    generate_ids,
30
33
    gpg,
31
34
    graph,
 
35
    inventory,
32
36
    inventory_delta,
 
37
    lazy_regex,
33
38
    lockable_files,
34
39
    lockdir,
35
40
    lru_cache,
36
41
    osutils,
 
42
    pyutils,
37
43
    revision as _mod_revision,
38
44
    static_tuple,
 
45
    trace,
39
46
    tsort,
40
47
    versionedfile,
41
48
    )
42
49
from bzrlib.bundle import serializer
43
 
from bzrlib.recordcounter import RecordCounter
44
 
from bzrlib.revisiontree import InventoryRevisionTree
 
50
from bzrlib.revisiontree import RevisionTree
45
51
from bzrlib.store.versioned import VersionedFileStore
46
52
from bzrlib.testament import Testament
47
53
""")
60
66
    ROOT_ID,
61
67
    entry_factory,
62
68
    )
 
69
from bzrlib.recordcounter import RecordCounter
63
70
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
64
71
from bzrlib.trace import (
65
72
    log_exception_quietly, note, mutter, mutter_callsite, warning)
88
95
    record_root_entry = True
89
96
    # the default CommitBuilder does not manage trees whose root is versioned.
90
97
    _versioned_root = False
91
 
    # this commit builder supports the record_entry_contents interface
92
 
    supports_record_entry_contents = True
93
98
 
94
99
    def __init__(self, repository, parents, config, timestamp=None,
95
100
                 timezone=None, committer=None, revprops=None,
96
 
                 revision_id=None, lossy=False):
 
101
                 revision_id=None):
97
102
        """Initiate a CommitBuilder.
98
103
 
99
104
        :param repository: Repository to commit to.
100
105
        :param parents: Revision ids of the parents of the new revision.
 
106
        :param config: Configuration to use.
101
107
        :param timestamp: Optional timestamp recorded for commit.
102
108
        :param timezone: Optional timezone for timestamp.
103
109
        :param committer: Optional committer to set for commit.
104
110
        :param revprops: Optional dictionary of revision properties.
105
111
        :param revision_id: Optional revision id.
106
 
        :param lossy: Whether to discard data that can not be natively
107
 
            represented, when pushing to a foreign VCS 
108
112
        """
109
113
        self._config = config
110
 
        self._lossy = lossy
111
114
 
112
115
        if committer is None:
113
116
            self._committer = self._config.username()
236
239
    def revision_tree(self):
237
240
        """Return the tree that was just committed.
238
241
 
239
 
        After calling commit() this can be called to get a
240
 
        InventoryRevisionTree representing the newly committed tree. This is
241
 
        preferred to calling Repository.revision_tree() because that may
242
 
        require deserializing the inventory, while we already have a copy in
 
242
        After calling commit() this can be called to get a RevisionTree
 
243
        representing the newly committed tree. This is preferred to
 
244
        calling Repository.revision_tree() because that may require
 
245
        deserializing the inventory, while we already have a copy in
243
246
        memory.
244
247
        """
245
248
        if self.new_inventory is None:
246
249
            self.new_inventory = self.repository.get_inventory(
247
250
                self._new_revision_id)
248
 
        return InventoryRevisionTree(self.repository, self.new_inventory,
 
251
        return RevisionTree(self.repository, self.new_inventory,
249
252
            self._new_revision_id)
250
253
 
251
254
    def finish_inventory(self):
985
988
    # in a Repository class subclass rather than to override
986
989
    # get_commit_builder.
987
990
    _commit_builder_class = CommitBuilder
 
991
    # The search regex used by xml based repositories to determine what things
 
992
    # where changed in a single commit.
 
993
    _file_ids_altered_regex = lazy_regex.lazy_compile(
 
994
        r'file_id="(?P<file_id>[^"]+)"'
 
995
        r'.* revision="(?P<revision_id>[^"]+)"'
 
996
        )
988
997
 
989
998
    def abort_write_group(self, suppress_errors=False):
990
999
        """Commit the contents accrued within the current write group.
1163
1172
        if config is not None and config.signature_needed():
1164
1173
            if inv is None:
1165
1174
                inv = self.get_inventory(revision_id)
1166
 
            tree = InventoryRevisionTree(self, inv, revision_id)
1167
 
            testament = Testament(rev, tree)
1168
 
            plaintext = testament.as_short_text()
 
1175
            plaintext = Testament(rev, inv).as_short_text()
1169
1176
            self.store_revision_signature(
1170
1177
                gpg.GPGStrategy(config), plaintext, revision_id)
1171
1178
        # check inventory present
1738
1745
    def _resume_write_group(self, tokens):
1739
1746
        raise errors.UnsuspendableWriteGroup(self)
1740
1747
 
1741
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
1748
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
1742
1749
            fetch_spec=None):
1743
1750
        """Fetch the content required to construct revision_id from source.
1744
1751
 
1779
1786
                self.get_revision(revision_id)
1780
1787
            return 0, []
1781
1788
        inter = InterRepository.get(source, self)
1782
 
        return inter.fetch(revision_id=revision_id,
 
1789
        return inter.fetch(revision_id=revision_id, pb=pb,
1783
1790
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1784
1791
 
1785
1792
    def create_bundle(self, target, base, fileobj, format=None):
1787
1794
 
1788
1795
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1789
1796
                           timezone=None, committer=None, revprops=None,
1790
 
                           revision_id=None, lossy=False):
 
1797
                           revision_id=None):
1791
1798
        """Obtain a CommitBuilder for this repository.
1792
1799
 
1793
1800
        :param branch: Branch to commit to.
1798
1805
        :param committer: Optional committer to set for commit.
1799
1806
        :param revprops: Optional dictionary of revision properties.
1800
1807
        :param revision_id: Optional revision id.
1801
 
        :param lossy: Whether to discard data that can not be natively
1802
 
            represented, when pushing to a foreign VCS
1803
1808
        """
1804
1809
        if self._fallback_repositories and not self._format.supports_chks:
1805
1810
            raise errors.BzrError("Cannot commit directly to a stacked branch"
1806
1811
                " in pre-2a formats. See "
1807
1812
                "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1808
1813
        result = self._commit_builder_class(self, parents, config,
1809
 
            timestamp, timezone, committer, revprops, revision_id,
1810
 
            lossy)
 
1814
            timestamp, timezone, committer, revprops, revision_id)
1811
1815
        self.start_write_group()
1812
1816
        return result
1813
1817
 
2059
2063
        w = self.inventories
2060
2064
        pb = ui.ui_factory.nested_progress_bar()
2061
2065
        try:
2062
 
            return self._serializer._find_text_key_references(
 
2066
            return self._find_text_key_references_from_xml_inventory_lines(
2063
2067
                w.iter_lines_added_or_present_in_keys(revision_keys, pb=pb))
2064
2068
        finally:
2065
2069
            pb.finished()
2066
2070
 
 
2071
    def _find_text_key_references_from_xml_inventory_lines(self,
 
2072
        line_iterator):
 
2073
        """Core routine for extracting references to texts from inventories.
 
2074
 
 
2075
        This performs the translation of xml lines to revision ids.
 
2076
 
 
2077
        :param line_iterator: An iterator of lines, origin_version_id
 
2078
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
 
2079
            to whether they were referred to by the inventory of the
 
2080
            revision_id that they contain. Note that if that revision_id was
 
2081
            not part of the line_iterator's output then False will be given -
 
2082
            even though it may actually refer to that key.
 
2083
        """
 
2084
        if not self._serializer.support_altered_by_hack:
 
2085
            raise AssertionError(
 
2086
                "_find_text_key_references_from_xml_inventory_lines only "
 
2087
                "supported for branches which store inventory as unnested xml"
 
2088
                ", not on %r" % self)
 
2089
        result = {}
 
2090
 
 
2091
        # this code needs to read every new line in every inventory for the
 
2092
        # inventories [revision_ids]. Seeing a line twice is ok. Seeing a line
 
2093
        # not present in one of those inventories is unnecessary but not
 
2094
        # harmful because we are filtering by the revision id marker in the
 
2095
        # inventory lines : we only select file ids altered in one of those
 
2096
        # revisions. We don't need to see all lines in the inventory because
 
2097
        # only those added in an inventory in rev X can contain a revision=X
 
2098
        # line.
 
2099
        unescape_revid_cache = {}
 
2100
        unescape_fileid_cache = {}
 
2101
 
 
2102
        # jam 20061218 In a big fetch, this handles hundreds of thousands
 
2103
        # of lines, so it has had a lot of inlining and optimizing done.
 
2104
        # Sorry that it is a little bit messy.
 
2105
        # Move several functions to be local variables, since this is a long
 
2106
        # running loop.
 
2107
        search = self._file_ids_altered_regex.search
 
2108
        unescape = _unescape_xml
 
2109
        setdefault = result.setdefault
 
2110
        for line, line_key in line_iterator:
 
2111
            match = search(line)
 
2112
            if match is None:
 
2113
                continue
 
2114
            # One call to match.group() returning multiple items is quite a
 
2115
            # bit faster than 2 calls to match.group() each returning 1
 
2116
            file_id, revision_id = match.group('file_id', 'revision_id')
 
2117
 
 
2118
            # Inlining the cache lookups helps a lot when you make 170,000
 
2119
            # lines and 350k ids, versus 8.4 unique ids.
 
2120
            # Using a cache helps in 2 ways:
 
2121
            #   1) Avoids unnecessary decoding calls
 
2122
            #   2) Re-uses cached strings, which helps in future set and
 
2123
            #      equality checks.
 
2124
            # (2) is enough that removing encoding entirely along with
 
2125
            # the cache (so we are using plain strings) results in no
 
2126
            # performance improvement.
 
2127
            try:
 
2128
                revision_id = unescape_revid_cache[revision_id]
 
2129
            except KeyError:
 
2130
                unescaped = unescape(revision_id)
 
2131
                unescape_revid_cache[revision_id] = unescaped
 
2132
                revision_id = unescaped
 
2133
 
 
2134
            # Note that unconditionally unescaping means that we deserialise
 
2135
            # every fileid, which for general 'pull' is not great, but we don't
 
2136
            # really want to have some many fulltexts that this matters anyway.
 
2137
            # RBC 20071114.
 
2138
            try:
 
2139
                file_id = unescape_fileid_cache[file_id]
 
2140
            except KeyError:
 
2141
                unescaped = unescape(file_id)
 
2142
                unescape_fileid_cache[file_id] = unescaped
 
2143
                file_id = unescaped
 
2144
 
 
2145
            key = (file_id, revision_id)
 
2146
            setdefault(key, False)
 
2147
            if revision_id == line_key[-1]:
 
2148
                result[key] = True
 
2149
        return result
 
2150
 
2067
2151
    def _inventory_xml_lines_for_keys(self, keys):
2068
2152
        """Get a line iterator of the sort needed for findind references.
2069
2153
 
2099
2183
        revision_ids. Each altered file-ids has the exact revision_ids that
2100
2184
        altered it listed explicitly.
2101
2185
        """
2102
 
        seen = set(self._serializer._find_text_key_references(
 
2186
        seen = set(self._find_text_key_references_from_xml_inventory_lines(
2103
2187
                line_iterator).iterkeys())
2104
2188
        parent_keys = self._find_parent_keys_of_revisions(revision_keys)
2105
 
        parent_seen = set(self._serializer._find_text_key_references(
 
2189
        parent_seen = set(self._find_text_key_references_from_xml_inventory_lines(
2106
2190
            self._inventory_xml_lines_for_keys(parent_keys)))
2107
2191
        new_keys = seen - parent_seen
2108
2192
        result = {}
2515
2599
        # TODO: refactor this to use an existing revision object
2516
2600
        # so we don't need to read it in twice.
2517
2601
        if revision_id == _mod_revision.NULL_REVISION:
2518
 
            return InventoryRevisionTree(self,
2519
 
                Inventory(root_id=None), _mod_revision.NULL_REVISION)
 
2602
            return RevisionTree(self, Inventory(root_id=None),
 
2603
                                _mod_revision.NULL_REVISION)
2520
2604
        else:
2521
2605
            inv = self.get_inventory(revision_id)
2522
 
            return InventoryRevisionTree(self, inv, revision_id)
 
2606
            return RevisionTree(self, inv, revision_id)
2523
2607
 
2524
2608
    def revision_trees(self, revision_ids):
2525
2609
        """Return Trees for revisions in this repository.
2529
2613
        """
2530
2614
        inventories = self.iter_inventories(revision_ids)
2531
2615
        for inv in inventories:
2532
 
            yield InventoryRevisionTree(self, inv, inv.revision_id)
 
2616
            yield RevisionTree(self, inv, inv.revision_id)
2533
2617
 
2534
2618
    def _filtered_revision_trees(self, revision_ids, file_ids):
2535
2619
        """Return Tree for a revision on this branch with only some files.
2545
2629
            # Should we introduce a FilteredRevisionTree class rather
2546
2630
            # than pre-filter the inventory here?
2547
2631
            filtered_inv = inv.filter(file_ids)
2548
 
            yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
 
2632
            yield RevisionTree(self, filtered_inv, filtered_inv.revision_id)
2549
2633
 
2550
2634
    @needs_read_lock
2551
2635
    def get_ancestry(self, revision_id, topo_sorted=True):
2736
2820
        return result
2737
2821
 
2738
2822
    def _warn_if_deprecated(self, branch=None):
2739
 
        if not self._format.is_deprecated():
2740
 
            return
2741
2823
        global _deprecation_warning_done
2742
2824
        if _deprecation_warning_done:
2743
2825
            return
2773
2855
                except UnicodeDecodeError:
2774
2856
                    raise errors.NonAsciiRevisionId(method, self)
2775
2857
 
2776
 
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2777
 
        """Find revisions with different parent lists in the revision object
2778
 
        and in the index graph.
 
2858
    def revision_graph_can_have_wrong_parents(self):
 
2859
        """Is it possible for this repository to have a revision graph with
 
2860
        incorrect parents?
2779
2861
 
2780
 
        :param revisions_iterator: None, or an iterator of (revid,
2781
 
            Revision-or-None). This iterator controls the revisions checked.
2782
 
        :returns: an iterator yielding tuples of (revison-id, parents-in-index,
2783
 
            parents-in-revision).
 
2862
        If True, then this repository must also implement
 
2863
        _find_inconsistent_revision_parents so that check and reconcile can
 
2864
        check for inconsistencies before proceeding with other checks that may
 
2865
        depend on the revision index being consistent.
2784
2866
        """
2785
 
        if not self.is_locked():
2786
 
            raise AssertionError()
2787
 
        vf = self.revisions
2788
 
        if revisions_iterator is None:
2789
 
            revisions_iterator = self._iter_revisions(None)
2790
 
        for revid, revision in revisions_iterator:
2791
 
            if revision is None:
2792
 
                pass
2793
 
            parent_map = vf.get_parent_map([(revid,)])
2794
 
            parents_according_to_index = tuple(parent[-1] for parent in
2795
 
                parent_map[(revid,)])
2796
 
            parents_according_to_revision = tuple(revision.parent_ids)
2797
 
            if parents_according_to_index != parents_according_to_revision:
2798
 
                yield (revid, parents_according_to_index,
2799
 
                    parents_according_to_revision)
2800
 
 
2801
 
    def _check_for_inconsistent_revision_parents(self):
2802
 
        inconsistencies = list(self._find_inconsistent_revision_parents())
2803
 
        if inconsistencies:
2804
 
            raise errors.BzrCheckError(
2805
 
                "Revision knit has inconsistent parents.")
 
2867
        raise NotImplementedError(self.revision_graph_can_have_wrong_parents)
2806
2868
 
2807
2869
 
2808
2870
def install_revision(repository, rev, revision_tree):
2867
2929
        for revision, tree in parent_trees.iteritems():
2868
2930
            if ie.file_id not in tree:
2869
2931
                continue
2870
 
            parent_id = tree.get_file_revision(ie.file_id)
 
2932
            parent_id = tree.inventory[ie.file_id].revision
2871
2933
            if parent_id in text_parents:
2872
2934
                continue
2873
2935
            text_parents.append((ie.file_id, parent_id))
2942
3004
            control_files)
2943
3005
 
2944
3006
 
2945
 
class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
 
3007
class RepositoryFormatRegistry(registry.FormatRegistry):
2946
3008
    """Repository format registry."""
2947
3009
 
 
3010
    def __init__(self, other_registry=None):
 
3011
        super(RepositoryFormatRegistry, self).__init__(other_registry)
 
3012
        self._extra_formats = []
 
3013
 
 
3014
    def register(self, format):
 
3015
        """Register a new repository format."""
 
3016
        super(RepositoryFormatRegistry, self).register(
 
3017
            format.get_format_string(), format)
 
3018
 
 
3019
    def remove(self, format):
 
3020
        """Remove a registered repository format."""
 
3021
        super(RepositoryFormatRegistry, self).remove(
 
3022
            format.get_format_string())
 
3023
 
 
3024
    def register_extra(self, format):
 
3025
        """Register a repository format that can not be used in a metadir.
 
3026
 
 
3027
        This is mainly useful to allow custom repository formats, such as older
 
3028
        Bazaar formats and foreign formats, to be tested.
 
3029
        """
 
3030
        self._extra_formats.append(registry._ObjectGetter(format))
 
3031
 
 
3032
    def remove_extra(self, format):
 
3033
        """Remove an extra repository format.
 
3034
        """
 
3035
        self._extra_formats.remove(registry._ObjectGetter(format))
 
3036
 
 
3037
    def register_extra_lazy(self, module_name, member_name):
 
3038
        """Register a repository format lazily.
 
3039
        """
 
3040
        self._extra_formats.append(
 
3041
            registry._LazyObjectGetter(module_name, member_name))
 
3042
 
2948
3043
    def get_default(self):
2949
3044
        """Return the current default format."""
2950
3045
        from bzrlib import bzrdir
2951
3046
        return bzrdir.format_registry.make_bzrdir('default').repository_format
2952
3047
 
 
3048
    def _get_extra(self):
 
3049
        result = []
 
3050
        for getter in self._extra_formats:
 
3051
            f = getter.get_obj()
 
3052
            if callable(f):
 
3053
                f = f()
 
3054
            result.append(f)
 
3055
        return result
 
3056
 
 
3057
    def _get_all(self):
 
3058
        """Return all repository formats, even those not usable in metadirs.
 
3059
        """
 
3060
        return [self.get(k) for k in self.keys()] + self._get_extra()
 
3061
 
2953
3062
 
2954
3063
network_format_registry = registry.FormatRegistry()
2955
3064
"""Registry of formats indexed by their network name.
2971
3080
#####################################################################
2972
3081
# Repository Formats
2973
3082
 
2974
 
class RepositoryFormat(controldir.ControlComponentFormat):
 
3083
class RepositoryFormat(object):
2975
3084
    """A repository format.
2976
3085
 
2977
3086
    Formats provide four things:
3040
3149
    experimental = False
3041
3150
    # Does this repository format escape funky characters, or does it create files with
3042
3151
    # similar names as the versioned files in its contents on disk ?
3043
 
    supports_funky_characters = None
3044
 
    # Does this repository format support leaving locks?
3045
 
    supports_leaving_lock = None
3046
 
    # Does this format support the full VersionedFiles interface?
3047
 
    supports_full_versioned_files = None
3048
 
    # Does this format support signing revision signatures?
3049
 
    supports_revision_signatures = True
3050
 
    # Can the revision graph have incorrect parents?
3051
 
    revision_graph_can_have_wrong_parents = None
 
3152
    supports_funky_characters = True
3052
3153
 
3053
3154
    def __repr__(self):
3054
3155
        return "%s()" % self.__class__.__name__
3106
3207
        """Return the short description for this format."""
3107
3208
        raise NotImplementedError(self.get_format_description)
3108
3209
 
 
3210
    # TODO: this shouldn't be in the base class, it's specific to things that
 
3211
    # use weaves or knits -- mbp 20070207
 
3212
    def _get_versioned_file_store(self,
 
3213
                                  name,
 
3214
                                  transport,
 
3215
                                  control_files,
 
3216
                                  prefixed=True,
 
3217
                                  versionedfile_class=None,
 
3218
                                  versionedfile_kwargs={},
 
3219
                                  escaped=False):
 
3220
        if versionedfile_class is None:
 
3221
            versionedfile_class = self._versionedfile_class
 
3222
        weave_transport = control_files._transport.clone(name)
 
3223
        dir_mode = control_files._dir_mode
 
3224
        file_mode = control_files._file_mode
 
3225
        return VersionedFileStore(weave_transport, prefixed=prefixed,
 
3226
                                  dir_mode=dir_mode,
 
3227
                                  file_mode=file_mode,
 
3228
                                  versionedfile_class=versionedfile_class,
 
3229
                                  versionedfile_kwargs=versionedfile_kwargs,
 
3230
                                  escaped=escaped)
 
3231
 
3109
3232
    def initialize(self, a_bzrdir, shared=False):
3110
3233
        """Initialize a repository of this format in a_bzrdir.
3111
3234
 
3127
3250
        """
3128
3251
        return True
3129
3252
 
3130
 
    def is_deprecated(self):
3131
 
        """Is this format deprecated?
3132
 
 
3133
 
        Deprecated formats may trigger a user-visible warning recommending
3134
 
        the user to upgrade. They are still fully supported.
3135
 
        """
3136
 
        return False
3137
 
 
3138
3253
    def network_name(self):
3139
3254
        """A simple byte string uniquely identifying this format for RPC calls.
3140
3255
 
3179
3294
    rich_root_data = False
3180
3295
    supports_tree_reference = False
3181
3296
    supports_external_lookups = False
3182
 
    supports_leaving_lock = True
3183
3297
 
3184
3298
    @property
3185
3299
    def _matchingbzrdir(self):
3223
3337
        return self.get_format_string()
3224
3338
 
3225
3339
 
 
3340
# Pre-0.8 formats that don't have a disk format string (because they are
 
3341
# versioned by the matching control directory). We use the control directories
 
3342
# disk format string as a key for the network_name because they meet the
 
3343
# constraints (simple string, unique, immutable).
 
3344
network_format_registry.register_lazy(
 
3345
    "Bazaar-NG branch, format 5\n",
 
3346
    'bzrlib.repofmt.weaverepo',
 
3347
    'RepositoryFormat5',
 
3348
)
 
3349
network_format_registry.register_lazy(
 
3350
    "Bazaar-NG branch, format 6\n",
 
3351
    'bzrlib.repofmt.weaverepo',
 
3352
    'RepositoryFormat6',
 
3353
)
 
3354
 
 
3355
format_registry.register_extra_lazy(
 
3356
    'bzrlib.repofmt.weaverepo',
 
3357
    'RepositoryFormat4')
 
3358
format_registry.register_extra_lazy(
 
3359
    'bzrlib.repofmt.weaverepo',
 
3360
    'RepositoryFormat5')
 
3361
format_registry.register_extra_lazy(
 
3362
    'bzrlib.repofmt.weaverepo',
 
3363
    'RepositoryFormat6')
 
3364
 
3226
3365
# formats which have no format string are not discoverable or independently
3227
3366
# creatable on disk, so are not registered in format_registry.  They're
3228
 
# all in bzrlib.repofmt.knitreponow.  When an instance of one of these is
 
3367
# all in bzrlib.repofmt.weaverepo now.  When an instance of one of these is
3229
3368
# needed, it's constructed directly by the BzrDir.  Non-native formats where
3230
3369
# the repository is not separately opened are similar.
3231
3370
 
3232
3371
format_registry.register_lazy(
 
3372
    'Bazaar-NG Repository format 7',
 
3373
    'bzrlib.repofmt.weaverepo',
 
3374
    'RepositoryFormat7'
 
3375
    )
 
3376
 
 
3377
format_registry.register_lazy(
3233
3378
    'Bazaar-NG Knit Repository Format 1',
3234
3379
    'bzrlib.repofmt.knitrepo',
3235
3380
    'RepositoryFormatKnit1',
3252
3397
# NOTE: These are experimental in 0.92. Stable in 1.0 and above
3253
3398
format_registry.register_lazy(
3254
3399
    'Bazaar pack repository format 1 (needs bzr 0.92)\n',
3255
 
    'bzrlib.repofmt.knitpack_repo',
 
3400
    'bzrlib.repofmt.pack_repo',
3256
3401
    'RepositoryFormatKnitPack1',
3257
3402
    )
3258
3403
format_registry.register_lazy(
3259
3404
    'Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n',
3260
 
    'bzrlib.repofmt.knitpack_repo',
 
3405
    'bzrlib.repofmt.pack_repo',
3261
3406
    'RepositoryFormatKnitPack3',
3262
3407
    )
3263
3408
format_registry.register_lazy(
3264
3409
    'Bazaar pack repository format 1 with rich root (needs bzr 1.0)\n',
3265
 
    'bzrlib.repofmt.knitpack_repo',
 
3410
    'bzrlib.repofmt.pack_repo',
3266
3411
    'RepositoryFormatKnitPack4',
3267
3412
    )
3268
3413
format_registry.register_lazy(
3269
3414
    'Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n',
3270
 
    'bzrlib.repofmt.knitpack_repo',
 
3415
    'bzrlib.repofmt.pack_repo',
3271
3416
    'RepositoryFormatKnitPack5',
3272
3417
    )
3273
3418
format_registry.register_lazy(
3274
3419
    'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n',
3275
 
    'bzrlib.repofmt.knitpack_repo',
 
3420
    'bzrlib.repofmt.pack_repo',
3276
3421
    'RepositoryFormatKnitPack5RichRoot',
3277
3422
    )
3278
3423
format_registry.register_lazy(
3279
3424
    'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n',
3280
 
    'bzrlib.repofmt.knitpack_repo',
 
3425
    'bzrlib.repofmt.pack_repo',
3281
3426
    'RepositoryFormatKnitPack5RichRootBroken',
3282
3427
    )
3283
3428
format_registry.register_lazy(
3284
3429
    'Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n',
3285
 
    'bzrlib.repofmt.knitpack_repo',
 
3430
    'bzrlib.repofmt.pack_repo',
3286
3431
    'RepositoryFormatKnitPack6',
3287
3432
    )
3288
3433
format_registry.register_lazy(
3289
3434
    'Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n',
3290
 
    'bzrlib.repofmt.knitpack_repo',
 
3435
    'bzrlib.repofmt.pack_repo',
3291
3436
    'RepositoryFormatKnitPack6RichRoot',
3292
3437
    )
3293
3438
format_registry.register_lazy(
3301
3446
format_registry.register_lazy(
3302
3447
    ("Bazaar development format 2 with subtree support "
3303
3448
        "(needs bzr.dev from before 1.8)\n"),
3304
 
    'bzrlib.repofmt.knitpack_repo',
 
3449
    'bzrlib.repofmt.pack_repo',
3305
3450
    'RepositoryFormatPackDevelopment2Subtree',
3306
3451
    )
3307
3452
format_registry.register_lazy(
3344
3489
        self.target.fetch(self.source, revision_id=revision_id)
3345
3490
 
3346
3491
    @needs_write_lock
3347
 
    def fetch(self, revision_id=None, find_ghosts=False,
 
3492
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
3348
3493
            fetch_spec=None):
3349
3494
        """Fetch the content required to construct revision_id.
3350
3495
 
3352
3497
 
3353
3498
        :param revision_id: if None all content is copied, if NULL_REVISION no
3354
3499
                            content is copied.
 
3500
        :param pb: ignored.
3355
3501
        :return: None.
3356
3502
        """
3357
3503
        ui.ui_factory.warn_experimental_format_fetch(self)
3816
3962
                  len(revision_ids))
3817
3963
 
3818
3964
    @needs_write_lock
3819
 
    def fetch(self, revision_id=None, find_ghosts=False,
 
3965
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
3820
3966
            fetch_spec=None):
3821
3967
        """See InterRepository.fetch()."""
3822
3968
        if fetch_spec is not None:
3852
3998
        # Walk though all revisions; get inventory deltas, copy referenced
3853
3999
        # texts that delta references, insert the delta, revision and
3854
4000
        # signature.
3855
 
        pb = ui.ui_factory.nested_progress_bar()
 
4001
        if pb is None:
 
4002
            my_pb = ui.ui_factory.nested_progress_bar()
 
4003
            pb = my_pb
 
4004
        else:
 
4005
            symbol_versioning.warn(
 
4006
                symbol_versioning.deprecated_in((1, 14, 0))
 
4007
                % "pb parameter to fetch()")
 
4008
            my_pb = None
3856
4009
        try:
3857
4010
            self._fetch_all_revisions(revision_ids, pb)
3858
4011
        finally:
3859
 
            pb.finished()
 
4012
            if my_pb is not None:
 
4013
                my_pb.finished()
3860
4014
        return len(revision_ids), 0
3861
4015
 
3862
4016
    def _get_basis(self, first_revision_id):
3934
4088
        pb.finished()
3935
4089
 
3936
4090
 
 
4091
_unescape_map = {
 
4092
    'apos':"'",
 
4093
    'quot':'"',
 
4094
    'amp':'&',
 
4095
    'lt':'<',
 
4096
    'gt':'>'
 
4097
}
 
4098
 
 
4099
 
 
4100
def _unescaper(match, _map=_unescape_map):
 
4101
    code = match.group(1)
 
4102
    try:
 
4103
        return _map[code]
 
4104
    except KeyError:
 
4105
        if not code.startswith('#'):
 
4106
            raise
 
4107
        return unichr(int(code[1:])).encode('utf8')
 
4108
 
 
4109
 
 
4110
_unescape_re = None
 
4111
 
 
4112
 
 
4113
def _unescape_xml(data):
 
4114
    """Unescape predefined XML entities in a string of data."""
 
4115
    global _unescape_re
 
4116
    if _unescape_re is None:
 
4117
        _unescape_re = re.compile('\&([^;]*);')
 
4118
    return _unescape_re.sub(_unescaper, data)
 
4119
 
 
4120
 
3937
4121
class _VersionedFileChecker(object):
3938
4122
 
3939
4123
    def __init__(self, repository, text_key_references=None, ancestors=None):
3998
4182
        return wrong_parents, unused_keys
3999
4183
 
4000
4184
 
 
4185
def _old_get_graph(repository, revision_id):
 
4186
    """DO NOT USE. That is all. I'm serious."""
 
4187
    graph = repository.get_graph()
 
4188
    revision_graph = dict(((key, value) for key, value in
 
4189
        graph.iter_ancestry([revision_id]) if value is not None))
 
4190
    return _strip_NULL_ghosts(revision_graph)
 
4191
 
 
4192
 
4001
4193
def _strip_NULL_ghosts(revision_graph):
4002
4194
    """Also don't use this. more compatibility code for unmigrated clients."""
4003
4195
    # Filter ghosts, and null:
4176
4368
                parse_result = deserialiser.parse_text_bytes(
4177
4369
                    inventory_delta_bytes)
4178
4370
            except inventory_delta.IncompatibleInventoryDelta, err:
4179
 
                mutter("Incompatible delta: %s", err.msg)
 
4371
                trace.mutter("Incompatible delta: %s", err.msg)
4180
4372
                raise errors.IncompatibleRevision(self.target_repo._format)
4181
4373
            basis_id, new_id, rich_root, tree_refs, inv_delta = parse_result
4182
4374
            revision_id = new_id
4517
4709
    except StopIteration:
4518
4710
        # No more history
4519
4711
        return
 
4712