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
94
99
def __init__(self, repository, parents, config, timestamp=None,
95
100
timezone=None, committer=None, revprops=None,
96
revision_id=None, lossy=False):
97
102
"""Initiate a CommitBuilder.
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
109
113
self._config = config
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.
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
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)
251
254
def finish_inventory(self):
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
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
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,
1814
timestamp, timezone, committer, revprops, revision_id)
1811
1815
self.start_write_group()
2059
2063
w = self.inventories
2060
2064
pb = ui.ui_factory.nested_progress_bar()
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))
2071
def _find_text_key_references_from_xml_inventory_lines(self,
2073
"""Core routine for extracting references to texts from inventories.
2075
This performs the translation of xml lines to revision ids.
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.
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)
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
2099
unescape_revid_cache = {}
2100
unescape_fileid_cache = {}
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
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)
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')
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
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.
2128
revision_id = unescape_revid_cache[revision_id]
2130
unescaped = unescape(revision_id)
2131
unescape_revid_cache[revision_id] = unescaped
2132
revision_id = unescaped
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.
2139
file_id = unescape_fileid_cache[file_id]
2141
unescaped = unescape(file_id)
2142
unescape_fileid_cache[file_id] = unescaped
2145
key = (file_id, revision_id)
2146
setdefault(key, False)
2147
if revision_id == line_key[-1]:
2067
2151
def _inventory_xml_lines_for_keys(self, keys):
2068
2152
"""Get a line iterator of the sort needed for findind references.
2099
2183
revision_ids. Each altered file-ids has the exact revision_ids that
2100
2184
altered it listed explicitly.
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
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)
2521
2605
inv = self.get_inventory(revision_id)
2522
return InventoryRevisionTree(self, inv, revision_id)
2606
return RevisionTree(self, inv, revision_id)
2524
2608
def revision_trees(self, revision_ids):
2525
2609
"""Return Trees for revisions in this repository.
2773
2855
except UnicodeDecodeError:
2774
2856
raise errors.NonAsciiRevisionId(method, self)
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
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.
2785
if not self.is_locked():
2786
raise AssertionError()
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:
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)
2801
def _check_for_inconsistent_revision_parents(self):
2802
inconsistencies = list(self._find_inconsistent_revision_parents())
2804
raise errors.BzrCheckError(
2805
"Revision knit has inconsistent parents.")
2867
raise NotImplementedError(self.revision_graph_can_have_wrong_parents)
2808
2870
def install_revision(repository, rev, revision_tree):
2945
class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
3007
class RepositoryFormatRegistry(registry.FormatRegistry):
2946
3008
"""Repository format registry."""
3010
def __init__(self, other_registry=None):
3011
super(RepositoryFormatRegistry, self).__init__(other_registry)
3012
self._extra_formats = []
3014
def register(self, format):
3015
"""Register a new repository format."""
3016
super(RepositoryFormatRegistry, self).register(
3017
format.get_format_string(), format)
3019
def remove(self, format):
3020
"""Remove a registered repository format."""
3021
super(RepositoryFormatRegistry, self).remove(
3022
format.get_format_string())
3024
def register_extra(self, format):
3025
"""Register a repository format that can not be used in a metadir.
3027
This is mainly useful to allow custom repository formats, such as older
3028
Bazaar formats and foreign formats, to be tested.
3030
self._extra_formats.append(registry._ObjectGetter(format))
3032
def remove_extra(self, format):
3033
"""Remove an extra repository format.
3035
self._extra_formats.remove(registry._ObjectGetter(format))
3037
def register_extra_lazy(self, module_name, member_name):
3038
"""Register a repository format lazily.
3040
self._extra_formats.append(
3041
registry._LazyObjectGetter(module_name, member_name))
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
3048
def _get_extra(self):
3050
for getter in self._extra_formats:
3051
f = getter.get_obj()
3058
"""Return all repository formats, even those not usable in metadirs.
3060
return [self.get(k) for k in self.keys()] + self._get_extra()
2954
3063
network_format_registry = registry.FormatRegistry()
2955
3064
"""Registry of formats indexed by their network name.
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
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)
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,
3217
versionedfile_class=None,
3218
versionedfile_kwargs={},
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,
3227
file_mode=file_mode,
3228
versionedfile_class=versionedfile_class,
3229
versionedfile_kwargs=versionedfile_kwargs,
3109
3232
def initialize(self, a_bzrdir, shared=False):
3110
3233
"""Initialize a repository of this format in a_bzrdir.
3223
3337
return self.get_format_string()
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',
3349
network_format_registry.register_lazy(
3350
"Bazaar-NG branch, format 6\n",
3351
'bzrlib.repofmt.weaverepo',
3352
'RepositoryFormat6',
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')
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.
3232
3371
format_registry.register_lazy(
3372
'Bazaar-NG Repository format 7',
3373
'bzrlib.repofmt.weaverepo',
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',
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',
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',
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',
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',
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',
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',
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',
3293
3438
format_registry.register_lazy(
3852
3998
# Walk though all revisions; get inventory deltas, copy referenced
3853
3999
# texts that delta references, insert the delta, revision and
3855
pb = ui.ui_factory.nested_progress_bar()
4002
my_pb = ui.ui_factory.nested_progress_bar()
4005
symbol_versioning.warn(
4006
symbol_versioning.deprecated_in((1, 14, 0))
4007
% "pb parameter to fetch()")
3857
4010
self._fetch_all_revisions(revision_ids, pb)
4012
if my_pb is not None:
3860
4014
return len(revision_ids), 0
3862
4016
def _get_basis(self, first_revision_id):
4100
def _unescaper(match, _map=_unescape_map):
4101
code = match.group(1)
4105
if not code.startswith('#'):
4107
return unichr(int(code[1:])).encode('utf8')
4113
def _unescape_xml(data):
4114
"""Unescape predefined XML entities in a string of data."""
4116
if _unescape_re is None:
4117
_unescape_re = re.compile('\&([^;]*);')
4118
return _unescape_re.sub(_unescaper, data)
3937
4121
class _VersionedFileChecker(object):
3939
4123
def __init__(self, repository, text_key_references=None, ancestors=None):
3998
4182
return wrong_parents, unused_keys
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)
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: