14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
17
19
from bzrlib.lazy_import import lazy_import
18
20
lazy_import(globals(), """
22
24
from bzrlib import (
73
76
record_root_entry = True
74
77
# whether this commit builder supports the record_entry_contents interface
75
78
supports_record_entry_contents = False
79
# whether this commit builder will automatically update the branch that is
81
updates_branch = False
77
def __init__(self, repository, parents, config, timestamp=None,
83
def __init__(self, repository, parents, config_stack, timestamp=None,
78
84
timezone=None, committer=None, revprops=None,
79
85
revision_id=None, lossy=False):
80
86
"""Initiate a CommitBuilder.
89
95
:param lossy: Whether to discard data that can not be natively
90
96
represented, when pushing to a foreign VCS
98
self._config_stack = config_stack
93
99
self._lossy = lossy
95
101
if committer is None:
96
self._committer = self._config.username()
102
self._committer = self._config_stack.get('email')
97
103
elif not isinstance(committer, unicode):
98
104
self._committer = committer.decode() # throw if non-ascii
341
347
self.control_files.break_lock()
344
def _eliminate_revisions_not_present(self, revision_ids):
345
"""Check every revision id in revision_ids to see if we have it.
347
Returns a set of the present revisions.
350
graph = self.get_graph()
351
parent_map = graph.get_parent_map(revision_ids)
352
# The old API returned a list, should this actually be a set?
353
return parent_map.keys()
356
def create(a_bzrdir):
357
"""Construct the current default format repository in a_bzrdir."""
358
return RepositoryFormat.get_default_format().initialize(a_bzrdir)
350
def create(controldir):
351
"""Construct the current default format repository in controldir."""
352
return RepositoryFormat.get_default_format().initialize(controldir)
360
def __init__(self, _format, a_bzrdir, control_files):
354
def __init__(self, _format, controldir, control_files):
361
355
"""instantiate a Repository.
363
357
:param _format: The format of the repository on disk.
364
:param a_bzrdir: The BzrDir of the repository.
358
:param controldir: The ControlDir of the repository.
365
359
:param control_files: Control files to use for locking, etc.
367
361
# In the future we will have a single api for all stores for
370
364
super(Repository, self).__init__()
371
365
self._format = _format
372
366
# the following are part of the public API for Repository:
373
self.bzrdir = a_bzrdir
367
self.bzrdir = controldir
374
368
self.control_files = control_files
375
self._transport = control_files._transport
376
self.base = self._transport.base
378
370
self._write_group = None
379
371
# Additional places to query for data.
560
552
def __init__(self):
561
553
self.first_call = True
563
def __call__(self, bzrdir):
564
# On the first call, the parameter is always the bzrdir
555
def __call__(self, controldir):
556
# On the first call, the parameter is always the controldir
565
557
# containing the current repo.
566
558
if not self.first_call:
568
repository = bzrdir.open_repository()
560
repository = controldir.open_repository()
569
561
except errors.NoRepositoryPresent:
572
564
return False, ([], repository)
573
565
self.first_call = False
574
value = (bzrdir.list_branches(), None)
566
value = (controldir.list_branches(), None)
575
567
return True, value
578
for branches, repository in bzrdir.BzrDir.find_bzrdirs(
570
for branches, repository in controldir.ControlDir.find_bzrdirs(
579
571
self.user_transport, evaluate=Evaluator()):
580
572
if branches is not None:
581
573
ret.extend(branches)
615
607
For instance, if the repository is at URL/.bzr/repository,
616
608
Repository.open(URL) -> a Repository instance.
618
control = bzrdir.BzrDir.open(base)
610
control = controldir.ControlDir.open(base)
619
611
return control.open_repository()
621
613
def copy_content_into(self, destination, revision_id=None):
679
677
def _resume_write_group(self, tokens):
680
678
raise errors.UnsuspendableWriteGroup(self)
682
def fetch(self, source, revision_id=None, find_ghosts=False,
680
def fetch(self, source, revision_id=None, find_ghosts=False):
684
681
"""Fetch the content required to construct revision_id from source.
686
If revision_id is None and fetch_spec is None, then all content is
683
If revision_id is None, then all content is copied.
689
685
fetch() may not be used when the repository is in a write group -
690
686
either finish the current write group before using fetch, or use
696
692
:param revision_id: If specified, all the content needed for this
697
693
revision ID will be copied to the target. Fetch will determine for
698
694
itself which content needs to be copied.
699
:param fetch_spec: If specified, a SearchResult or
700
PendingAncestryResult that describes which revisions to copy. This
701
allows copying multiple heads at once. Mutually exclusive with
704
if fetch_spec is not None and revision_id is not None:
705
raise AssertionError(
706
"fetch_spec and revision_id are mutually exclusive.")
707
696
if self.is_in_write_group():
708
697
raise errors.InternalBzrError(
709
698
"May not fetch while in a write group.")
720
708
self.get_revision(revision_id)
722
710
inter = InterRepository.get(source, self)
723
return inter.fetch(revision_id=revision_id,
724
find_ghosts=find_ghosts, fetch_spec=fetch_spec)
711
return inter.fetch(revision_id=revision_id, find_ghosts=find_ghosts)
726
713
def create_bundle(self, target, base, fileobj, format=None):
727
714
return serializer.write_bundle(self, target, base, fileobj, format)
729
def get_commit_builder(self, branch, parents, config, timestamp=None,
716
def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
730
717
timezone=None, committer=None, revprops=None,
731
718
revision_id=None, lossy=False):
732
719
"""Obtain a CommitBuilder for this repository.
734
721
:param branch: Branch to commit to.
735
722
:param parents: Revision ids of the parents of the new revision.
736
:param config: Configuration to use.
723
:param config_stack: Configuration stack to use.
737
724
:param timestamp: Optional timestamp recorded for commit.
738
725
:param timezone: Optional timezone for timestamp.
739
726
:param committer: Optional committer to set for commit.
762
def clone(self, a_bzrdir, revision_id=None):
763
"""Clone this repository into a_bzrdir using the current format.
749
def clone(self, controldir, revision_id=None):
750
"""Clone this repository into controldir using the current format.
765
752
Currently no check is made that the format of this repository and
766
753
the bzrdir format are compatible. FIXME RBC 20060201.
770
757
# TODO: deprecate after 0.16; cloning this with all its settings is
771
758
# probably not very useful -- mbp 20070423
772
dest_repo = self._create_sprouting_repo(a_bzrdir, shared=self.is_shared())
759
dest_repo = self._create_sprouting_repo(
760
controldir, shared=self.is_shared())
773
761
self.copy_content_into(dest_repo, revision_id)
942
930
parent_ids.discard(_mod_revision.NULL_REVISION)
943
931
return parent_ids
945
def fileids_altered_by_revision_ids(self, revision_ids):
946
"""Find the file ids and versions affected by revisions.
948
:param revisions: an iterable containing revision ids.
949
:return: a dictionary mapping altered file-ids to an iterable of
950
revision_ids. Each altered file-ids has the exact revision_ids
951
that altered it listed explicitly.
953
raise NotImplementedError(self.fileids_altered_by_revision_ids)
955
933
def iter_files_bytes(self, desired_files):
956
934
"""Iterate through file versions.
1003
981
raise AssertionError('_iter_for_revno returned too much history')
1004
982
return (True, partial_history[-1])
1006
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1007
def iter_reverse_revision_history(self, revision_id):
1008
"""Iterate backwards through revision ids in the lefthand history
1010
:param revision_id: The revision id to start with. All its lefthand
1011
ancestors will be traversed.
1013
graph = self.get_graph()
1014
stop_revisions = (None, _mod_revision.NULL_REVISION)
1015
return graph.iter_lefthand_ancestry(revision_id, stop_revisions)
1017
984
def is_shared(self):
1018
985
"""Return True if this repository is flagged as a shared repository."""
1019
986
raise NotImplementedError(self.is_shared)
1057
1024
raise NotImplementedError(self.revision_trees)
1060
@symbol_versioning.deprecated_method(
1061
symbol_versioning.deprecated_in((2, 4, 0)))
1062
def get_ancestry(self, revision_id, topo_sorted=True):
1063
"""Return a list of revision-ids integrated by a revision.
1065
The first element of the list is always None, indicating the origin
1066
revision. This might change when we have history horizons, or
1067
perhaps we should have a new API.
1069
This is topologically sorted.
1071
if 'evil' in debug.debug_flags:
1072
mutter_callsite(2, "get_ancestry is linear with history.")
1073
if _mod_revision.is_null(revision_id):
1075
if not self.has_revision(revision_id):
1076
raise errors.NoSuchRevision(self, revision_id)
1077
graph = self.get_graph()
1079
search = graph._make_breadth_first_searcher([revision_id])
1082
found, ghosts = search.next_with_ghosts()
1083
except StopIteration:
1086
if _mod_revision.NULL_REVISION in keys:
1087
keys.remove(_mod_revision.NULL_REVISION)
1089
parent_map = graph.get_parent_map(keys)
1090
keys = tsort.topo_sort(parent_map)
1091
return [None] + list(keys)
1093
1026
def pack(self, hint=None, clean_obsolete_packs=False):
1094
1027
"""Compress the data within the repository.
1171
1104
[parents_provider, other_repository._make_parents_provider()])
1172
1105
return graph.Graph(parents_provider)
1174
def revision_ids_to_search_result(self, result_set):
1175
"""Convert a set of revision ids to a graph SearchResult."""
1176
result_parents = set()
1177
for parents in self.get_graph().get_parent_map(
1178
result_set).itervalues():
1179
result_parents.update(parents)
1180
included_keys = result_set.intersection(result_parents)
1181
start_keys = result_set.difference(included_keys)
1182
exclude_keys = result_parents.difference(result_set)
1183
result = graph.SearchResult(start_keys, exclude_keys,
1184
len(result_set), result_set)
1187
1107
@needs_write_lock
1188
1108
def set_make_working_trees(self, new_value):
1189
1109
"""Set the policy flag for making working trees when creating branches.
1207
1127
self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1209
1129
@needs_read_lock
1210
def verify_revision(self, revision_id, gpg_strategy):
1130
def verify_revision_signature(self, revision_id, gpg_strategy):
1211
1131
"""Verify the signature on a revision.
1213
1133
:param revision_id: the revision to verify
1214
1134
:gpg_strategy: the GPGStrategy object to used
1216
1136
:return: gpg.SIGNATURE_VALID or a failed SIGNATURE_ value
1218
1138
if not self.has_signature_for_revision_id(revision_id):
1225
1145
return gpg_strategy.verify(signature, plaintext)
1148
def verify_revision_signatures(self, revision_ids, gpg_strategy):
1149
"""Verify revision signatures for a number of revisions.
1151
:param revision_id: the revision to verify
1152
:gpg_strategy: the GPGStrategy object to used
1153
:return: Iterator over tuples with revision id, result and keys
1155
for revid in revision_ids:
1156
(result, key) = self.verify_revision_signature(revid, gpg_strategy)
1157
yield revid, result, key
1227
1159
def has_signature_for_revision_id(self, revision_id):
1228
1160
"""Query for a revision signature for revision_id in the repository."""
1229
1161
raise NotImplementedError(self.has_signature_for_revision_id)
1261
1193
if branch is None:
1262
conf = config.GlobalConfig()
1194
conf = config.GlobalStack()
1264
conf = branch.get_config()
1265
if conf.suppress_warning('format_deprecation'):
1196
conf = branch.get_config_stack()
1197
if 'format_deprecation' in conf.get('suppress_warnings'):
1267
1199
warning("Format %s for %s is deprecated -"
1268
1200
" please use 'bzr upgrade' to get better performance"
1328
1260
"""Returns the policy for making working trees on new branches."""
1329
1261
return not self._transport.has('no-working-trees')
1264
def update_feature_flags(self, updated_flags):
1265
"""Update the feature flags for this branch.
1267
:param updated_flags: Dictionary mapping feature names to necessities
1268
A necessity can be None to indicate the feature should be removed
1270
self._format._update_feature_flags(updated_flags)
1271
self.control_transport.put_bytes('format', self._format.as_string())
1332
1274
class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
1333
1275
"""Repository format registry."""
1335
1277
def get_default(self):
1336
1278
"""Return the current default format."""
1337
from bzrlib import bzrdir
1338
return bzrdir.format_registry.make_bzrdir('default').repository_format
1279
return controldir.format_registry.make_bzrdir('default').repository_format
1341
1282
network_format_registry = registry.FormatRegistry()
1430
1371
rich_root_data = None
1431
1372
# Does this format support explicitly versioned directories?
1432
1373
supports_versioned_directories = None
1374
# Can other repositories be nested into one of this format?
1375
supports_nesting_repositories = None
1376
# Is it possible for revisions to be present without being referenced
1378
supports_unreferenced_revisions = None
1434
1380
def __repr__(self):
1435
1381
return "%s()" % self.__class__.__name__
1441
1387
def __ne__(self, other):
1442
1388
return not self == other
1445
def find_format(klass, a_bzrdir):
1446
"""Return the format for the repository object in a_bzrdir.
1448
This is used by bzr native formats that have a "format" file in
1449
the repository. Other methods may be used by different types of
1453
transport = a_bzrdir.get_repository_transport(None)
1454
format_string = transport.get_bytes("format")
1455
format_string = bzrdir.extract_format_string(format_string)
1456
return format_registry.get(format_string)
1457
except errors.NoSuchFile:
1458
raise errors.NoRepositoryPresent(a_bzrdir)
1460
raise errors.UnknownFormatError(format=format_string,
1464
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1465
def register_format(klass, format):
1466
format_registry.register(format)
1469
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1470
def unregister_format(klass, format):
1471
format_registry.remove(format)
1474
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1475
def get_default_format(klass):
1476
"""Return the current default format."""
1477
return format_registry.get_default()
1479
def get_format_string(self):
1480
"""Return the ASCII format string that identifies this format.
1482
Note that in pre format ?? repositories the format string is
1483
not permitted nor written to disk.
1485
raise NotImplementedError(self.get_format_string)
1487
1390
def get_format_description(self):
1488
1391
"""Return the short description for this format."""
1489
1392
raise NotImplementedError(self.get_format_description)
1491
def initialize(self, a_bzrdir, shared=False):
1492
"""Initialize a repository of this format in a_bzrdir.
1394
def initialize(self, controldir, shared=False):
1395
"""Initialize a repository of this format in controldir.
1494
:param a_bzrdir: The bzrdir to put the new repository in it.
1397
:param controldir: The controldir to put the new repository in it.
1495
1398
:param shared: The repository should be initialized as a sharable one.
1496
1399
:returns: The new repository object.
1498
1401
This may raise UninitializableFormat if shared repository are not
1499
compatible the a_bzrdir.
1402
compatible the controldir.
1501
1404
raise NotImplementedError(self.initialize)
1538
1441
'Does not support nested trees', target_format,
1539
1442
from_format=self)
1541
def open(self, a_bzrdir, _found=False):
1542
"""Return an instance of this format for the bzrdir a_bzrdir.
1444
def open(self, controldir, _found=False):
1445
"""Return an instance of this format for a controldir.
1544
1447
_found is a private parameter, do not use it.
1546
1449
raise NotImplementedError(self.open)
1548
def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
1549
from bzrlib.bzrdir import BzrDir, RepoInitHookParams
1550
hooks = BzrDir.hooks['post_repo_init']
1451
def _run_post_repo_init_hooks(self, repository, controldir, shared):
1452
from bzrlib.controldir import ControlDir, RepoInitHookParams
1453
hooks = ControlDir.hooks['post_repo_init']
1553
params = RepoInitHookParams(repository, self, a_bzrdir, shared)
1456
params = RepoInitHookParams(repository, self, controldir, shared)
1554
1457
for hook in hooks:
1558
class MetaDirRepositoryFormat(RepositoryFormat):
1461
class RepositoryFormatMetaDir(bzrdir.BzrFormat, RepositoryFormat):
1559
1462
"""Common base class for the new repositories using the metadir layout."""
1561
1464
rich_root_data = False
1562
1465
supports_tree_reference = False
1563
1466
supports_external_lookups = False
1564
1467
supports_leaving_lock = True
1468
supports_nesting_repositories = True
1567
1471
def _matchingbzrdir(self):
1601
1506
control_files.unlock()
1603
def network_name(self):
1604
"""Metadir formats have matching disk and network format strings."""
1605
return self.get_format_string()
1509
def find_format(klass, a_bzrdir):
1510
"""Return the format for the repository object in a_bzrdir.
1512
This is used by bzr native formats that have a "format" file in
1513
the repository. Other methods may be used by different types of
1517
transport = a_bzrdir.get_repository_transport(None)
1518
format_string = transport.get_bytes("format")
1519
except errors.NoSuchFile:
1520
raise errors.NoRepositoryPresent(a_bzrdir)
1521
return klass._find_format(format_registry, 'repository', format_string)
1523
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
1525
RepositoryFormat.check_support_status(self,
1526
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
1528
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
1529
recommend_upgrade=recommend_upgrade, basedir=basedir)
1608
1532
# formats which have no format string are not discoverable or independently
1609
1533
# creatable on disk, so are not registered in format_registry. They're
1610
1534
# all in bzrlib.repofmt.knitreponow. When an instance of one of these is
1611
# needed, it's constructed directly by the BzrDir. Non-native formats where
1535
# needed, it's constructed directly by the ControlDir. Non-native formats where
1612
1536
# the repository is not separately opened are similar.
1614
1538
format_registry.register_lazy(
1725
1649
self.target.fetch(self.source, revision_id=revision_id)
1727
1651
@needs_write_lock
1728
def fetch(self, revision_id=None, find_ghosts=False,
1652
def fetch(self, revision_id=None, find_ghosts=False):
1730
1653
"""Fetch the content required to construct revision_id.
1732
1655
The content is copied from self.source to self.target.
1812
1735
# trigger an assertion if not such
1813
1736
repo._format.get_format_string()
1814
1737
self.repo_dir = repo.bzrdir
1815
pb.update('Moving repository to repository.backup')
1738
pb.update(gettext('Moving repository to repository.backup'))
1816
1739
self.repo_dir.transport.move('repository', 'repository.backup')
1817
1740
backup_transport = self.repo_dir.transport.clone('repository.backup')
1818
1741
repo._format.check_conversion_target(self.target_format)
1819
1742
self.source_repo = repo._format.open(self.repo_dir,
1821
1744
_override_transport=backup_transport)
1822
pb.update('Creating new repository')
1745
pb.update(gettext('Creating new repository'))
1823
1746
converted = self.target_format.initialize(self.repo_dir,
1824
1747
self.source_repo.is_shared())
1825
1748
converted.lock_write()
1827
pb.update('Copying content')
1750
pb.update(gettext('Copying content'))
1828
1751
self.source_repo.copy_content_into(converted)
1830
1753
converted.unlock()
1831
pb.update('Deleting old repository content')
1754
pb.update(gettext('Deleting old repository content'))
1832
1755
self.repo_dir.transport.delete_tree('repository.backup')
1833
ui.ui_factory.note('repository converted')
1756
ui.ui_factory.note(gettext('repository converted'))