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
19
17
from bzrlib.lazy_import import lazy_import
20
18
lazy_import(globals(), """
24
22
from bzrlib import (
76
73
record_root_entry = True
77
74
# whether this commit builder supports the record_entry_contents interface
78
75
supports_record_entry_contents = False
79
# whether this commit builder will automatically update the branch that is
81
updates_branch = False
83
def __init__(self, repository, parents, config_stack, timestamp=None,
77
def __init__(self, repository, parents, config, timestamp=None,
84
78
timezone=None, committer=None, revprops=None,
85
79
revision_id=None, lossy=False):
86
80
"""Initiate a CommitBuilder.
95
89
:param lossy: Whether to discard data that can not be natively
96
90
represented, when pushing to a foreign VCS
98
self._config_stack = config_stack
99
93
self._lossy = lossy
101
95
if committer is None:
102
self._committer = self._config_stack.get('email')
96
self._committer = self._config.username()
103
97
elif not isinstance(committer, unicode):
104
98
self._committer = committer.decode() # throw if non-ascii
347
341
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()
350
def create(controldir):
351
"""Construct the current default format repository in controldir."""
352
return RepositoryFormat.get_default_format().initialize(controldir)
356
def create(a_bzrdir):
357
"""Construct the current default format repository in a_bzrdir."""
358
return RepositoryFormat.get_default_format().initialize(a_bzrdir)
354
def __init__(self, _format, controldir, control_files):
360
def __init__(self, _format, a_bzrdir, control_files):
355
361
"""instantiate a Repository.
357
363
:param _format: The format of the repository on disk.
358
:param controldir: The ControlDir of the repository.
364
:param a_bzrdir: The BzrDir of the repository.
359
365
:param control_files: Control files to use for locking, etc.
361
367
# In the future we will have a single api for all stores for
364
370
super(Repository, self).__init__()
365
371
self._format = _format
366
372
# the following are part of the public API for Repository:
367
self.bzrdir = controldir
373
self.bzrdir = a_bzrdir
368
374
self.control_files = control_files
375
self._transport = control_files._transport
376
self.base = self._transport.base
370
378
self._write_group = None
371
379
# Additional places to query for data.
552
560
def __init__(self):
553
561
self.first_call = True
555
def __call__(self, controldir):
556
# On the first call, the parameter is always the controldir
563
def __call__(self, bzrdir):
564
# On the first call, the parameter is always the bzrdir
557
565
# containing the current repo.
558
566
if not self.first_call:
560
repository = controldir.open_repository()
568
repository = bzrdir.open_repository()
561
569
except errors.NoRepositoryPresent:
564
572
return False, ([], repository)
565
573
self.first_call = False
566
value = (controldir.list_branches(), None)
574
value = (bzrdir.list_branches(), None)
567
575
return True, value
570
for branches, repository in controldir.ControlDir.find_bzrdirs(
578
for branches, repository in bzrdir.BzrDir.find_bzrdirs(
571
579
self.user_transport, evaluate=Evaluator()):
572
580
if branches is not None:
573
581
ret.extend(branches)
607
615
For instance, if the repository is at URL/.bzr/repository,
608
616
Repository.open(URL) -> a Repository instance.
610
control = controldir.ControlDir.open(base)
618
control = bzrdir.BzrDir.open(base)
611
619
return control.open_repository()
613
621
def copy_content_into(self, destination, revision_id=None):
677
679
def _resume_write_group(self, tokens):
678
680
raise errors.UnsuspendableWriteGroup(self)
680
def fetch(self, source, revision_id=None, find_ghosts=False):
682
def fetch(self, source, revision_id=None, find_ghosts=False,
681
684
"""Fetch the content required to construct revision_id from source.
683
If revision_id is None, then all content is copied.
686
If revision_id is None and fetch_spec is None, then all content is
685
689
fetch() may not be used when the repository is in a write group -
686
690
either finish the current write group before using fetch, or use
692
696
:param revision_id: If specified, all the content needed for this
693
697
revision ID will be copied to the target. Fetch will determine for
694
698
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.")
696
707
if self.is_in_write_group():
697
708
raise errors.InternalBzrError(
698
709
"May not fetch while in a write group.")
708
720
self.get_revision(revision_id)
710
722
inter = InterRepository.get(source, self)
711
return inter.fetch(revision_id=revision_id, find_ghosts=find_ghosts)
723
return inter.fetch(revision_id=revision_id,
724
find_ghosts=find_ghosts, fetch_spec=fetch_spec)
713
726
def create_bundle(self, target, base, fileobj, format=None):
714
727
return serializer.write_bundle(self, target, base, fileobj, format)
716
def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
729
def get_commit_builder(self, branch, parents, config, timestamp=None,
717
730
timezone=None, committer=None, revprops=None,
718
731
revision_id=None, lossy=False):
719
732
"""Obtain a CommitBuilder for this repository.
721
734
:param branch: Branch to commit to.
722
735
:param parents: Revision ids of the parents of the new revision.
723
:param config_stack: Configuration stack to use.
736
:param config: Configuration to use.
724
737
:param timestamp: Optional timestamp recorded for commit.
725
738
:param timezone: Optional timezone for timestamp.
726
739
:param committer: Optional committer to set for commit.
749
def clone(self, controldir, revision_id=None):
750
"""Clone this repository into controldir using the current format.
762
def clone(self, a_bzrdir, revision_id=None):
763
"""Clone this repository into a_bzrdir using the current format.
752
765
Currently no check is made that the format of this repository and
753
766
the bzrdir format are compatible. FIXME RBC 20060201.
757
770
# TODO: deprecate after 0.16; cloning this with all its settings is
758
771
# probably not very useful -- mbp 20070423
759
dest_repo = self._create_sprouting_repo(
760
controldir, shared=self.is_shared())
772
dest_repo = self._create_sprouting_repo(a_bzrdir, shared=self.is_shared())
761
773
self.copy_content_into(dest_repo, revision_id)
930
942
parent_ids.discard(_mod_revision.NULL_REVISION)
931
943
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)
933
955
def iter_files_bytes(self, desired_files):
934
956
"""Iterate through file versions.
981
1003
raise AssertionError('_iter_for_revno returned too much history')
982
1004
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)
984
1017
def is_shared(self):
985
1018
"""Return True if this repository is flagged as a shared repository."""
986
1019
raise NotImplementedError(self.is_shared)
1024
1057
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)
1026
1093
def pack(self, hint=None, clean_obsolete_packs=False):
1027
1094
"""Compress the data within the repository.
1104
1171
[parents_provider, other_repository._make_parents_provider()])
1105
1172
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)
1107
1187
@needs_write_lock
1108
1188
def set_make_working_trees(self, new_value):
1109
1189
"""Set the policy flag for making working trees when creating branches.
1127
1207
self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1129
1209
@needs_read_lock
1130
def verify_revision_signature(self, revision_id, gpg_strategy):
1210
def verify_revision(self, revision_id, gpg_strategy):
1131
1211
"""Verify the signature on a revision.
1133
1213
:param revision_id: the revision to verify
1134
1214
:gpg_strategy: the GPGStrategy object to used
1136
1216
:return: gpg.SIGNATURE_VALID or a failed SIGNATURE_ value
1138
1218
if not self.has_signature_for_revision_id(revision_id):
1145
1225
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
1159
1227
def has_signature_for_revision_id(self, revision_id):
1160
1228
"""Query for a revision signature for revision_id in the repository."""
1161
1229
raise NotImplementedError(self.has_signature_for_revision_id)
1193
1261
if branch is None:
1194
conf = config.GlobalStack()
1262
conf = config.GlobalConfig()
1196
conf = branch.get_config_stack()
1197
if 'format_deprecation' in conf.get('suppress_warnings'):
1264
conf = branch.get_config()
1265
if conf.suppress_warning('format_deprecation'):
1199
1267
warning("Format %s for %s is deprecated -"
1200
1268
" please use 'bzr upgrade' to get better performance"
1260
1328
"""Returns the policy for making working trees on new branches."""
1261
1329
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())
1274
1332
class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
1275
1333
"""Repository format registry."""
1277
1335
def get_default(self):
1278
1336
"""Return the current default format."""
1279
return controldir.format_registry.make_bzrdir('default').repository_format
1337
from bzrlib import bzrdir
1338
return bzrdir.format_registry.make_bzrdir('default').repository_format
1282
1341
network_format_registry = registry.FormatRegistry()
1371
1430
rich_root_data = None
1372
1431
# Does this format support explicitly versioned directories?
1373
1432
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
1380
1434
def __repr__(self):
1381
1435
return "%s()" % self.__class__.__name__
1387
1441
def __ne__(self, other):
1388
1442
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
return format_registry.get(format_string)
1456
except errors.NoSuchFile:
1457
raise errors.NoRepositoryPresent(a_bzrdir)
1459
raise errors.UnknownFormatError(format=format_string,
1463
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1464
def register_format(klass, format):
1465
format_registry.register(format)
1468
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1469
def unregister_format(klass, format):
1470
format_registry.remove(format)
1473
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1474
def get_default_format(klass):
1475
"""Return the current default format."""
1476
return format_registry.get_default()
1478
def get_format_string(self):
1479
"""Return the ASCII format string that identifies this format.
1481
Note that in pre format ?? repositories the format string is
1482
not permitted nor written to disk.
1484
raise NotImplementedError(self.get_format_string)
1390
1486
def get_format_description(self):
1391
1487
"""Return the short description for this format."""
1392
1488
raise NotImplementedError(self.get_format_description)
1394
def initialize(self, controldir, shared=False):
1395
"""Initialize a repository of this format in controldir.
1490
def initialize(self, a_bzrdir, shared=False):
1491
"""Initialize a repository of this format in a_bzrdir.
1397
:param controldir: The controldir to put the new repository in it.
1493
:param a_bzrdir: The bzrdir to put the new repository in it.
1398
1494
:param shared: The repository should be initialized as a sharable one.
1399
1495
:returns: The new repository object.
1401
1497
This may raise UninitializableFormat if shared repository are not
1402
compatible the controldir.
1498
compatible the a_bzrdir.
1404
1500
raise NotImplementedError(self.initialize)
1441
1537
'Does not support nested trees', target_format,
1442
1538
from_format=self)
1444
def open(self, controldir, _found=False):
1445
"""Return an instance of this format for a controldir.
1540
def open(self, a_bzrdir, _found=False):
1541
"""Return an instance of this format for the bzrdir a_bzrdir.
1447
1543
_found is a private parameter, do not use it.
1449
1545
raise NotImplementedError(self.open)
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']
1547
def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
1548
from bzrlib.bzrdir import BzrDir, RepoInitHookParams
1549
hooks = BzrDir.hooks['post_repo_init']
1456
params = RepoInitHookParams(repository, self, controldir, shared)
1552
params = RepoInitHookParams(repository, self, a_bzrdir, shared)
1457
1553
for hook in hooks:
1461
class RepositoryFormatMetaDir(bzrdir.BzrFormat, RepositoryFormat):
1557
class MetaDirRepositoryFormat(RepositoryFormat):
1462
1558
"""Common base class for the new repositories using the metadir layout."""
1464
1560
rich_root_data = False
1465
1561
supports_tree_reference = False
1466
1562
supports_external_lookups = False
1467
1563
supports_leaving_lock = True
1468
supports_nesting_repositories = True
1471
1566
def _matchingbzrdir(self):
1474
1569
return matching
1476
1571
def __init__(self):
1477
RepositoryFormat.__init__(self)
1478
bzrdir.BzrFormat.__init__(self)
1572
super(MetaDirRepositoryFormat, self).__init__()
1480
1574
def _create_control_files(self, a_bzrdir):
1481
1575
"""Create the required files and the initial control_files object."""
1506
1600
control_files.unlock()
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)
1602
def network_name(self):
1603
"""Metadir formats have matching disk and network format strings."""
1604
return self.get_format_string()
1532
1607
# formats which have no format string are not discoverable or independently
1533
1608
# creatable on disk, so are not registered in format_registry. They're
1534
1609
# all in bzrlib.repofmt.knitreponow. When an instance of one of these is
1535
# needed, it's constructed directly by the ControlDir. Non-native formats where
1610
# needed, it's constructed directly by the BzrDir. Non-native formats where
1536
1611
# the repository is not separately opened are similar.
1538
1613
format_registry.register_lazy(
1649
1724
self.target.fetch(self.source, revision_id=revision_id)
1651
1726
@needs_write_lock
1652
def fetch(self, revision_id=None, find_ghosts=False):
1727
def fetch(self, revision_id=None, find_ghosts=False,
1653
1729
"""Fetch the content required to construct revision_id.
1655
1731
The content is copied from self.source to self.target.
1735
1811
# trigger an assertion if not such
1736
1812
repo._format.get_format_string()
1737
1813
self.repo_dir = repo.bzrdir
1738
pb.update(gettext('Moving repository to repository.backup'))
1814
pb.update('Moving repository to repository.backup')
1739
1815
self.repo_dir.transport.move('repository', 'repository.backup')
1740
1816
backup_transport = self.repo_dir.transport.clone('repository.backup')
1741
1817
repo._format.check_conversion_target(self.target_format)
1742
1818
self.source_repo = repo._format.open(self.repo_dir,
1744
1820
_override_transport=backup_transport)
1745
pb.update(gettext('Creating new repository'))
1821
pb.update('Creating new repository')
1746
1822
converted = self.target_format.initialize(self.repo_dir,
1747
1823
self.source_repo.is_shared())
1748
1824
converted.lock_write()
1750
pb.update(gettext('Copying content'))
1826
pb.update('Copying content')
1751
1827
self.source_repo.copy_content_into(converted)
1753
1829
converted.unlock()
1754
pb.update(gettext('Deleting old repository content'))
1830
pb.update('Deleting old repository content')
1755
1831
self.repo_dir.transport.delete_tree('repository.backup')
1756
ui.ui_factory.note(gettext('repository converted'))
1832
ui.ui_factory.note('repository converted')