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 (
32
33
revision as _mod_revision,
33
34
testament as _mod_testament,
36
38
from bzrlib.bundle import serializer
39
from bzrlib.i18n import gettext
39
42
from bzrlib import (
72
76
record_root_entry = True
73
77
# whether this commit builder supports the record_entry_contents interface
74
78
supports_record_entry_contents = False
79
# whether this commit builder will automatically update the branch that is
81
updates_branch = False
76
def __init__(self, repository, parents, config, timestamp=None,
83
def __init__(self, repository, parents, config_stack, timestamp=None,
77
84
timezone=None, committer=None, revprops=None,
78
85
revision_id=None, lossy=False):
79
86
"""Initiate a CommitBuilder.
88
95
:param lossy: Whether to discard data that can not be natively
89
96
represented, when pushing to a foreign VCS
98
self._config_stack = config_stack
92
99
self._lossy = lossy
94
101
if committer is None:
95
self._committer = self._config.username()
102
self._committer = self._config_stack.get('email')
96
103
elif not isinstance(committer, unicode):
97
104
self._committer = committer.decode() # throw if non-ascii
280
287
mutter('abort_write_group failed')
281
288
log_exception_quietly()
282
note('bzr: ERROR (ignored): %s', exc)
289
note(gettext('bzr: ERROR (ignored): %s'), exc)
283
290
self._write_group = None
285
292
def _abort_write_group(self):
340
347
self.control_files.break_lock()
343
def _eliminate_revisions_not_present(self, revision_ids):
344
"""Check every revision id in revision_ids to see if we have it.
346
Returns a set of the present revisions.
349
graph = self.get_graph()
350
parent_map = graph.get_parent_map(revision_ids)
351
# The old API returned a list, should this actually be a set?
352
return parent_map.keys()
355
def create(a_bzrdir):
356
"""Construct the current default format repository in a_bzrdir."""
357
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)
359
def __init__(self, _format, a_bzrdir, control_files):
354
def __init__(self, _format, controldir, control_files):
360
355
"""instantiate a Repository.
362
357
:param _format: The format of the repository on disk.
363
:param a_bzrdir: The BzrDir of the repository.
358
:param controldir: The ControlDir of the repository.
364
359
:param control_files: Control files to use for locking, etc.
366
361
# In the future we will have a single api for all stores for
369
364
super(Repository, self).__init__()
370
365
self._format = _format
371
366
# the following are part of the public API for Repository:
372
self.bzrdir = a_bzrdir
367
self.bzrdir = controldir
373
368
self.control_files = control_files
374
self._transport = control_files._transport
375
self.base = self._transport.base
377
370
self._write_group = None
378
371
# Additional places to query for data.
417
410
if self.__class__ is not other.__class__:
419
return (self._transport.base == other._transport.base)
412
return (self.control_url == other.control_url)
421
414
def is_in_write_group(self):
422
415
"""Return True if there is an open write group.
522
515
if revid and committers:
523
516
result['committers'] = 0
524
517
if revid and revid != _mod_revision.NULL_REVISION:
518
graph = self.get_graph()
526
520
all_committers = set()
527
revisions = self.get_ancestry(revid)
528
# pop the leading None
530
first_revision = None
521
revisions = [r for (r, p) in graph.iter_ancestry([revid])
522
if r != _mod_revision.NULL_REVISION]
531
524
if not committers:
532
525
# ignore the revisions in the middle - just grab first and last
533
526
revisions = revisions[0], revisions[-1]
534
527
for revision in self.get_revisions(revisions):
535
if not first_revision:
536
first_revision = revision
528
if not last_revision:
529
last_revision = revision
538
531
all_committers.add(revision.committer)
539
last_revision = revision
532
first_revision = revision
541
534
result['committers'] = len(all_committers)
542
535
result['firstrev'] = (first_revision.timestamp,
559
552
def __init__(self):
560
553
self.first_call = True
562
def __call__(self, bzrdir):
563
# 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
564
557
# containing the current repo.
565
558
if not self.first_call:
567
repository = bzrdir.open_repository()
560
repository = controldir.open_repository()
568
561
except errors.NoRepositoryPresent:
571
564
return False, ([], repository)
572
565
self.first_call = False
573
value = (bzrdir.list_branches(), None)
566
value = (controldir.list_branches(), None)
574
567
return True, value
577
for branches, repository in bzrdir.BzrDir.find_bzrdirs(
570
for branches, repository in controldir.ControlDir.find_bzrdirs(
578
571
self.user_transport, evaluate=Evaluator()):
579
572
if branches is not None:
580
573
ret.extend(branches)
614
607
For instance, if the repository is at URL/.bzr/repository,
615
608
Repository.open(URL) -> a Repository instance.
617
control = bzrdir.BzrDir.open(base)
610
control = controldir.ControlDir.open(base)
618
611
return control.open_repository()
620
613
def copy_content_into(self, destination, revision_id=None):
678
677
def _resume_write_group(self, tokens):
679
678
raise errors.UnsuspendableWriteGroup(self)
681
def fetch(self, source, revision_id=None, find_ghosts=False,
680
def fetch(self, source, revision_id=None, find_ghosts=False):
683
681
"""Fetch the content required to construct revision_id from source.
685
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.
688
685
fetch() may not be used when the repository is in a write group -
689
686
either finish the current write group before using fetch, or use
695
692
:param revision_id: If specified, all the content needed for this
696
693
revision ID will be copied to the target. Fetch will determine for
697
694
itself which content needs to be copied.
698
:param fetch_spec: If specified, a SearchResult or
699
PendingAncestryResult that describes which revisions to copy. This
700
allows copying multiple heads at once. Mutually exclusive with
703
if fetch_spec is not None and revision_id is not None:
704
raise AssertionError(
705
"fetch_spec and revision_id are mutually exclusive.")
706
696
if self.is_in_write_group():
707
697
raise errors.InternalBzrError(
708
698
"May not fetch while in a write group.")
710
700
# TODO: lift out to somewhere common with RemoteRepository
711
701
# <https://bugs.launchpad.net/bzr/+bug/401646>
712
702
if (self.has_same_location(source)
713
and fetch_spec is None
714
703
and self._has_same_fallbacks(source)):
715
704
# check that last_revision is in 'from' and then return a
719
708
self.get_revision(revision_id)
721
710
inter = InterRepository.get(source, self)
722
return inter.fetch(revision_id=revision_id,
723
find_ghosts=find_ghosts, fetch_spec=fetch_spec)
711
return inter.fetch(revision_id=revision_id, find_ghosts=find_ghosts)
725
713
def create_bundle(self, target, base, fileobj, format=None):
726
714
return serializer.write_bundle(self, target, base, fileobj, format)
728
def get_commit_builder(self, branch, parents, config, timestamp=None,
716
def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
729
717
timezone=None, committer=None, revprops=None,
730
718
revision_id=None, lossy=False):
731
719
"""Obtain a CommitBuilder for this repository.
733
721
:param branch: Branch to commit to.
734
722
:param parents: Revision ids of the parents of the new revision.
735
:param config: Configuration to use.
723
:param config_stack: Configuration stack to use.
736
724
:param timestamp: Optional timestamp recorded for commit.
737
725
:param timezone: Optional timezone for timestamp.
738
726
:param committer: Optional committer to set for commit.
761
def clone(self, a_bzrdir, revision_id=None):
762
"""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.
764
752
Currently no check is made that the format of this repository and
765
753
the bzrdir format are compatible. FIXME RBC 20060201.
769
757
# TODO: deprecate after 0.16; cloning this with all its settings is
770
758
# probably not very useful -- mbp 20070423
771
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())
772
761
self.copy_content_into(dest_repo, revision_id)
941
930
parent_ids.discard(_mod_revision.NULL_REVISION)
942
931
return parent_ids
944
def fileids_altered_by_revision_ids(self, revision_ids):
945
"""Find the file ids and versions affected by revisions.
947
:param revisions: an iterable containing revision ids.
948
:return: a dictionary mapping altered file-ids to an iterable of
949
revision_ids. Each altered file-ids has the exact revision_ids
950
that altered it listed explicitly.
952
raise NotImplementedError(self.fileids_altered_by_revision_ids)
954
933
def iter_files_bytes(self, desired_files):
955
934
"""Iterate through file versions.
1002
981
raise AssertionError('_iter_for_revno returned too much history')
1003
982
return (True, partial_history[-1])
984
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1005
985
def iter_reverse_revision_history(self, revision_id):
1006
986
"""Iterate backwards through revision ids in the lefthand history
1055
1035
raise NotImplementedError(self.revision_trees)
1057
1037
@needs_read_lock
1038
@symbol_versioning.deprecated_method(
1039
symbol_versioning.deprecated_in((2, 4, 0)))
1058
1040
def get_ancestry(self, revision_id, topo_sorted=True):
1059
1041
"""Return a list of revision-ids integrated by a revision.
1065
1047
This is topologically sorted.
1049
if 'evil' in debug.debug_flags:
1050
mutter_callsite(2, "get_ancestry is linear with history.")
1067
1051
if _mod_revision.is_null(revision_id):
1069
1053
if not self.has_revision(revision_id):
1165
1149
[parents_provider, other_repository._make_parents_provider()])
1166
1150
return graph.Graph(parents_provider)
1168
def revision_ids_to_search_result(self, result_set):
1169
"""Convert a set of revision ids to a graph SearchResult."""
1170
result_parents = set()
1171
for parents in self.get_graph().get_parent_map(
1172
result_set).itervalues():
1173
result_parents.update(parents)
1174
included_keys = result_set.intersection(result_parents)
1175
start_keys = result_set.difference(included_keys)
1176
exclude_keys = result_parents.difference(result_set)
1177
result = graph.SearchResult(start_keys, exclude_keys,
1178
len(result_set), result_set)
1181
1152
@needs_write_lock
1182
1153
def set_make_working_trees(self, new_value):
1183
1154
"""Set the policy flag for making working trees when creating branches.
1200
1171
plaintext = testament.as_short_text()
1201
1172
self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1175
def verify_revision_signature(self, revision_id, gpg_strategy):
1176
"""Verify the signature on a revision.
1178
:param revision_id: the revision to verify
1179
:gpg_strategy: the GPGStrategy object to used
1181
:return: gpg.SIGNATURE_VALID or a failed SIGNATURE_ value
1183
if not self.has_signature_for_revision_id(revision_id):
1184
return gpg.SIGNATURE_NOT_SIGNED, None
1185
signature = self.get_signature_text(revision_id)
1187
testament = _mod_testament.Testament.from_revision(self, revision_id)
1188
plaintext = testament.as_short_text()
1190
return gpg_strategy.verify(signature, plaintext)
1203
1192
def has_signature_for_revision_id(self, revision_id):
1204
1193
"""Query for a revision signature for revision_id in the repository."""
1205
1194
raise NotImplementedError(self.has_signature_for_revision_id)
1304
1293
"""Returns the policy for making working trees on new branches."""
1305
1294
return not self._transport.has('no-working-trees')
1297
def update_feature_flags(self, updated_flags):
1298
"""Update the feature flags for this branch.
1300
:param updated_flags: Dictionary mapping feature names to necessities
1301
A necessity can be None to indicate the feature should be removed
1303
self._format._update_feature_flags(updated_flags)
1304
self.control_transport.put_bytes('format', self._format.as_string())
1308
1307
class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
1309
1308
"""Repository format registry."""
1311
1310
def get_default(self):
1312
1311
"""Return the current default format."""
1313
from bzrlib import bzrdir
1314
return bzrdir.format_registry.make_bzrdir('default').repository_format
1312
return controldir.format_registry.make_bzrdir('default').repository_format
1317
1315
network_format_registry = registry.FormatRegistry()
1362
1360
Common instance attributes:
1363
_matchingbzrdir - the bzrdir format that the repository format was
1361
_matchingbzrdir - the controldir format that the repository format was
1364
1362
originally written to work with. This can be used if manually
1365
1363
constructing a bzrdir and repository, or more commonly for test suite
1366
1364
parameterization.
1404
1402
revision_graph_can_have_wrong_parents = None
1405
1403
# Does this format support rich root data?
1406
1404
rich_root_data = None
1405
# Does this format support explicitly versioned directories?
1406
supports_versioned_directories = None
1407
# Can other repositories be nested into one of this format?
1408
supports_nesting_repositories = None
1409
# Is it possible for revisions to be present without being referenced
1411
supports_unreferenced_revisions = None
1408
1413
def __repr__(self):
1409
1414
return "%s()" % self.__class__.__name__
1416
1421
return not self == other
1419
def find_format(klass, a_bzrdir):
1420
"""Return the format for the repository object in a_bzrdir.
1422
This is used by bzr native formats that have a "format" file in
1423
the repository. Other methods may be used by different types of
1427
transport = a_bzrdir.get_repository_transport(None)
1428
format_string = transport.get_bytes("format")
1429
return format_registry.get(format_string)
1430
except errors.NoSuchFile:
1431
raise errors.NoRepositoryPresent(a_bzrdir)
1433
raise errors.UnknownFormatError(format=format_string,
1437
1424
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1438
1425
def register_format(klass, format):
1439
1426
format_registry.register(format)
1449
1436
"""Return the current default format."""
1450
1437
return format_registry.get_default()
1452
def get_format_string(self):
1453
"""Return the ASCII format string that identifies this format.
1455
Note that in pre format ?? repositories the format string is
1456
not permitted nor written to disk.
1458
raise NotImplementedError(self.get_format_string)
1460
1439
def get_format_description(self):
1461
1440
"""Return the short description for this format."""
1462
1441
raise NotImplementedError(self.get_format_description)
1464
def initialize(self, a_bzrdir, shared=False):
1465
"""Initialize a repository of this format in a_bzrdir.
1443
def initialize(self, controldir, shared=False):
1444
"""Initialize a repository of this format in controldir.
1467
:param a_bzrdir: The bzrdir to put the new repository in it.
1446
:param controldir: The controldir to put the new repository in it.
1468
1447
:param shared: The repository should be initialized as a sharable one.
1469
1448
:returns: The new repository object.
1471
1450
This may raise UninitializableFormat if shared repository are not
1472
compatible the a_bzrdir.
1451
compatible the controldir.
1474
1453
raise NotImplementedError(self.initialize)
1511
1490
'Does not support nested trees', target_format,
1512
1491
from_format=self)
1514
def open(self, a_bzrdir, _found=False):
1515
"""Return an instance of this format for the bzrdir a_bzrdir.
1493
def open(self, controldir, _found=False):
1494
"""Return an instance of this format for a controldir.
1517
1496
_found is a private parameter, do not use it.
1519
1498
raise NotImplementedError(self.open)
1521
def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
1522
from bzrlib.bzrdir import BzrDir, RepoInitHookParams
1523
hooks = BzrDir.hooks['post_repo_init']
1500
def _run_post_repo_init_hooks(self, repository, controldir, shared):
1501
from bzrlib.controldir import ControlDir, RepoInitHookParams
1502
hooks = ControlDir.hooks['post_repo_init']
1526
params = RepoInitHookParams(repository, self, a_bzrdir, shared)
1505
params = RepoInitHookParams(repository, self, controldir, shared)
1527
1506
for hook in hooks:
1531
class MetaDirRepositoryFormat(RepositoryFormat):
1510
class RepositoryFormatMetaDir(bzrdir.BzrFormat, RepositoryFormat):
1532
1511
"""Common base class for the new repositories using the metadir layout."""
1534
1513
rich_root_data = False
1535
1514
supports_tree_reference = False
1536
1515
supports_external_lookups = False
1537
1516
supports_leaving_lock = True
1517
supports_nesting_repositories = True
1540
1520
def _matchingbzrdir(self):
1543
1523
return matching
1545
1525
def __init__(self):
1546
super(MetaDirRepositoryFormat, self).__init__()
1526
RepositoryFormat.__init__(self)
1527
bzrdir.BzrFormat.__init__(self)
1548
1529
def _create_control_files(self, a_bzrdir):
1549
1530
"""Create the required files and the initial control_files object."""
1574
1555
control_files.unlock()
1576
def network_name(self):
1577
"""Metadir formats have matching disk and network format strings."""
1578
return self.get_format_string()
1558
def find_format(klass, a_bzrdir):
1559
"""Return the format for the repository object in a_bzrdir.
1561
This is used by bzr native formats that have a "format" file in
1562
the repository. Other methods may be used by different types of
1566
transport = a_bzrdir.get_repository_transport(None)
1567
format_string = transport.get_bytes("format")
1568
except errors.NoSuchFile:
1569
raise errors.NoRepositoryPresent(a_bzrdir)
1570
return klass._find_format(format_registry, 'repository', format_string)
1572
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
1574
RepositoryFormat.check_support_status(self,
1575
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
1577
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
1578
recommend_upgrade=recommend_upgrade, basedir=basedir)
1581
1581
# formats which have no format string are not discoverable or independently
1582
1582
# creatable on disk, so are not registered in format_registry. They're
1583
1583
# all in bzrlib.repofmt.knitreponow. When an instance of one of these is
1584
# needed, it's constructed directly by the BzrDir. Non-native formats where
1584
# needed, it's constructed directly by the ControlDir. Non-native formats where
1585
1585
# the repository is not separately opened are similar.
1587
1587
format_registry.register_lazy(
1698
1698
self.target.fetch(self.source, revision_id=revision_id)
1700
1700
@needs_write_lock
1701
def fetch(self, revision_id=None, find_ghosts=False,
1701
def fetch(self, revision_id=None, find_ghosts=False):
1703
1702
"""Fetch the content required to construct revision_id.
1705
1704
The content is copied from self.source to self.target.
1785
1784
# trigger an assertion if not such
1786
1785
repo._format.get_format_string()
1787
1786
self.repo_dir = repo.bzrdir
1788
pb.update('Moving repository to repository.backup')
1787
pb.update(gettext('Moving repository to repository.backup'))
1789
1788
self.repo_dir.transport.move('repository', 'repository.backup')
1790
1789
backup_transport = self.repo_dir.transport.clone('repository.backup')
1791
1790
repo._format.check_conversion_target(self.target_format)
1792
1791
self.source_repo = repo._format.open(self.repo_dir,
1794
1793
_override_transport=backup_transport)
1795
pb.update('Creating new repository')
1794
pb.update(gettext('Creating new repository'))
1796
1795
converted = self.target_format.initialize(self.repo_dir,
1797
1796
self.source_repo.is_shared())
1798
1797
converted.lock_write()
1800
pb.update('Copying content')
1799
pb.update(gettext('Copying content'))
1801
1800
self.source_repo.copy_content_into(converted)
1803
1802
converted.unlock()
1804
pb.update('Deleting old repository content')
1803
pb.update(gettext('Deleting old repository content'))
1805
1804
self.repo_dir.transport.delete_tree('repository.backup')
1806
ui.ui_factory.note('repository converted')
1805
ui.ui_factory.note(gettext('repository converted'))
1833
1832
it is encountered, history extension will stop.
1835
1834
start_revision = partial_history_cache[-1]
1836
iterator = repo.iter_reverse_revision_history(start_revision)
1835
graph = repo.get_graph()
1836
iterator = graph.iter_lefthand_ancestry(start_revision,
1837
(_mod_revision.NULL_REVISION,))
1838
#skip the last revision in the list
1839
# skip the last revision in the list
1839
1840
iterator.next()
1841
1842
if (stop_index is not None and