17
17
from copy import deepcopy
18
18
from cStringIO import StringIO
19
19
from unittest import TestSuite
20
import xml.sax.saxutils
22
21
import bzrlib.bzrdir as bzrdir
23
22
from bzrlib.decorators import needs_read_lock, needs_write_lock
23
import bzrlib.errors as errors
24
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
25
29
from bzrlib.lockable_files import LockableFiles, TransportLock
26
30
from bzrlib.lockdir import LockDir
27
31
from bzrlib.osutils import safe_unicode
28
32
from bzrlib.revision import NULL_REVISION
29
import bzrlib.errors as errors
30
import bzrlib.gpg as gpg
31
from bzrlib.store import copy_all
32
from bzrlib.store.weave import WeaveStore
33
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
33
34
from bzrlib.store.text import TextStore
34
35
from bzrlib.symbol_versioning import *
35
36
from bzrlib.trace import mutter
36
37
from bzrlib.tree import RevisionTree
38
from bzrlib.tsort import topo_sort
37
39
from bzrlib.testament import Testament
38
40
from bzrlib.tree import EmptyTree
42
from bzrlib.weave import WeaveFile
219
226
self.copy_content_into(result, revision_id, basis)
222
230
def has_revision(self, revision_id):
223
"""True if this branch has a copy of the revision.
225
This does not necessarily imply the revision is merge
226
or on the mainline."""
227
return (revision_id is None
228
or self.revision_store.has_id(revision_id))
231
def get_revision_xml_file(self, revision_id):
232
"""Return XML file object for revision object."""
233
if not revision_id or not isinstance(revision_id, basestring):
234
raise InvalidRevisionId(revision_id=revision_id, branch=self)
236
return self.revision_store.get(revision_id)
237
except (IndexError, KeyError):
238
raise bzrlib.errors.NoSuchRevision(self, revision_id)
241
def get_revision_xml(self, revision_id):
242
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())
245
236
def get_revision_reconcile(self, revision_id):
250
241
be used by reconcile, or reconcile-alike commands that are correcting
251
242
or testing the revision graph.
253
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())
256
r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
257
except SyntaxError, e:
258
raise bzrlib.errors.BzrError('failed to unpack revision_xml',
262
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()
266
259
def get_revision(self, revision_id):
293
286
# but it is a ghost
294
287
raise errors.CorruptRepository(self)
297
def get_revision_sha1(self, revision_id):
298
"""Hash the stored value of a revision, and return it."""
299
# In the future, revision entries will be signed. At that
300
# point, it is probably best *not* to include the signature
301
# in the revision hash. Because that lets you re-sign
302
# the revision, (add signatures/remove signatures) and still
303
# have all hash pointers stay consistent.
304
# But for now, just hash the contents.
305
return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
307
289
@needs_write_lock
308
290
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
309
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())
312
296
def fileid_involved_between_revs(self, from_revid, to_revid):
313
297
"""Find file_id(s) which are involved in the changes between revisions.
315
299
This determines the set of revisions which are involved, and then
316
300
finds all file ids affected by those revisions.
318
# TODO: jam 20060119 This code assumes that w.inclusions will
319
# always be correct. But because of the presence of ghosts
320
# it is possible to be wrong.
321
# One specific example from Robert Collins:
322
# Two branches, with revisions ABC, and AD
323
# C is a ghost merge of D.
324
# Inclusions doesn't recognize D as an ancestor.
325
# If D is ever merged in the future, the weave
326
# won't be fixed, because AD never saw revision C
327
# to cause a conflict which would force a reweave.
328
302
w = self.get_inventory_weave()
329
from_set = set(w.inclusions([w.lookup(from_revid)]))
330
to_set = set(w.inclusions([w.lookup(to_revid)]))
331
included = to_set.difference(from_set)
332
changed = map(w.idx_to_name, included)
303
from_set = set(w.get_ancestry(from_revid))
304
to_set = set(w.get_ancestry(to_revid))
305
changed = to_set.difference(from_set)
333
306
return self._fileid_involved_by_set(changed)
335
308
def fileid_involved(self, last_revid=None):
378
350
w = self.get_inventory_weave()
380
for line in w._weave:
382
# it is ugly, but it is due to the weave structure
383
if not isinstance(line, basestring): continue
353
# this code needs to read every line in every inventory for the
354
# inventories [changes]. Seeing a line twice is ok. Seeing a line
355
# not pesent in one of those inventories is unnecessary and not
356
# harmful because we are filtering by the revision id marker in the
357
# inventory lines to only select file ids altered in one of those
358
# revisions. We dont need to see all lines in the inventory because
359
# only those added in an inventory in rev X can contain a revision=X
361
for line in w.iter_lines_added_or_present_in_versions(changes):
385
362
start = line.find('file_id="')+9
386
363
if start < 9: continue
387
364
end = line.find('"', start)
389
file_id = xml.sax.saxutils.unescape(line[start:end])
366
file_id = _unescape_xml(line[start:end])
391
368
# check if file_id is already present
392
369
if file_id in file_ids: continue
434
def get_revision_graph_with_ghosts(self, revision_ids=None):
435
"""Return a graph of the revisions with ghosts marked as applicable.
437
:param revision_ids: an iterable of revisions to graph or None for all.
438
:return: a Graph object with the graph reachable from revision_ids.
442
pending = set(self.all_revision_ids())
445
pending = set(revision_ids)
446
required = set(revision_ids)
449
revision_id = pending.pop()
451
rev = self.get_revision(revision_id)
452
except errors.NoSuchRevision:
453
if revision_id in required:
456
result.add_ghost(revision_id)
458
for parent_id in rev.parent_ids:
459
# is this queued or done ?
460
if (parent_id not in pending and
461
parent_id not in done):
463
pending.add(parent_id)
464
result.add_node(revision_id, rev.parent_ids)
465
done.add(revision_id)
458
469
def get_revision_inventory(self, revision_id):
459
470
"""Return inventory of a past revision."""
460
471
# TODO: Unify this with get_inventory()
545
563
:param new_value: True to restore the default, False to disable making
548
# FIXME: split out into a new class/strategy ?
549
if isinstance(self._format, (RepositoryFormat4,
552
raise NotImplementedError(self.set_make_working_trees)
555
self.control_files._transport.delete('no-working-trees')
556
except errors.NoSuchFile:
559
self.control_files.put_utf8('no-working-trees', '')
566
raise NotImplementedError(self.set_make_working_trees)
561
568
def make_working_trees(self):
562
569
"""Returns the policy for making working trees on new branches."""
563
# FIXME: split out into a new class/strategy ?
564
if isinstance(self._format, (RepositoryFormat4,
568
return not self.control_files._transport.has('no-working-trees')
570
raise NotImplementedError(self.make_working_trees)
570
572
@needs_write_lock
571
573
def sign_revision(self, revision_id, gpg_strategy):
572
574
plaintext = Testament.from_revision(self, revision_id).as_short_text()
573
575
self.store_revision_signature(gpg_strategy, plaintext, revision_id)
578
def has_signature_for_revision_id(self, revision_id):
579
"""Query for a revision signature for revision_id in the repository."""
580
return self._revision_store.has_signature(revision_id,
581
self.get_transaction())
584
def get_signature_text(self, revision_id):
585
"""Return the text for a signature."""
586
return self._revision_store.get_signature_text(revision_id,
587
self.get_transaction())
576
590
class AllInOneRepository(Repository):
577
591
"""Legacy support - the repository behaviour for all-in-one branches."""
579
def __init__(self, _format, a_bzrdir, revision_store):
593
def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
580
594
# we reuse one control files instance.
581
595
dir_mode = a_bzrdir._control_files._dir_mode
582
596
file_mode = a_bzrdir._control_files._file_mode
614
628
# not broken out yet because the controlweaves|inventory_store
615
629
# and text_store | weave_store bits are still different.
616
630
if isinstance(_format, RepositoryFormat4):
631
# cannot remove these - there is still no consistent api
632
# which allows access to this old info.
617
633
self.inventory_store = get_store('inventory-store')
618
self.text_store = get_store('text-store')
619
elif isinstance(_format, RepositoryFormat5):
620
self.control_weaves = get_weave('')
621
self.weave_store = get_weave('weaves')
622
elif isinstance(_format, RepositoryFormat6):
623
self.control_weaves = get_weave('')
624
self.weave_store = get_weave('weaves', prefixed=True)
626
raise errors.BzrError('unreachable code: unexpected repository'
628
revision_store.register_suffix('sig')
629
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, revision_store)
634
text_store = get_store('text-store')
635
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
639
"""AllInOne repositories cannot be shared."""
643
def set_make_working_trees(self, new_value):
644
"""Set the policy flag for making working trees when creating branches.
646
This only applies to branches that use this repository.
648
The default is 'True'.
649
:param new_value: True to restore the default, False to disable making
652
raise NotImplementedError(self.set_make_working_trees)
654
def make_working_trees(self):
655
"""Returns the policy for making working trees on new branches."""
632
659
class MetaDirRepository(Repository):
633
660
"""Repositories in the new meta-dir layout."""
635
def __init__(self, _format, a_bzrdir, control_files, revision_store):
662
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
636
663
super(MetaDirRepository, self).__init__(_format,
641
670
dir_mode = self.control_files._dir_mode
642
671
file_mode = self.control_files._file_mode
655
684
ws.enable_cache = True
658
if isinstance(self._format, RepositoryFormat7):
659
self.control_weaves = get_weave('')
660
self.weave_store = get_weave('weaves', prefixed=True)
661
elif isinstance(self._format, RepositoryFormatKnit1):
662
self.control_weaves = get_weave('')
663
self.weave_store = get_weave('knits', prefixed=True)
665
raise errors.BzrError('unreachable code: unexpected repository'
689
"""Return True if this repository is flagged as a shared repository."""
690
return self.control_files._transport.has('shared-storage')
693
def set_make_working_trees(self, new_value):
694
"""Set the policy flag for making working trees when creating branches.
696
This only applies to branches that use this repository.
698
The default is 'True'.
699
:param new_value: True to restore the default, False to disable making
704
self.control_files._transport.delete('no-working-trees')
705
except errors.NoSuchFile:
708
self.control_files.put_utf8('no-working-trees', '')
710
def make_working_trees(self):
711
"""Returns the policy for making working trees on new branches."""
712
return not self.control_files._transport.has('no-working-trees')
715
class KnitRepository(MetaDirRepository):
716
"""Knit format repository."""
719
def all_revision_ids(self):
720
"""See Repository.all_revision_ids()."""
721
return self._revision_store.all_revision_ids(self.get_transaction())
723
def fileid_involved_between_revs(self, from_revid, to_revid):
724
"""Find file_id(s) which are involved in the changes between revisions.
726
This determines the set of revisions which are involved, and then
727
finds all file ids affected by those revisions.
729
vf = self._get_revision_vf()
730
from_set = set(vf.get_ancestry(from_revid))
731
to_set = set(vf.get_ancestry(to_revid))
732
changed = to_set.difference(from_set)
733
return self._fileid_involved_by_set(changed)
735
def fileid_involved(self, last_revid=None):
736
"""Find all file_ids modified in the ancestry of last_revid.
738
:param last_revid: If None, last_revision() will be used.
741
changed = set(self.all_revision_ids())
743
changed = set(self.get_ancestry(last_revid))
746
return self._fileid_involved_by_set(changed)
749
def get_ancestry(self, revision_id):
750
"""Return a list of revision-ids integrated by a revision.
752
This is topologically sorted.
754
if revision_id is None:
756
vf = self._get_revision_vf()
758
return [None] + vf.get_ancestry(revision_id)
759
except errors.RevisionNotPresent:
760
raise errors.NoSuchRevision(self, revision_id)
763
def get_revision(self, revision_id):
764
"""Return the Revision object for a named revision"""
765
return self.get_revision_reconcile(revision_id)
768
def get_revision_graph(self, revision_id=None):
769
"""Return a dictionary containing the revision graph.
771
:return: a dictionary of revision_id->revision_parents_list.
773
weave = self._get_revision_vf()
774
entire_graph = weave.get_graph()
775
if revision_id is None:
776
return weave.get_graph()
777
elif revision_id not in weave:
778
raise errors.NoSuchRevision(self, revision_id)
780
# add what can be reached from revision_id
782
pending = set([revision_id])
783
while len(pending) > 0:
785
result[node] = weave.get_parents(node)
786
for revision_id in result[node]:
787
if revision_id not in result:
788
pending.add(revision_id)
792
def get_revision_graph_with_ghosts(self, revision_ids=None):
793
"""Return a graph of the revisions with ghosts marked as applicable.
795
:param revision_ids: an iterable of revisions to graph or None for all.
796
:return: a Graph object with the graph reachable from revision_ids.
799
vf = self._get_revision_vf()
800
versions = vf.versions()
802
pending = set(self.all_revision_ids())
805
pending = set(revision_ids)
806
required = set(revision_ids)
809
revision_id = pending.pop()
810
if not revision_id in versions:
811
if revision_id in required:
812
raise errors.NoSuchRevision(self, revision_id)
814
result.add_ghost(revision_id)
816
parent_ids = vf.get_parents_with_ghosts(revision_id)
817
for parent_id in parent_ids:
818
# is this queued or done ?
819
if (parent_id not in pending and
820
parent_id not in done):
822
pending.add(parent_id)
823
result.add_node(revision_id, parent_ids)
827
def _get_revision_vf(self):
828
""":return: a versioned file containing the revisions."""
829
vf = self._revision_store.get_revision_file(self.get_transaction())
834
"""Reconcile this repository."""
835
from bzrlib.reconcile import KnitReconciler
836
reconciler = KnitReconciler(self)
837
reconciler.reconcile()
840
def revision_parents(self, revid):
841
return self._get_revision_vf().get_parents(rev_id)
669
843
class RepositoryFormat(object):
670
844
"""A repository format.
725
903
"""Return the revision store object for this a_bzrdir."""
726
904
raise NotImplementedError(self._get_revision_store)
728
def _get_rev_store(self,
906
def _get_text_rev_store(self,
734
913
"""Common logic for getting a revision store for a repository.
736
see self._get_revision_store for the method to
915
see self._get_revision_store for the subclass-overridable method to
737
916
get the store for a repository.
740
name = safe_unicode(name)
743
dir_mode = control_files._dir_mode
744
file_mode = control_files._file_mode
745
revision_store =TextStore(transport.clone(name),
747
compressed=compressed,
750
revision_store.register_suffix('sig')
751
return revision_store
918
from bzrlib.store.revision.text import TextRevisionStore
919
dir_mode = control_files._dir_mode
920
file_mode = control_files._file_mode
921
text_store =TextStore(transport.clone(name),
923
compressed=compressed,
926
_revision_store = TextRevisionStore(text_store, serializer)
927
return _revision_store
929
def _get_versioned_file_store(self,
934
versionedfile_class=WeaveFile):
935
weave_transport = control_files._transport.clone(name)
936
dir_mode = control_files._dir_mode
937
file_mode = control_files._file_mode
938
return VersionedFileStore(weave_transport, prefixed=prefixed,
941
versionedfile_class=versionedfile_class)
753
943
def initialize(self, a_bzrdir, shared=False):
754
944
"""Initialize a repository of this format in a_bzrdir.
944
1169
control_files.create_lock()
945
1170
return control_files
947
def _get_revision_store(self, repo_transport, control_files):
948
"""See RepositoryFormat._get_revision_store()."""
949
return self._get_rev_store(repo_transport,
956
def open(self, a_bzrdir, _found=False, _override_transport=None):
957
"""See RepositoryFormat.open().
959
:param _override_transport: INTERNAL USE ONLY. Allows opening the
960
repository at a slightly different url
961
than normal. I.e. during 'upgrade'.
964
format = RepositoryFormat.find_format(a_bzrdir)
965
assert format.__class__ == self.__class__
966
if _override_transport is not None:
967
repo_transport = _override_transport
969
repo_transport = a_bzrdir.get_repository_transport(None)
970
control_files = LockableFiles(repo_transport, 'lock', LockDir)
971
revision_store = self._get_revision_store(repo_transport, control_files)
972
return MetaDirRepository(_format=self,
974
control_files=control_files,
975
revision_store=revision_store)
977
1172
def _upload_blank_content(self, a_bzrdir, dirs, files, utf8_files, shared):
978
1173
"""Upload the initial blank content."""
979
1174
control_files = self._create_control_files(a_bzrdir)
1003
1198
- an optional 'no-working-trees' flag
1201
def _get_control_store(self, repo_transport, control_files):
1202
"""Return the control store for this repository."""
1203
return self._get_versioned_file_store('',
1006
1208
def get_format_string(self):
1007
1209
"""See RepositoryFormat.get_format_string()."""
1008
1210
return "Bazaar-NG Repository format 7"
1212
def _get_revision_store(self, repo_transport, control_files):
1213
"""See RepositoryFormat._get_revision_store()."""
1214
return self._get_text_rev_store(repo_transport,
1221
def _get_text_store(self, transport, control_files):
1222
"""See RepositoryFormat._get_text_store()."""
1223
return self._get_versioned_file_store('weaves',
1010
1227
def initialize(self, a_bzrdir, shared=False):
1011
1228
"""Create a weave repository.
1030
1247
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1031
1248
return self.open(a_bzrdir=a_bzrdir, _found=True)
1250
def open(self, a_bzrdir, _found=False, _override_transport=None):
1251
"""See RepositoryFormat.open().
1253
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1254
repository at a slightly different url
1255
than normal. I.e. during 'upgrade'.
1258
format = RepositoryFormat.find_format(a_bzrdir)
1259
assert format.__class__ == self.__class__
1260
if _override_transport is not None:
1261
repo_transport = _override_transport
1263
repo_transport = a_bzrdir.get_repository_transport(None)
1264
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1265
text_store = self._get_text_store(repo_transport, control_files)
1266
control_store = self._get_control_store(repo_transport, control_files)
1267
_revision_store = self._get_revision_store(repo_transport, control_files)
1268
return MetaDirRepository(_format=self,
1270
control_files=control_files,
1271
_revision_store=_revision_store,
1272
control_store=control_store,
1273
text_store=text_store)
1034
1276
class RepositoryFormatKnit1(MetaDirRepositoryFormat):
1035
1277
"""Bzr repository knit format 1.
1045
1287
- a LockDir lock
1290
def _get_control_store(self, repo_transport, control_files):
1291
"""Return the control store for this repository."""
1292
return self._get_versioned_file_store('',
1296
versionedfile_class=KnitVersionedFile)
1048
1298
def get_format_string(self):
1049
1299
"""See RepositoryFormat.get_format_string()."""
1050
1300
return "Bazaar-NG Knit Repository Format 1"
1302
def _get_revision_store(self, repo_transport, control_files):
1303
"""See RepositoryFormat._get_revision_store()."""
1304
from bzrlib.store.revision.knit import KnitRevisionStore
1305
versioned_file_store = VersionedFileStore(
1307
file_mode = control_files._file_mode,
1310
versionedfile_class=KnitVersionedFile)
1311
return KnitRevisionStore(versioned_file_store)
1313
def _get_text_store(self, transport, control_files):
1314
"""See RepositoryFormat._get_text_store()."""
1315
return self._get_versioned_file_store('knits',
1318
versionedfile_class=KnitVersionedFile)
1052
1320
def initialize(self, a_bzrdir, shared=False):
1053
1321
"""Create a knit format 1 repository.
1066
1334
empty_weave = sio.getvalue()
1068
1336
mutter('creating repository in %s.', a_bzrdir.transport.base)
1069
dirs = ['revision-store', 'knits']
1070
files = [('inventory.weave', StringIO(empty_weave)),
1337
dirs = ['revision-store', 'knits', 'control']
1338
files = [('control/inventory.weave', StringIO(empty_weave)),
1072
1340
utf8_files = [('format', self.get_format_string())]
1074
1342
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1343
repo_transport = a_bzrdir.get_repository_transport(None)
1344
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1345
control_store = self._get_control_store(repo_transport, control_files)
1346
transaction = bzrlib.transactions.WriteTransaction()
1347
# trigger a write of the inventory store.
1348
control_store.get_weave_or_empty('inventory', transaction)
1349
_revision_store = self._get_revision_store(repo_transport, control_files)
1350
_revision_store.has_revision_id('A', transaction)
1351
_revision_store.get_signature_file(transaction)
1075
1352
return self.open(a_bzrdir=a_bzrdir, _found=True)
1354
def open(self, a_bzrdir, _found=False, _override_transport=None):
1355
"""See RepositoryFormat.open().
1357
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1358
repository at a slightly different url
1359
than normal. I.e. during 'upgrade'.
1362
format = RepositoryFormat.find_format(a_bzrdir)
1363
assert format.__class__ == self.__class__
1364
if _override_transport is not None:
1365
repo_transport = _override_transport
1367
repo_transport = a_bzrdir.get_repository_transport(None)
1368
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1369
text_store = self._get_text_store(repo_transport, control_files)
1370
control_store = self._get_control_store(repo_transport, control_files)
1371
_revision_store = self._get_revision_store(repo_transport, control_files)
1372
return KnitRepository(_format=self,
1374
control_files=control_files,
1375
_revision_store=_revision_store,
1376
control_store=control_store,
1377
text_store=text_store)
1078
1380
# formats which have no format string are not discoverable
1079
1381
# and not independently creatable, so are not registered.
1164
1452
Returns the copied revision count and the failed revisions in a tuple:
1165
1453
(copied, failures).
1167
from bzrlib.fetch import RepoFetcher
1455
from bzrlib.fetch import GenericRepoFetcher
1168
1456
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1169
1457
self.source, self.source._format, self.target, self.target._format)
1170
f = RepoFetcher(to_repository=self.target,
1171
from_repository=self.source,
1172
last_revision=revision_id,
1458
f = GenericRepoFetcher(to_repository=self.target,
1459
from_repository=self.source,
1460
last_revision=revision_id,
1174
1462
return f.count_copied, f.failed_revisions
1177
def get(klass, repository_source, repository_target):
1178
"""Retrieve a InterRepository worker object for these repositories.
1180
:param repository_source: the repository to be the 'source' member of
1181
the InterRepository instance.
1182
:param repository_target: the repository to be the 'target' member of
1183
the InterRepository instance.
1184
If an optimised InterRepository worker exists it will be used otherwise
1185
a default InterRepository instance will be created.
1187
for provider in klass._optimisers:
1188
if provider.is_compatible(repository_source, repository_target):
1189
return provider(repository_source, repository_target)
1190
return InterRepository(repository_source, repository_target)
1192
1464
def lock_read(self):
1193
1465
"""Take out a logical read lock.
1290
1552
# FIXME do not peek!
1291
1553
if self.source.control_files._transport.listable():
1292
pb = bzrlib.ui.ui_factory.progress_bar()
1293
copy_all(self.source.weave_store,
1294
self.target.weave_store, pb=pb)
1295
pb.update('copying inventory', 0, 1)
1296
self.target.control_weaves.copy_multi(
1297
self.source.control_weaves, ['inventory'])
1298
copy_all(self.source.revision_store,
1299
self.target.revision_store, pb=pb)
1554
pb = bzrlib.ui.ui_factory.nested_progress_bar()
1556
self.target.weave_store.copy_all_ids(
1557
self.source.weave_store,
1559
from_transaction=self.source.get_transaction(),
1560
to_transaction=self.target.get_transaction())
1561
pb.update('copying inventory', 0, 1)
1562
self.target.control_weaves.copy_multi(
1563
self.source.control_weaves, ['inventory'],
1564
from_transaction=self.source.get_transaction(),
1565
to_transaction=self.target.get_transaction())
1566
self.target._revision_store.text_store.copy_all_ids(
1567
self.source._revision_store.text_store,
1301
1572
self.target.fetch(self.source, revision_id=revision_id)
1303
1574
@needs_write_lock
1304
1575
def fetch(self, revision_id=None, pb=None):
1305
1576
"""See InterRepository.fetch()."""
1306
from bzrlib.fetch import RepoFetcher
1577
from bzrlib.fetch import GenericRepoFetcher
1307
1578
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1308
1579
self.source, self.source._format, self.target, self.target._format)
1309
f = RepoFetcher(to_repository=self.target,
1310
from_repository=self.source,
1311
last_revision=revision_id,
1580
f = GenericRepoFetcher(to_repository=self.target,
1581
from_repository=self.source,
1582
last_revision=revision_id,
1313
1584
return f.count_copied, f.failed_revisions
1315
1586
@needs_read_lock
1353
1624
return self.source._eliminate_revisions_not_present(required_topo_revisions)
1627
class InterKnitRepo(InterRepository):
1628
"""Optimised code paths between Knit based repositories."""
1630
_matching_repo_format = RepositoryFormatKnit1()
1631
"""Repository format for testing with."""
1634
def is_compatible(source, target):
1635
"""Be compatible with known Knit formats.
1637
We dont test for the stores being of specific types becase that
1638
could lead to confusing results, and there is no need to be
1642
return (isinstance(source._format, (RepositoryFormatKnit1)) and
1643
isinstance(target._format, (RepositoryFormatKnit1)))
1644
except AttributeError:
1648
def fetch(self, revision_id=None, pb=None):
1649
"""See InterRepository.fetch()."""
1650
from bzrlib.fetch import KnitRepoFetcher
1651
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1652
self.source, self.source._format, self.target, self.target._format)
1653
f = KnitRepoFetcher(to_repository=self.target,
1654
from_repository=self.source,
1655
last_revision=revision_id,
1657
return f.count_copied, f.failed_revisions
1660
def missing_revision_ids(self, revision_id=None):
1661
"""See InterRepository.missing_revision_ids()."""
1662
if revision_id is not None:
1663
source_ids = self.source.get_ancestry(revision_id)
1664
assert source_ids.pop(0) == None
1666
source_ids = self.source._all_possible_ids()
1667
source_ids_set = set(source_ids)
1668
# source_ids is the worst possible case we may need to pull.
1669
# now we want to filter source_ids against what we actually
1670
# have in target, but dont try to check for existence where we know
1671
# we do not have a revision as that would be pointless.
1672
target_ids = set(self.target._all_possible_ids())
1673
possibly_present_revisions = target_ids.intersection(source_ids_set)
1674
actually_present_revisions = set(self.target._eliminate_revisions_not_present(possibly_present_revisions))
1675
required_revisions = source_ids_set.difference(actually_present_revisions)
1676
required_topo_revisions = [rev_id for rev_id in source_ids if rev_id in required_revisions]
1677
if revision_id is not None:
1678
# we used get_ancestry to determine source_ids then we are assured all
1679
# revisions referenced are present as they are installed in topological order.
1680
# and the tip revision was validated by get_ancestry.
1681
return required_topo_revisions
1683
# if we just grabbed the possibly available ids, then
1684
# we only have an estimate of whats available and need to validate
1685
# that against the revision records.
1686
return self.source._eliminate_revisions_not_present(required_topo_revisions)
1356
1688
InterRepository.register_optimiser(InterWeaveRepo)
1689
InterRepository.register_optimiser(InterKnitRepo)
1359
1692
class RepositoryTestProviderAdapter(object):