18
18
from cStringIO import StringIO
19
19
from unittest import TestSuite
21
# FIXME: Pulling this in just for the unescape() routine seems like overkill.
22
import xml.sax.saxutils
24
21
import bzrlib.bzrdir as bzrdir
25
22
from bzrlib.decorators import needs_read_lock, needs_write_lock
23
import bzrlib.errors as errors
26
24
from bzrlib.errors import InvalidRevisionId
25
import bzrlib.gpg as gpg
26
from bzrlib.graph import Graph
27
from bzrlib.inter import InterObject
28
from bzrlib.knit import KnitVersionedFile
27
29
from bzrlib.lockable_files import LockableFiles, TransportLock
28
30
from bzrlib.lockdir import LockDir
29
31
from bzrlib.osutils import safe_unicode
30
32
from bzrlib.revision import NULL_REVISION
31
import bzrlib.errors as errors
32
import bzrlib.gpg as gpg
33
from bzrlib.store import copy_all
34
from bzrlib.store.weave import WeaveStore
33
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
35
34
from bzrlib.store.text import TextStore
36
35
from bzrlib.symbol_versioning import *
37
36
from bzrlib.trace import mutter
38
37
from bzrlib.tree import RevisionTree
38
from bzrlib.tsort import topo_sort
39
39
from bzrlib.testament import Testament
40
40
from bzrlib.tree import EmptyTree
42
from bzrlib.weave import WeaveFile
66
67
inv_text = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
67
68
inv_sha1 = bzrlib.osutils.sha_string(inv_text)
68
self.control_weaves.add_text('inventory', revid,
69
bzrlib.osutils.split_lines(inv_text), parents,
70
self.get_transaction())
69
inv_vf = self.control_weaves.get_weave('inventory',
70
self.get_transaction())
71
inv_vf.add_lines(revid, parents, bzrlib.osutils.split_lines(inv_text))
96
97
# yes, this is not suitable for adding with ghosts.
97
98
self.add_inventory(rev_id, inv, rev.parent_ids)
100
bzrlib.xml5.serializer_v5.write_revision(rev, rev_tmp)
102
self.revision_store.add(rev_tmp, rev_id)
103
mutter('added revision_id {%s}', rev_id)
99
self._revision_store.add_revision(rev, self.get_transaction())
106
102
def _all_possible_ids(self):
107
103
"""Return all the possible revisions that we could find."""
108
return self.get_inventory_weave().names()
104
return self.get_inventory_weave().versions()
111
107
def all_revision_ids(self):
135
133
"""Construct the current default format repository in a_bzrdir."""
136
134
return RepositoryFormat.get_default_format().initialize(a_bzrdir)
138
def __init__(self, _format, a_bzrdir, control_files, revision_store):
136
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
139
137
"""instantiate a Repository.
141
139
:param _format: The format of the repository on disk.
150
148
# the following are part of the public API for Repository:
151
149
self.bzrdir = a_bzrdir
152
150
self.control_files = control_files
153
self.revision_store = revision_store
151
self._revision_store = _revision_store
152
self.text_store = text_store
153
# backwards compatability
154
self.weave_store = text_store
155
# not right yet - should be more semantically clear ?
157
self.control_store = control_store
158
self.control_weaves = control_store
155
160
def lock_write(self):
156
161
self.control_files.lock_write()
221
226
self.copy_content_into(result, revision_id, basis)
224
230
def has_revision(self, revision_id):
225
"""True if this branch has a copy of the revision.
227
This does not necessarily imply the revision is merge
228
or on the mainline."""
229
return (revision_id is None
230
or self.revision_store.has_id(revision_id))
233
def get_revision_xml_file(self, revision_id):
234
"""Return XML file object for revision object."""
235
if not revision_id or not isinstance(revision_id, basestring):
236
raise InvalidRevisionId(revision_id=revision_id, branch=self)
238
return self.revision_store.get(revision_id)
239
except (IndexError, KeyError):
240
raise bzrlib.errors.NoSuchRevision(self, revision_id)
243
def get_revision_xml(self, revision_id):
244
return self.get_revision_xml_file(revision_id).read()
231
"""True if this repository has a copy of the revision."""
232
return self._revision_store.has_revision_id(revision_id,
233
self.get_transaction())
247
236
def get_revision_reconcile(self, revision_id):
252
241
be used by reconcile, or reconcile-alike commands that are correcting
253
242
or testing the revision graph.
255
xml_file = self.get_revision_xml_file(revision_id)
244
if not revision_id or not isinstance(revision_id, basestring):
245
raise InvalidRevisionId(revision_id=revision_id, branch=self)
246
return self._revision_store.get_revision(revision_id,
247
self.get_transaction())
258
r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
259
except SyntaxError, e:
260
raise bzrlib.errors.BzrError('failed to unpack revision_xml',
264
assert r.revision_id == revision_id
250
def get_revision_xml(self, revision_id):
251
rev = self.get_revision(revision_id)
253
# the current serializer..
254
self._revision_store._serializer.write_revision(rev, rev_tmp)
256
return rev_tmp.getvalue()
268
259
def get_revision(self, revision_id):
286
277
consistency and is only applicable to inventory-weave-for-ancestry
287
278
using repository formats & fetchers.
289
weave_parents = inventory.parent_names(revision.revision_id)
290
weave_names = inventory.names()
280
weave_parents = inventory.get_parents(revision.revision_id)
281
weave_names = inventory.versions()
291
282
for parent_id in revision.parent_ids:
292
283
if parent_id in weave_names:
293
284
# this parent must not be a ghost.
295
286
# but it is a ghost
296
287
raise errors.CorruptRepository(self)
299
def get_revision_sha1(self, revision_id):
300
"""Hash the stored value of a revision, and return it."""
301
# In the future, revision entries will be signed. At that
302
# point, it is probably best *not* to include the signature
303
# in the revision hash. Because that lets you re-sign
304
# the revision, (add signatures/remove signatures) and still
305
# have all hash pointers stay consistent.
306
# But for now, just hash the contents.
307
return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
309
289
@needs_write_lock
310
290
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
311
self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)),
291
signature = gpg_strategy.sign(plaintext)
292
self._revision_store.add_revision_signature_text(revision_id,
294
self.get_transaction())
314
296
def fileid_involved_between_revs(self, from_revid, to_revid):
315
297
"""Find file_id(s) which are involved in the changes between revisions.
328
310
# won't be fixed, because AD never saw revision C
329
311
# to cause a conflict which would force a reweave.
330
312
w = self.get_inventory_weave()
331
from_set = set(w.inclusions([w.lookup(from_revid)]))
332
to_set = set(w.inclusions([w.lookup(to_revid)]))
333
included = to_set.difference(from_set)
334
changed = map(w.idx_to_name, included)
313
from_set = set(w.get_ancestry(from_revid))
314
to_set = set(w.get_ancestry(to_revid))
315
changed = to_set.difference(from_set)
335
316
return self._fileid_involved_by_set(changed)
337
318
def fileid_involved(self, last_revid=None):
380
360
w = self.get_inventory_weave()
382
for line in w._weave:
384
# it is ugly, but it is due to the weave structure
385
if not isinstance(line, basestring): continue
363
for lineno, insert, deletes, line in w.walk(changes):
387
364
start = line.find('file_id="')+9
388
365
if start < 9: continue
389
366
end = line.find('"', start)
391
file_id = xml.sax.saxutils.unescape(line[start:end])
368
file_id = _unescape_xml(line[start:end])
393
370
# check if file_id is already present
394
371
if file_id in file_ids: continue
431
407
return self.get_revision(revision_id).inventory_sha1
410
def get_revision_graph(self, revision_id=None):
411
"""Return a dictionary containing the revision graph.
413
:return: a dictionary of revision_id->revision_parents_list.
415
weave = self.get_inventory_weave()
416
all_revisions = self._eliminate_revisions_not_present(weave.versions())
417
entire_graph = dict([(node, weave.get_parents(node)) for
418
node in all_revisions])
419
if revision_id is None:
421
elif revision_id not in entire_graph:
422
raise errors.NoSuchRevision(self, revision_id)
424
# add what can be reached from revision_id
426
pending = set([revision_id])
427
while len(pending) > 0:
429
result[node] = entire_graph[node]
430
for revision_id in result[node]:
431
if revision_id not in result:
432
pending.add(revision_id)
436
def get_revision_graph_with_ghosts(self, revision_ids=None):
437
"""Return a graph of the revisions with ghosts marked as applicable.
439
:param revision_ids: an iterable of revisions to graph or None for all.
440
:return: a Graph object with the graph reachable from revision_ids.
444
pending = set(self.all_revision_ids())
447
pending = set(revision_ids)
448
required = set(revision_ids)
451
revision_id = pending.pop()
453
rev = self.get_revision(revision_id)
454
except errors.NoSuchRevision:
455
if revision_id in required:
458
result.add_ghost(revision_id)
460
for parent_id in rev.parent_ids:
461
# is this queued or done ?
462
if (parent_id not in pending and
463
parent_id not in done):
465
pending.add(parent_id)
466
result.add_node(revision_id, rev.parent_ids)
434
471
def get_revision_inventory(self, revision_id):
435
472
"""Return inventory of a past revision."""
436
473
# TODO: Unify this with get_inventory()
477
514
if not self.has_revision(revision_id):
478
515
raise errors.NoSuchRevision(self, revision_id)
479
516
w = self.get_inventory_weave()
480
return [None] + map(w.idx_to_name,
481
w.inclusions([w.lookup(revision_id)]))
517
return [None] + w.get_ancestry(revision_id)
484
520
def print_file(self, file, revision_id):
545
584
plaintext = Testament.from_revision(self, revision_id).as_short_text()
546
585
self.store_revision_signature(gpg_strategy, plaintext, revision_id)
588
def has_signature_for_revision_id(self, revision_id):
589
"""Query for a revision signature for revision_id in the repository."""
590
return self._revision_store.has_signature(revision_id,
591
self.get_transaction())
594
def get_signature_text(self, revision_id):
595
"""Return the text for a signature."""
596
return self._revision_store.get_signature_text(revision_id,
597
self.get_transaction())
549
600
class AllInOneRepository(Repository):
550
601
"""Legacy support - the repository behaviour for all-in-one branches."""
552
def __init__(self, _format, a_bzrdir, revision_store):
603
def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
553
604
# we reuse one control files instance.
554
605
dir_mode = a_bzrdir._control_files._dir_mode
555
606
file_mode = a_bzrdir._control_files._file_mode
587
638
# not broken out yet because the controlweaves|inventory_store
588
639
# and text_store | weave_store bits are still different.
589
640
if isinstance(_format, RepositoryFormat4):
641
# cannot remove these - there is still no consistent api
642
# which allows access to this old info.
590
643
self.inventory_store = get_store('inventory-store')
591
self.text_store = get_store('text-store')
592
elif isinstance(_format, RepositoryFormat5):
593
self.control_weaves = get_weave('')
594
self.weave_store = get_weave('weaves')
595
elif isinstance(_format, RepositoryFormat6):
596
self.control_weaves = get_weave('')
597
self.weave_store = get_weave('weaves', prefixed=True)
599
raise errors.BzrError('unreachable code: unexpected repository'
601
revision_store.register_suffix('sig')
602
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, revision_store)
644
text_store = get_store('text-store')
645
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
605
648
class MetaDirRepository(Repository):
606
649
"""Repositories in the new meta-dir layout."""
608
def __init__(self, _format, a_bzrdir, control_files, revision_store):
651
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
609
652
super(MetaDirRepository, self).__init__(_format,
614
659
dir_mode = self.control_files._dir_mode
615
660
file_mode = self.control_files._file_mode
628
673
ws.enable_cache = True
631
if isinstance(self._format, RepositoryFormat7):
632
self.control_weaves = get_weave('')
633
self.weave_store = get_weave('weaves', prefixed=True)
634
elif isinstance(self._format, RepositoryFormatKnit1):
635
self.control_weaves = get_weave('')
636
self.weave_store = get_weave('knits', prefixed=True)
638
raise errors.BzrError('unreachable code: unexpected repository'
677
class KnitRepository(MetaDirRepository):
678
"""Knit format repository."""
681
def all_revision_ids(self):
682
"""See Repository.all_revision_ids()."""
683
return self._revision_store.all_revision_ids(self.get_transaction())
642
686
class RepositoryFormat(object):
698
746
"""Return the revision store object for this a_bzrdir."""
699
747
raise NotImplementedError(self._get_revision_store)
701
def _get_rev_store(self,
749
def _get_text_rev_store(self,
707
756
"""Common logic for getting a revision store for a repository.
709
see self._get_revision_store for the method to
758
see self._get_revision_store for the subclass-overridable method to
710
759
get the store for a repository.
713
name = safe_unicode(name)
716
dir_mode = control_files._dir_mode
717
file_mode = control_files._file_mode
718
revision_store =TextStore(transport.clone(name),
720
compressed=compressed,
723
revision_store.register_suffix('sig')
724
return revision_store
761
from bzrlib.store.revision.text import TextRevisionStore
762
dir_mode = control_files._dir_mode
763
file_mode = control_files._file_mode
764
text_store =TextStore(transport.clone(name),
766
compressed=compressed,
769
_revision_store = TextRevisionStore(text_store, serializer)
770
return _revision_store
772
def _get_versioned_file_store(self,
777
versionedfile_class=WeaveFile):
778
weave_transport = control_files._transport.clone(name)
779
dir_mode = control_files._dir_mode
780
file_mode = control_files._file_mode
781
return VersionedFileStore(weave_transport, prefixed=prefixed,
784
versionedfile_class=versionedfile_class)
726
786
def initialize(self, a_bzrdir, shared=False):
727
787
"""Initialize a repository of this format in a_bzrdir.
807
867
control_files.unlock()
808
868
return self.open(a_bzrdir, _found=True)
870
def _get_control_store(self, repo_transport, control_files):
871
"""Return the control store for this repository."""
872
return self._get_versioned_file_store('',
877
def _get_text_store(self, transport, control_files):
878
"""Get a store for file texts for this format."""
879
raise NotImplementedError(self._get_text_store)
810
881
def open(self, a_bzrdir, _found=False):
811
882
"""See RepositoryFormat.open()."""
816
887
repo_transport = a_bzrdir.get_repository_transport(None)
817
888
control_files = a_bzrdir._control_files
818
revision_store = self._get_revision_store(repo_transport, control_files)
889
text_store = self._get_text_store(repo_transport, control_files)
890
control_store = self._get_control_store(repo_transport, control_files)
891
_revision_store = self._get_revision_store(repo_transport, control_files)
819
892
return AllInOneRepository(_format=self,
820
893
a_bzrdir=a_bzrdir,
821
revision_store=revision_store)
894
_revision_store=_revision_store,
895
control_store=control_store,
896
text_store=text_store)
824
899
class RepositoryFormat4(PreSplitOutRepositoryFormat):
928
def _get_control_store(self, repo_transport, control_files):
929
"""Format 4 repositories have no formal control store at this point.
931
This will cause any control-file-needing apis to fail - this is desired.
853
935
def _get_revision_store(self, repo_transport, control_files):
854
936
"""See RepositoryFormat._get_revision_store()."""
855
return self._get_rev_store(repo_transport,
937
from bzrlib.xml4 import serializer_v4
938
return self._get_text_rev_store(repo_transport,
941
serializer=serializer_v4)
943
def _get_text_store(self, transport, control_files):
944
"""See RepositoryFormat._get_text_store()."""
860
947
class RepositoryFormat5(PreSplitOutRepositoryFormat):
873
960
def _get_revision_store(self, repo_transport, control_files):
874
961
"""See RepositoryFormat._get_revision_store()."""
875
962
"""Return the revision store object for this a_bzrdir."""
876
return self._get_rev_store(repo_transport,
963
return self._get_text_rev_store(repo_transport,
968
def _get_text_store(self, transport, control_files):
969
"""See RepositoryFormat._get_text_store()."""
970
return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
882
973
class RepositoryFormat6(PreSplitOutRepositoryFormat):
895
986
def _get_revision_store(self, repo_transport, control_files):
896
987
"""See RepositoryFormat._get_revision_store()."""
897
return self._get_rev_store(repo_transport,
988
return self._get_text_rev_store(repo_transport,
994
def _get_text_store(self, transport, control_files):
995
"""See RepositoryFormat._get_text_store()."""
996
return self._get_versioned_file_store('weaves', transport, control_files)
904
999
class MetaDirRepositoryFormat(RepositoryFormat):
917
1012
control_files.create_lock()
918
1013
return control_files
920
def _get_revision_store(self, repo_transport, control_files):
921
"""See RepositoryFormat._get_revision_store()."""
922
return self._get_rev_store(repo_transport,
929
def open(self, a_bzrdir, _found=False, _override_transport=None):
930
"""See RepositoryFormat.open().
932
:param _override_transport: INTERNAL USE ONLY. Allows opening the
933
repository at a slightly different url
934
than normal. I.e. during 'upgrade'.
937
format = RepositoryFormat.find_format(a_bzrdir)
938
assert format.__class__ == self.__class__
939
if _override_transport is not None:
940
repo_transport = _override_transport
942
repo_transport = a_bzrdir.get_repository_transport(None)
943
control_files = LockableFiles(repo_transport, 'lock', LockDir)
944
revision_store = self._get_revision_store(repo_transport, control_files)
945
return MetaDirRepository(_format=self,
947
control_files=control_files,
948
revision_store=revision_store)
950
1015
def _upload_blank_content(self, a_bzrdir, dirs, files, utf8_files, shared):
951
1016
"""Upload the initial blank content."""
952
1017
control_files = self._create_control_files(a_bzrdir)
976
1041
- an optional 'no-working-trees' flag
1044
def _get_control_store(self, repo_transport, control_files):
1045
"""Return the control store for this repository."""
1046
return self._get_versioned_file_store('',
979
1051
def get_format_string(self):
980
1052
"""See RepositoryFormat.get_format_string()."""
981
1053
return "Bazaar-NG Repository format 7"
1055
def _get_revision_store(self, repo_transport, control_files):
1056
"""See RepositoryFormat._get_revision_store()."""
1057
return self._get_text_rev_store(repo_transport,
1064
def _get_text_store(self, transport, control_files):
1065
"""See RepositoryFormat._get_text_store()."""
1066
return self._get_versioned_file_store('weaves',
983
1070
def initialize(self, a_bzrdir, shared=False):
984
1071
"""Create a weave repository.
1003
1090
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1004
1091
return self.open(a_bzrdir=a_bzrdir, _found=True)
1093
def open(self, a_bzrdir, _found=False, _override_transport=None):
1094
"""See RepositoryFormat.open().
1096
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1097
repository at a slightly different url
1098
than normal. I.e. during 'upgrade'.
1101
format = RepositoryFormat.find_format(a_bzrdir)
1102
assert format.__class__ == self.__class__
1103
if _override_transport is not None:
1104
repo_transport = _override_transport
1106
repo_transport = a_bzrdir.get_repository_transport(None)
1107
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1108
text_store = self._get_text_store(repo_transport, control_files)
1109
control_store = self._get_control_store(repo_transport, control_files)
1110
_revision_store = self._get_revision_store(repo_transport, control_files)
1111
return MetaDirRepository(_format=self,
1113
control_files=control_files,
1114
_revision_store=_revision_store,
1115
control_store=control_store,
1116
text_store=text_store)
1007
1119
class RepositoryFormatKnit1(MetaDirRepositoryFormat):
1008
1120
"""Bzr repository knit format 1.
1018
1130
- a LockDir lock
1133
def _get_control_store(self, repo_transport, control_files):
1134
"""Return the control store for this repository."""
1135
return self._get_versioned_file_store('',
1139
versionedfile_class=KnitVersionedFile)
1021
1141
def get_format_string(self):
1022
1142
"""See RepositoryFormat.get_format_string()."""
1023
1143
return "Bazaar-NG Knit Repository Format 1"
1145
def _get_revision_store(self, repo_transport, control_files):
1146
"""See RepositoryFormat._get_revision_store()."""
1147
from bzrlib.store.revision.knit import KnitRevisionStore
1148
versioned_file_store = VersionedFileStore(
1150
file_mode = control_files._file_mode,
1153
versionedfile_class=KnitVersionedFile)
1154
return KnitRevisionStore(versioned_file_store)
1156
def _get_text_store(self, transport, control_files):
1157
"""See RepositoryFormat._get_text_store()."""
1158
return self._get_versioned_file_store('knits',
1161
versionedfile_class=KnitVersionedFile)
1025
1163
def initialize(self, a_bzrdir, shared=False):
1026
1164
"""Create a knit format 1 repository.
1039
1177
empty_weave = sio.getvalue()
1041
1179
mutter('creating repository in %s.', a_bzrdir.transport.base)
1042
dirs = ['revision-store', 'knits']
1043
files = [('inventory.weave', StringIO(empty_weave)),
1180
dirs = ['revision-store', 'knits', 'control']
1181
files = [('control/inventory.weave', StringIO(empty_weave)),
1045
1183
utf8_files = [('format', self.get_format_string())]
1047
1185
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1186
repo_transport = a_bzrdir.get_repository_transport(None)
1187
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1188
control_store = self._get_control_store(repo_transport, control_files)
1189
transaction = bzrlib.transactions.PassThroughTransaction()
1190
# trigger a write of the inventory store.
1191
control_store.get_weave_or_empty('inventory', transaction)
1192
_revision_store = self._get_revision_store(repo_transport, control_files)
1193
_revision_store.has_revision_id('A', transaction)
1194
_revision_store.get_signature_file(transaction)
1048
1195
return self.open(a_bzrdir=a_bzrdir, _found=True)
1197
def open(self, a_bzrdir, _found=False, _override_transport=None):
1198
"""See RepositoryFormat.open().
1200
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1201
repository at a slightly different url
1202
than normal. I.e. during 'upgrade'.
1205
format = RepositoryFormat.find_format(a_bzrdir)
1206
assert format.__class__ == self.__class__
1207
if _override_transport is not None:
1208
repo_transport = _override_transport
1210
repo_transport = a_bzrdir.get_repository_transport(None)
1211
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1212
text_store = self._get_text_store(repo_transport, control_files)
1213
control_store = self._get_control_store(repo_transport, control_files)
1214
_revision_store = self._get_revision_store(repo_transport, control_files)
1215
return KnitRepository(_format=self,
1217
control_files=control_files,
1218
_revision_store=_revision_store,
1219
control_store=control_store,
1220
text_store=text_store)
1051
1223
# formats which have no format string are not discoverable
1052
1224
# and not independently creatable, so are not registered.
1070
1242
operations with another repository - they will always forward to
1071
1243
InterRepository.get(other).method_name(parameters).
1073
# XXX: FIXME: FUTURE: robertc
1074
# testing of these probably requires a factory in optimiser type, and
1075
# then a test adapter to test each type thoroughly.
1078
1246
_optimisers = set()
1079
1247
"""The available optimised InterRepository types."""
1081
def __init__(self, source, target):
1082
"""Construct a default InterRepository instance. Please use 'get'.
1084
Only subclasses of InterRepository should call
1085
InterRepository.__init__ - clients should call InterRepository.get
1086
instead which will create an optimised InterRepository if possible.
1088
self.source = source
1089
self.target = target
1091
1249
@needs_write_lock
1092
1250
def copy_content(self, revision_id=None, basis=None):
1093
1251
"""Make a complete copy of the content in self into destination.
1137
1295
Returns the copied revision count and the failed revisions in a tuple:
1138
1296
(copied, failures).
1140
from bzrlib.fetch import RepoFetcher
1298
from bzrlib.fetch import GenericRepoFetcher
1141
1299
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1142
1300
self.source, self.source._format, self.target, self.target._format)
1143
f = RepoFetcher(to_repository=self.target,
1144
from_repository=self.source,
1145
last_revision=revision_id,
1301
f = GenericRepoFetcher(to_repository=self.target,
1302
from_repository=self.source,
1303
last_revision=revision_id,
1147
1305
return f.count_copied, f.failed_revisions
1150
def get(klass, repository_source, repository_target):
1151
"""Retrieve a InterRepository worker object for these repositories.
1153
:param repository_source: the repository to be the 'source' member of
1154
the InterRepository instance.
1155
:param repository_target: the repository to be the 'target' member of
1156
the InterRepository instance.
1157
If an optimised InterRepository worker exists it will be used otherwise
1158
a default InterRepository instance will be created.
1160
for provider in klass._optimisers:
1161
if provider.is_compatible(repository_source, repository_target):
1162
return provider(repository_source, repository_target)
1163
return InterRepository(repository_source, repository_target)
1165
1307
def lock_read(self):
1166
1308
"""Take out a logical read lock.
1263
1395
# FIXME do not peek!
1264
1396
if self.source.control_files._transport.listable():
1265
pb = bzrlib.ui.ui_factory.progress_bar()
1266
copy_all(self.source.weave_store,
1267
self.target.weave_store, pb=pb)
1268
pb.update('copying inventory', 0, 1)
1269
self.target.control_weaves.copy_multi(
1270
self.source.control_weaves, ['inventory'])
1271
copy_all(self.source.revision_store,
1272
self.target.revision_store, pb=pb)
1397
pb = bzrlib.ui.ui_factory.nested_progress_bar()
1399
self.target.weave_store.copy_all_ids(
1400
self.source.weave_store,
1402
from_transaction=self.source.get_transaction(),
1403
to_transaction=self.target.get_transaction())
1404
pb.update('copying inventory', 0, 1)
1405
self.target.control_weaves.copy_multi(
1406
self.source.control_weaves, ['inventory'],
1407
from_transaction=self.source.get_transaction(),
1408
to_transaction=self.target.get_transaction())
1409
self.target._revision_store.text_store.copy_all_ids(
1410
self.source._revision_store.text_store,
1274
1415
self.target.fetch(self.source, revision_id=revision_id)
1276
1417
@needs_write_lock
1277
1418
def fetch(self, revision_id=None, pb=None):
1278
1419
"""See InterRepository.fetch()."""
1279
from bzrlib.fetch import RepoFetcher
1420
from bzrlib.fetch import GenericRepoFetcher
1280
1421
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1281
1422
self.source, self.source._format, self.target, self.target._format)
1282
f = RepoFetcher(to_repository=self.target,
1283
from_repository=self.source,
1284
last_revision=revision_id,
1423
f = GenericRepoFetcher(to_repository=self.target,
1424
from_repository=self.source,
1425
last_revision=revision_id,
1286
1427
return f.count_copied, f.failed_revisions
1288
1429
@needs_read_lock
1326
1467
return self.source._eliminate_revisions_not_present(required_topo_revisions)
1470
class InterKnitRepo(InterRepository):
1471
"""Optimised code paths between Knit based repositories."""
1473
_matching_repo_format = RepositoryFormatKnit1()
1474
"""Repository format for testing with."""
1477
def is_compatible(source, target):
1478
"""Be compatible with known Knit formats.
1480
We dont test for the stores being of specific types becase that
1481
could lead to confusing results, and there is no need to be
1485
return (isinstance(source._format, (RepositoryFormatKnit1)) and
1486
isinstance(target._format, (RepositoryFormatKnit1)))
1487
except AttributeError:
1491
def fetch(self, revision_id=None, pb=None):
1492
"""See InterRepository.fetch()."""
1493
from bzrlib.fetch import KnitRepoFetcher
1494
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1495
self.source, self.source._format, self.target, self.target._format)
1496
f = KnitRepoFetcher(to_repository=self.target,
1497
from_repository=self.source,
1498
last_revision=revision_id,
1500
return f.count_copied, f.failed_revisions
1503
def missing_revision_ids(self, revision_id=None):
1504
"""See InterRepository.missing_revision_ids()."""
1505
if revision_id is not None:
1506
source_ids = self.source.get_ancestry(revision_id)
1507
assert source_ids.pop(0) == None
1509
source_ids = self.source._all_possible_ids()
1510
source_ids_set = set(source_ids)
1511
# source_ids is the worst possible case we may need to pull.
1512
# now we want to filter source_ids against what we actually
1513
# have in target, but dont try to check for existence where we know
1514
# we do not have a revision as that would be pointless.
1515
target_ids = set(self.target._all_possible_ids())
1516
possibly_present_revisions = target_ids.intersection(source_ids_set)
1517
actually_present_revisions = set(self.target._eliminate_revisions_not_present(possibly_present_revisions))
1518
required_revisions = source_ids_set.difference(actually_present_revisions)
1519
required_topo_revisions = [rev_id for rev_id in source_ids if rev_id in required_revisions]
1520
if revision_id is not None:
1521
# we used get_ancestry to determine source_ids then we are assured all
1522
# revisions referenced are present as they are installed in topological order.
1523
# and the tip revision was validated by get_ancestry.
1524
return required_topo_revisions
1526
# if we just grabbed the possibly available ids, then
1527
# we only have an estimate of whats available and need to validate
1528
# that against the revision records.
1529
return self.source._eliminate_revisions_not_present(required_topo_revisions)
1329
1531
InterRepository.register_optimiser(InterWeaveRepo)
1532
InterRepository.register_optimiser(InterKnitRepo)
1332
1535
class RepositoryTestProviderAdapter(object):
1459
1662
"""Update the pb by a step."""
1461
1664
self.pb.update(message, self.count, self.total)
1667
# Copied from xml.sax.saxutils
1668
def _unescape_xml(data):
1669
"""Unescape &, <, and > in a string of data.
1671
data = data.replace("<", "<")
1672
data = data.replace(">", ">")
1673
# must do ampersand last
1674
return data.replace("&", "&")