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 .lazy_import import lazy_import
18
20
lazy_import(globals(), """
59
66
_fmt = "Repository format does not support setting revision ids."
62
class FetchResult(object):
63
"""Result of a fetch operation.
65
:ivar revidmap: For lossy fetches, map from source revid to target revid.
66
:ivar total_fetched: Number of revisions fetched
69
def __init__(self, total_fetched=None, revidmap=None):
70
self.total_fetched = total_fetched
71
self.revidmap = revidmap
74
69
class CommitBuilder(object):
75
70
"""Provides an interface to build up a commit.
146
141
raise ValueError('Invalid value for %s: %r' % (context, text))
148
143
def _validate_revprops(self, revprops):
149
for key, value in revprops.items():
144
for key, value in viewitems(revprops):
150
145
# We know that the XML serializers do not round trip '\r'
151
146
# correctly, so refuse to accept them
152
if not isinstance(value, str):
147
if not isinstance(value, (text_type, str)):
153
148
raise ValueError('revision property (%s) is not a valid'
154
149
' (unicode) string: %r' % (key, value))
155
150
# TODO(jelmer): Make this repository-format specific
244
class WriteGroup(object):
245
"""Context manager that manages a write group.
247
Raising an exception will result in the write group being aborted.
250
def __init__(self, repository, suppress_errors=False):
251
self.repository = repository
252
self._suppress_errors = suppress_errors
255
self.repository.start_write_group()
258
def __exit__(self, exc_type, exc_val, exc_tb):
260
self.repository.abort_write_group(self._suppress_errors)
263
self.repository.commit_write_group()
266
239
######################################################################
592
563
value = (controldir.list_branches(), None)
593
564
return True, value
595
567
for branches, repository in controldir.ControlDir.find_controldirs(
596
568
self.user_transport, evaluate=Evaluator()):
597
569
if branches is not None:
598
for branch in branches:
600
571
if not using and repository is not None:
601
for branch in repository.find_branches():
572
ret.extend(repository.find_branches())
604
575
def search_missing_revision_ids(self, other,
605
576
find_ghosts=True, revision_ids=None, if_present_ids=None,
692
663
def _resume_write_group(self, tokens):
693
664
raise errors.UnsuspendableWriteGroup(self)
695
def fetch(self, source, revision_id=None, find_ghosts=False, lossy=False):
666
def fetch(self, source, revision_id=None, find_ghosts=False):
696
667
"""Fetch the content required to construct revision_id from source.
698
669
If revision_id is None, then all content is copied.
724
694
self.get_revision(revision_id)
726
696
inter = InterRepository.get(source, self)
728
revision_id=revision_id, find_ghosts=find_ghosts, lossy=lossy)
697
return inter.fetch(revision_id=revision_id, find_ghosts=find_ghosts)
699
def create_bundle(self, target, base, fileobj, format=None):
700
return serializer.write_bundle(self, target, base, fileobj, format)
730
702
def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
731
703
timezone=None, committer=None, revprops=None,
885
857
raise NotImplementedError(self.iter_revisions)
887
def get_revision_delta(self, revision_id):
859
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
860
"""Produce a generator of revision deltas.
862
Note that the input is a sequence of REVISIONS, not revision_ids.
863
Trees will be held in memory until the generator exits.
864
Each delta is relative to the revision's lefthand predecessor.
866
:param specific_fileids: if not None, the result is filtered
867
so that only those file-ids, their parents and their
868
children are included.
870
raise NotImplementedError(self.get_deltas_for_revisions)
872
def get_revision_delta(self, revision_id, specific_fileids=None):
888
873
"""Return the delta for one revision.
890
875
The delta is relative to the left-hand predecessor of the
878
:param specific_fileids: if not None, the result is filtered
879
so that only those file-ids, their parents and their
880
children are included.
893
882
with self.lock_read():
894
883
r = self.get_revision(revision_id)
895
return list(self.get_revision_deltas([r]))[0]
897
def get_revision_deltas(self, revisions, specific_files=None):
898
"""Produce a generator of revision deltas.
900
Note that the input is a sequence of REVISIONS, not revision ids.
901
Trees will be held in memory until the generator exits.
902
Each delta is relative to the revision's lefthand predecessor.
904
specific_files should exist in the first revision.
906
:param specific_files: if not None, the result is filtered
907
so that only those files, their parents and their
908
children are included.
910
from .tree import InterTree
911
# Get the revision-ids of interest
912
required_trees = set()
913
for revision in revisions:
914
required_trees.add(revision.revision_id)
915
required_trees.update(revision.parent_ids[:1])
918
t.get_revision_id(): t
919
for t in self.revision_trees(required_trees)}
921
# Calculate the deltas
922
for revision in revisions:
923
if not revision.parent_ids:
924
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
926
old_tree = trees[revision.parent_ids[0]]
927
intertree = InterTree.get(old_tree, trees[revision.revision_id])
928
yield intertree.compare(specific_files=specific_files)
929
if specific_files is not None:
931
p for p in intertree.find_source_paths(
932
specific_files).values()
884
return list(self.get_deltas_for_revisions(
885
[r], specific_fileids=specific_fileids))[0]
935
887
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
936
888
raise NotImplementedError(self.store_revision_signature)
971
923
partial_history = [known_revid]
972
924
distance_from_known = known_revno - revno
973
925
if distance_from_known < 0:
974
raise errors.RevnoOutOfBounds(revno, (0, known_revno))
927
'requested revno (%d) is later than given known revno (%d)'
928
% (revno, known_revno))
977
931
self, partial_history, stop_index=distance_from_known)
978
932
except errors.RevisionNotPresent as err:
979
933
if err.revision_id == known_revid:
980
934
# The start revision (known_revid) wasn't found.
981
raise errors.NoSuchRevision(self, known_revid)
982
936
# This is a stacked repository with no fallbacks, or a there's a
983
937
# left-hand ghost. Either way, even though the revision named in
984
938
# the error isn't in this repo, we know it's the next step in this
1072
1026
query_keys.append((revision_id,))
1073
1027
vf = self.revisions.without_fallbacks()
1074
for (revision_id,), parent_keys in (
1075
vf.get_parent_map(query_keys).items()):
1028
for (revision_id,), parent_keys in viewitems(
1029
vf.get_parent_map(query_keys)):
1076
1030
if parent_keys:
1077
1031
result[revision_id] = tuple([parent_revid
1078
1032
for (parent_revid,) in parent_keys])
1203
1157
# weave repositories refuse to store revisionids that are non-ascii.
1204
1158
if revision_id is not None:
1205
1159
# weaves require ascii revision ids.
1206
if isinstance(revision_id, str):
1160
if isinstance(revision_id, text_type):
1208
1162
revision_id.encode('ascii')
1209
1163
except UnicodeEncodeError:
1530
1484
self.target.set_make_working_trees(
1531
1485
self.source.make_working_trees())
1532
except (NotImplementedError, errors.RepositoryUpgradeRequired):
1486
except NotImplementedError:
1534
1488
self.target.fetch(self.source, revision_id=revision_id)
1536
def fetch(self, revision_id=None, find_ghosts=False, lossy=False):
1490
def fetch(self, revision_id=None, find_ghosts=False):
1537
1491
"""Fetch the content required to construct revision_id.
1539
1493
The content is copied from self.source to self.target.
1541
1495
:param revision_id: if None all content is copied, if NULL_REVISION no
1542
1496
content is copied.
1543
:return: FetchResult
1545
1499
raise NotImplementedError(self.fetch)
1626
1580
pb.update(gettext('Creating new repository'))
1627
1581
converted = self.target_format.initialize(self.repo_dir,
1628
1582
self.source_repo.is_shared())
1629
with converted.lock_write():
1583
converted.lock_write()
1630
1585
pb.update(gettext('Copying content'))
1631
1586
self.source_repo.copy_content_into(converted)
1632
1589
pb.update(gettext('Deleting old repository content'))
1633
1590
self.repo_dir.transport.delete_tree('repository.backup')
1634
1591
ui.ui_factory.note(gettext('repository converted'))
1639
1596
# Filter ghosts, and null:
1640
1597
if _mod_revision.NULL_REVISION in revision_graph:
1641
1598
del revision_graph[_mod_revision.NULL_REVISION]
1642
for key, parents in revision_graph.items():
1599
for key, parents in viewitems(revision_graph):
1643
1600
revision_graph[key] = tuple(parent for parent in parents if parent
1644
1601
in revision_graph)
1645
1602
return revision_graph