860
859
# versioned roots do not change unless the tree found a change.
863
class RepositoryWriteLockResult(object):
864
"""The result of write locking a repository.
866
:ivar repository_token: The token obtained from the underlying lock, or
868
:ivar unlock: A callable which will unlock the lock.
871
def __init__(self, unlock, repository_token):
872
self.repository_token = repository_token
876
862
######################################################################
880
class Repository(_RelockDebugMixin, bzrdir.ControlComponent):
866
class Repository(_RelockDebugMixin):
881
867
"""Repository holding history for one or more branches.
883
869
The repository holds and retrieves historical information including
1042
1028
:seealso: add_inventory, for the contract.
1044
inv_lines = self._serializer.write_inventory_to_lines(inv)
1030
inv_lines = self._serialise_inventory_to_lines(inv)
1045
1031
return self._inventory_add_lines(revision_id, parents,
1046
1032
inv_lines, check_content=False)
1305
1291
:param _format: The format of the repository on disk.
1306
1292
:param a_bzrdir: The BzrDir of the repository.
1294
In the future we will have a single api for all stores for
1295
getting file texts, inventories and revisions, then
1296
this construct will accept instances of those things.
1308
# In the future we will have a single api for all stores for
1309
# getting file texts, inventories and revisions, then
1310
# this construct will accept instances of those things.
1311
1298
super(Repository, self).__init__()
1312
1299
self._format = _format
1313
1300
# the following are part of the public API for Repository:
1328
1315
# rather copying them?
1329
1316
self._safe_to_return_from_cache = False
1332
def user_transport(self):
1333
return self.bzrdir.user_transport
1336
def control_transport(self):
1337
return self._transport
1339
1318
def __repr__(self):
1340
1319
if self._fallback_repositories:
1341
1320
return '%s(%r, fallback_repositories=%r)' % (
1389
1368
data during reads, and allows a 'write_group' to be obtained. Write
1390
1369
groups must be used for actual data insertion.
1392
A token should be passed in if you know that you have locked the object
1393
some other way, and need to synchronise this object's state with that
1396
XXX: this docstring is duplicated in many places, e.g. lockable_files.py
1398
1371
:param token: if this is already locked, then lock_write will fail
1399
1372
unless the token matches the existing lock.
1400
1373
:returns: a token if this instance supports tokens, otherwise None.
1403
1376
:raises MismatchedToken: if the specified token doesn't match the token
1404
1377
of the existing lock.
1405
1378
:seealso: start_write_group.
1406
:return: A RepositoryWriteLockResult.
1380
A token should be passed in if you know that you have locked the object
1381
some other way, and need to synchronise this object's state with that
1384
XXX: this docstring is duplicated in many places, e.g. lockable_files.py
1408
1386
locked = self.is_locked()
1409
token = self.control_files.lock_write(token=token)
1387
result = self.control_files.lock_write(token=token)
1411
1389
self._warn_if_deprecated()
1412
1390
self._note_lock('w')
1414
1392
# Writes don't affect fallback repos
1415
1393
repo.lock_read()
1416
1394
self._refresh_data()
1417
return RepositoryWriteLockResult(self.unlock, token)
1419
1397
def lock_read(self):
1420
"""Lock the repository for read operations.
1422
:return: An object with an unlock method which will release the lock
1425
1398
locked = self.is_locked()
1426
1399
self.control_files.lock_read()
1497
1469
# now gather global repository information
1498
1470
# XXX: This is available for many repos regardless of listability.
1499
if self.user_transport.listable():
1471
if self.bzrdir.root_transport.listable():
1500
1472
# XXX: do we want to __define len__() ?
1501
1473
# Maybe the versionedfiles object should provide a different
1502
1474
# method to get the number of keys.
1512
1484
:param using: If True, list only branches using this repository.
1514
1486
if using and not self.is_shared():
1515
return self.bzrdir.list_branches()
1488
return [self.bzrdir.open_branch()]
1489
except errors.NotBranchError:
1516
1491
class Evaluator(object):
1518
1493
def __init__(self):
1527
1502
except errors.NoRepositoryPresent:
1530
return False, ([], repository)
1505
return False, (None, repository)
1531
1506
self.first_call = False
1532
value = (bzrdir.list_branches(), None)
1508
value = (bzrdir.open_branch(), None)
1509
except errors.NotBranchError:
1510
value = (None, None)
1533
1511
return True, value
1536
for branches, repository in bzrdir.BzrDir.find_bzrdirs(
1537
self.user_transport, evaluate=Evaluator()):
1538
if branches is not None:
1539
ret.extend(branches)
1514
for branch, repository in bzrdir.BzrDir.find_bzrdirs(
1515
self.bzrdir.root_transport, evaluate=Evaluator()):
1516
if branch is not None:
1517
branches.append(branch)
1540
1518
if not using and repository is not None:
1541
ret.extend(repository.find_branches())
1519
branches.extend(repository.find_branches())
1544
1522
@needs_read_lock
1545
1523
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1923
1901
rev = self._serializer.read_revision_from_string(text)
1924
1902
yield (revid, rev)
1905
def get_revision_xml(self, revision_id):
1906
# TODO: jam 20070210 This shouldn't be necessary since get_revision
1907
# would have already do it.
1908
# TODO: jam 20070210 Just use _serializer.write_revision_to_string()
1909
# TODO: this can't just be replaced by:
1910
# return self._serializer.write_revision_to_string(
1911
# self.get_revision(revision_id))
1912
# as cStringIO preservers the encoding unlike write_revision_to_string
1913
# or some other call down the path.
1914
rev = self.get_revision(revision_id)
1915
rev_tmp = cStringIO.StringIO()
1916
# the current serializer..
1917
self._serializer.write_revision(rev, rev_tmp)
1919
return rev_tmp.getvalue()
1926
1921
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1927
1922
"""Produce a generator of revision deltas.
2171
2166
selected_keys = set((revid,) for revid in revision_ids)
2172
2167
w = _inv_weave or self.inventories
2173
return self._find_file_ids_from_xml_inventory_lines(
2174
w.iter_lines_added_or_present_in_keys(
2175
selected_keys, pb=None),
2168
pb = ui.ui_factory.nested_progress_bar()
2170
return self._find_file_ids_from_xml_inventory_lines(
2171
w.iter_lines_added_or_present_in_keys(
2172
selected_keys, pb=pb),
2178
2177
def iter_files_bytes(self, desired_files):
2179
2178
"""Iterate through file versions.
2441
2440
result.revision_id, revision_id))
2443
def _serialise_inventory(self, inv):
2444
return self._serializer.write_inventory_to_string(inv)
2446
def _serialise_inventory_to_lines(self, inv):
2447
return self._serializer.write_inventory_to_lines(inv)
2444
2449
def get_serializer_format(self):
2445
2450
return self._serializer.format_num
2511
2516
next_id = parents[0]
2519
def get_revision_inventory(self, revision_id):
2520
"""Return inventory of a past revision."""
2521
# TODO: Unify this with get_inventory()
2522
# bzr 0.0.6 and later imposes the constraint that the inventory_id
2523
# must be the same as its revision, so this is trivial.
2524
if revision_id is None:
2525
# This does not make sense: if there is no revision,
2526
# then it is the current tree inventory surely ?!
2527
# and thus get_root_id() is something that looks at the last
2528
# commit on the branch, and the get_root_id is an inventory check.
2529
raise NotImplementedError
2530
# return Inventory(self.get_root_id())
2532
return self.get_inventory(revision_id)
2513
2534
def is_shared(self):
2514
2535
"""Return True if this repository is flagged as a shared repository."""
2515
2536
raise NotImplementedError(self.is_shared)
2549
2570
return RevisionTree(self, Inventory(root_id=None),
2550
2571
_mod_revision.NULL_REVISION)
2552
inv = self.get_inventory(revision_id)
2573
inv = self.get_revision_inventory(revision_id)
2553
2574
return RevisionTree(self, inv, revision_id)
2555
2576
def revision_trees(self, revision_ids):
2608
2629
keys = tsort.topo_sort(parent_map)
2609
2630
return [None] + list(keys)
2611
def pack(self, hint=None, clean_obsolete_packs=False):
2632
def pack(self, hint=None):
2612
2633
"""Compress the data within the repository.
2614
2635
This operation only makes sense for some repository types. For other
2624
2645
obtained from the result of commit_write_group(). Out of
2625
2646
date hints are simply ignored, because concurrent operations
2626
2647
can obsolete them rapidly.
2628
:param clean_obsolete_packs: Clean obsolete packs immediately after
2632
2650
def get_transaction(self):
2657
2675
def _make_parents_provider(self):
2661
def get_known_graph_ancestry(self, revision_ids):
2662
"""Return the known graph for a set of revision ids and their ancestors.
2664
st = static_tuple.StaticTuple
2665
revision_keys = [st(r_id).intern() for r_id in revision_ids]
2666
known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
2667
return graph.GraphThunkIdsToKeys(known_graph)
2669
2678
def get_graph(self, other_repository=None):
2670
2679
"""Return the graph walker for this repository format"""
2671
2680
parents_provider = self._make_parents_provider()
3070
3079
pack_compresses = False
3071
3080
# Does the repository inventory storage understand references to trees?
3072
3081
supports_tree_reference = None
3073
# Is the format experimental ?
3074
experimental = False
3077
return "%s()" % self.__class__.__name__
3084
return "<%s>" % self.__class__.__name__
3079
3086
def __eq__(self, other):
3080
3087
# format objects are generally stateless
3199
3206
raise NotImplementedError(self.open)
3201
def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
3202
from bzrlib.bzrdir import BzrDir, RepoInitHookParams
3203
hooks = BzrDir.hooks['post_repo_init']
3206
params = RepoInitHookParams(repository, self, a_bzrdir, shared)
3211
3209
class MetaDirRepositoryFormat(RepositoryFormat):
3212
3210
"""Common base class for the new repositories using the metadir layout."""
3418
3416
:param revision_id: if None all content is copied, if NULL_REVISION no
3419
3417
content is copied.
3418
:param pb: optional progress bar to use for progress reports. If not
3419
provided a default one will be created.
3423
ui.ui_factory.warn_experimental_format_fetch(self)
3424
from bzrlib.fetch import RepoFetcher
3425
# See <https://launchpad.net/bugs/456077> asking for a warning here
3426
if self.source._format.network_name() != self.target._format.network_name():
3427
ui.ui_factory.show_user_warning('cross_format_fetch',
3428
from_format=self.source._format,
3429
to_format=self.target._format)
3430
f = RepoFetcher(to_repository=self.target,
3422
f = _mod_fetch.RepoFetcher(to_repository=self.target,
3431
3423
from_repository=self.source,
3432
3424
last_revision=revision_id,
3433
3425
fetch_spec=fetch_spec,
3434
find_ghosts=find_ghosts)
3426
pb=pb, find_ghosts=find_ghosts)
3436
3428
def _walk_to_common_revisions(self, revision_ids):
3437
3429
"""Walk out from revision_ids in source to revisions target has.
4011
4003
"""See InterRepository.fetch()."""
4012
4004
if fetch_spec is not None:
4013
4005
raise AssertionError("Not implemented yet...")
4014
ui.ui_factory.warn_experimental_format_fetch(self)
4006
# See <https://launchpad.net/bugs/456077> asking for a warning here
4008
# nb this is only active for local-local fetches; other things using
4010
ui.ui_factory.warn_cross_format_fetch(self.source._format,
4011
self.target._format)
4015
4012
if (not self.source.supports_rich_root()
4016
4013
and self.target.supports_rich_root()):
4017
4014
self._converting_to_rich_root = True
4018
4015
self._revision_id_to_root_id = {}
4020
4017
self._converting_to_rich_root = False
4021
# See <https://launchpad.net/bugs/456077> asking for a warning here
4022
if self.source._format.network_name() != self.target._format.network_name():
4023
ui.ui_factory.show_user_warning('cross_format_fetch',
4024
from_format=self.source._format,
4025
to_format=self.target._format)
4026
4018
revision_ids = self.target.search_missing_revision_ids(self.source,
4027
4019
revision_id, find_ghosts=find_ghosts).get_keys()
4028
4020
if not revision_ids:
4097
4089
:param to_convert: The disk object to convert.
4098
4090
:param pb: a progress bar to use for progress information.
4100
pb = ui.ui_factory.nested_progress_bar()
4103
4095
# this is only useful with metadir layouts - separated repo content.
4104
4096
# trigger an assertion if not such
4105
4097
repo._format.get_format_string()
4106
4098
self.repo_dir = repo.bzrdir
4107
pb.update('Moving repository to repository.backup')
4099
self.step('Moving repository to repository.backup')
4108
4100
self.repo_dir.transport.move('repository', 'repository.backup')
4109
4101
backup_transport = self.repo_dir.transport.clone('repository.backup')
4110
4102
repo._format.check_conversion_target(self.target_format)
4111
4103
self.source_repo = repo._format.open(self.repo_dir,
4113
4105
_override_transport=backup_transport)
4114
pb.update('Creating new repository')
4106
self.step('Creating new repository')
4115
4107
converted = self.target_format.initialize(self.repo_dir,
4116
4108
self.source_repo.is_shared())
4117
4109
converted.lock_write()
4119
pb.update('Copying content')
4111
self.step('Copying content')
4120
4112
self.source_repo.copy_content_into(converted)
4122
4114
converted.unlock()
4123
pb.update('Deleting old repository content')
4115
self.step('Deleting old repository content')
4124
4116
self.repo_dir.transport.delete_tree('repository.backup')
4125
4117
ui.ui_factory.note('repository converted')
4119
def step(self, message):
4120
"""Update the pb by a step."""
4122
self.pb.update(message, self.count, self.total)
4129
4125
_unescape_map = {
4311
4307
self._extract_and_insert_inventories(
4312
4308
substream, src_serializer)
4313
4309
elif substream_type == 'inventory-deltas':
4310
ui.ui_factory.warn_cross_format_fetch(src_format,
4311
self.target_repo._format)
4314
4312
self._extract_and_insert_inventory_deltas(
4315
4313
substream, src_serializer)
4316
4314
elif substream_type == 'chk_bytes':