14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from binascii import hexlify
18
from copy import deepcopy
19
17
from cStringIO import StringIO
19
from bzrlib.lazy_import import lazy_import
20
lazy_import(globals(), """
22
from unittest import TestSuite
24
24
from bzrlib import (
38
revision as _mod_revision,
43
from bzrlib.revisiontree import RevisionTree
44
from bzrlib.store.versioned import VersionedFileStore
45
from bzrlib.store.text import TextStore
46
from bzrlib.testament import Testament
36
50
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
from bzrlib.errors import InvalidRevisionId
38
from bzrlib.graph import Graph
39
51
from bzrlib.inter import InterObject
40
52
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
41
from bzrlib.knit import KnitVersionedFile, KnitPlainFactory
42
from bzrlib.lockable_files import LockableFiles, TransportLock
43
from bzrlib.lockdir import LockDir
44
from bzrlib.osutils import (safe_unicode, rand_bytes, compact_date,
46
from bzrlib.revision import NULL_REVISION, Revision
47
from bzrlib.revisiontree import RevisionTree
48
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
49
from bzrlib.store.text import TextStore
50
from bzrlib import symbol_versioning
51
from bzrlib.symbol_versioning import (deprecated_method,
53
from bzrlib.symbol_versioning import (
54
from bzrlib.testament import Testament
55
57
from bzrlib.trace import mutter, note, warning
56
from bzrlib.tsort import topo_sort
57
from bzrlib.weave import WeaveFile
60
60
# Old formats display a warning, but only once
61
61
_deprecation_warning_done = False
64
######################################################################
64
67
class Repository(object):
65
68
"""Repository holding history for one or more branches.
79
_file_ids_altered_regex = lazy_regex.lazy_compile(
80
r'file_id="(?P<file_id>[^"]+)"'
81
r'.*revision="(?P<revision_id>[^"]+)"'
77
def add_inventory(self, revid, inv, parents):
78
"""Add the inventory inv to the repository as revid.
85
def add_inventory(self, revision_id, inv, parents):
86
"""Add the inventory inv to the repository as revision_id.
80
:param parents: The revision ids of the parents that revid
88
:param parents: The revision ids of the parents that revision_id
81
89
is known to have and are in the repository already.
83
91
returns the sha1 of the serialized inventory.
85
assert inv.revision_id is None or inv.revision_id == revid, \
93
revision_id = osutils.safe_revision_id(revision_id)
94
_mod_revision.check_not_reserved_id(revision_id)
95
assert inv.revision_id is None or inv.revision_id == revision_id, \
86
96
"Mismatch between inventory revision" \
87
" id and insertion revid (%r, %r)" % (inv.revision_id, revid)
97
" id and insertion revid (%r, %r)" % (inv.revision_id, revision_id)
88
98
assert inv.root is not None
89
99
inv_text = self.serialise_inventory(inv)
90
100
inv_sha1 = osutils.sha_string(inv_text)
91
101
inv_vf = self.control_weaves.get_weave('inventory',
92
102
self.get_transaction())
93
self._inventory_add_lines(inv_vf, revid, parents, osutils.split_lines(inv_text))
103
self._inventory_add_lines(inv_vf, revision_id, parents,
104
osutils.split_lines(inv_text))
96
def _inventory_add_lines(self, inv_vf, revid, parents, lines):
107
def _inventory_add_lines(self, inv_vf, revision_id, parents, lines):
97
108
final_parents = []
98
109
for parent in parents:
99
110
if parent in inv_vf:
100
111
final_parents.append(parent)
102
inv_vf.add_lines(revid, final_parents, lines)
113
inv_vf.add_lines(revision_id, final_parents, lines)
104
115
@needs_write_lock
105
def add_revision(self, rev_id, rev, inv=None, config=None):
106
"""Add rev to the revision store as rev_id.
116
def add_revision(self, revision_id, rev, inv=None, config=None):
117
"""Add rev to the revision store as revision_id.
108
:param rev_id: the revision id to use.
119
:param revision_id: the revision id to use.
109
120
:param rev: The revision object.
110
121
:param inv: The inventory for the revision. if None, it will be looked
111
122
up in the inventory storer
224
257
def get_physical_lock_status(self):
225
258
return self.control_files.get_physical_lock_status()
260
def leave_lock_in_place(self):
261
"""Tell this repository not to release the physical lock when this
264
If lock_write doesn't return a token, then this method is not supported.
266
self.control_files.leave_in_place()
268
def dont_leave_lock_in_place(self):
269
"""Tell this repository to release the physical lock when this
270
object is unlocked, even if it didn't originally acquire it.
272
If lock_write doesn't return a token, then this method is not supported.
274
self.control_files.dont_leave_in_place()
277
def gather_stats(self, revid=None, committers=None):
278
"""Gather statistics from a revision id.
280
:param revid: The revision id to gather statistics from, if None, then
281
no revision specific statistics are gathered.
282
:param committers: Optional parameter controlling whether to grab
283
a count of committers from the revision specific statistics.
284
:return: A dictionary of statistics. Currently this contains:
285
committers: The number of committers if requested.
286
firstrev: A tuple with timestamp, timezone for the penultimate left
287
most ancestor of revid, if revid is not the NULL_REVISION.
288
latestrev: A tuple with timestamp, timezone for revid, if revid is
289
not the NULL_REVISION.
290
revisions: The total revision count in the repository.
291
size: An estimate disk size of the repository in bytes.
294
if revid and committers:
295
result['committers'] = 0
296
if revid and revid != _mod_revision.NULL_REVISION:
298
all_committers = set()
299
revisions = self.get_ancestry(revid)
300
# pop the leading None
302
first_revision = None
304
# ignore the revisions in the middle - just grab first and last
305
revisions = revisions[0], revisions[-1]
306
for revision in self.get_revisions(revisions):
307
if not first_revision:
308
first_revision = revision
310
all_committers.add(revision.committer)
311
last_revision = revision
313
result['committers'] = len(all_committers)
314
result['firstrev'] = (first_revision.timestamp,
315
first_revision.timezone)
316
result['latestrev'] = (last_revision.timestamp,
317
last_revision.timezone)
319
# now gather global repository information
320
if self.bzrdir.root_transport.listable():
321
c, t = self._revision_store.total_size(self.get_transaction())
322
result['revisions'] = c
228
327
def missing_revision_ids(self, other, revision_id=None):
229
328
"""Return the revision ids that other has that this does not.
717
959
def supports_rich_root(self):
718
960
return self._format.rich_root_data
721
class AllInOneRepository(Repository):
722
"""Legacy support - the repository behaviour for all-in-one branches."""
724
def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
725
# we reuse one control files instance.
726
dir_mode = a_bzrdir._control_files._dir_mode
727
file_mode = a_bzrdir._control_files._file_mode
729
def get_store(name, compressed=True, prefixed=False):
730
# FIXME: This approach of assuming stores are all entirely compressed
731
# or entirely uncompressed is tidy, but breaks upgrade from
732
# some existing branches where there's a mixture; we probably
733
# still want the option to look for both.
734
relpath = a_bzrdir._control_files._escape(name)
735
store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
736
prefixed=prefixed, compressed=compressed,
739
#if self._transport.should_cache():
740
# cache_path = os.path.join(self.cache_root, name)
741
# os.mkdir(cache_path)
742
# store = bzrlib.store.CachedStore(store, cache_path)
745
# not broken out yet because the controlweaves|inventory_store
746
# and text_store | weave_store bits are still different.
747
if isinstance(_format, RepositoryFormat4):
748
# cannot remove these - there is still no consistent api
749
# which allows access to this old info.
750
self.inventory_store = get_store('inventory-store')
751
text_store = get_store('text-store')
752
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
756
"""AllInOne repositories cannot be shared."""
760
def set_make_working_trees(self, new_value):
761
"""Set the policy flag for making working trees when creating branches.
763
This only applies to branches that use this repository.
765
The default is 'True'.
766
:param new_value: True to restore the default, False to disable making
769
raise NotImplementedError(self.set_make_working_trees)
771
def make_working_trees(self):
772
"""Returns the policy for making working trees on new branches."""
962
def _check_ascii_revisionid(self, revision_id, method):
963
"""Private helper for ascii-only repositories."""
964
# weave repositories refuse to store revisionids that are non-ascii.
965
if revision_id is not None:
966
# weaves require ascii revision ids.
967
if isinstance(revision_id, unicode):
969
revision_id.encode('ascii')
970
except UnicodeEncodeError:
971
raise errors.NonAsciiRevisionId(method, self)
974
revision_id.decode('ascii')
975
except UnicodeDecodeError:
976
raise errors.NonAsciiRevisionId(method, self)
980
# remove these delegates a while after bzr 0.15
981
def __make_delegated(name, from_module):
982
def _deprecated_repository_forwarder():
983
symbol_versioning.warn('%s moved to %s in bzr 0.15'
984
% (name, from_module),
987
m = __import__(from_module, globals(), locals(), [name])
989
return getattr(m, name)
990
except AttributeError:
991
raise AttributeError('module %s has no name %s'
993
globals()[name] = _deprecated_repository_forwarder
996
'AllInOneRepository',
997
'WeaveMetaDirRepository',
998
'PreSplitOutRepositoryFormat',
1000
'RepositoryFormat5',
1001
'RepositoryFormat6',
1002
'RepositoryFormat7',
1004
__make_delegated(_name, 'bzrlib.repofmt.weaverepo')
1008
'RepositoryFormatKnit',
1009
'RepositoryFormatKnit1',
1011
__make_delegated(_name, 'bzrlib.repofmt.knitrepo')
776
1014
def install_revision(repository, rev, revision_tree):
862
1100
return not self.control_files._transport.has('no-working-trees')
865
class KnitRepository(MetaDirRepository):
866
"""Knit format repository."""
868
def _warn_if_deprecated(self):
869
# This class isn't deprecated
872
def _inventory_add_lines(self, inv_vf, revid, parents, lines):
873
inv_vf.add_lines_with_ghosts(revid, parents, lines)
876
def _all_revision_ids(self):
877
"""See Repository.all_revision_ids()."""
878
# Knits get the revision graph from the index of the revision knit, so
879
# it's always possible even if they're on an unlistable transport.
880
return self._revision_store.all_revision_ids(self.get_transaction())
882
def fileid_involved_between_revs(self, from_revid, to_revid):
883
"""Find file_id(s) which are involved in the changes between revisions.
885
This determines the set of revisions which are involved, and then
886
finds all file ids affected by those revisions.
888
vf = self._get_revision_vf()
889
from_set = set(vf.get_ancestry(from_revid))
890
to_set = set(vf.get_ancestry(to_revid))
891
changed = to_set.difference(from_set)
892
return self._fileid_involved_by_set(changed)
894
def fileid_involved(self, last_revid=None):
895
"""Find all file_ids modified in the ancestry of last_revid.
897
:param last_revid: If None, last_revision() will be used.
900
changed = set(self.all_revision_ids())
902
changed = set(self.get_ancestry(last_revid))
905
return self._fileid_involved_by_set(changed)
908
def get_ancestry(self, revision_id):
909
"""Return a list of revision-ids integrated by a revision.
911
This is topologically sorted.
913
if revision_id is None:
915
vf = self._get_revision_vf()
917
return [None] + vf.get_ancestry(revision_id)
918
except errors.RevisionNotPresent:
919
raise errors.NoSuchRevision(self, revision_id)
922
def get_revision(self, revision_id):
923
"""Return the Revision object for a named revision"""
924
return self.get_revision_reconcile(revision_id)
927
def get_revision_graph(self, revision_id=None):
928
"""Return a dictionary containing the revision graph.
930
:param revision_id: The revision_id to get a graph from. If None, then
931
the entire revision graph is returned. This is a deprecated mode of
932
operation and will be removed in the future.
933
:return: a dictionary of revision_id->revision_parents_list.
935
# special case NULL_REVISION
936
if revision_id == NULL_REVISION:
938
weave = self._get_revision_vf()
939
entire_graph = weave.get_graph()
940
if revision_id is None:
941
return weave.get_graph()
942
elif revision_id not in weave:
943
raise errors.NoSuchRevision(self, revision_id)
945
# add what can be reached from revision_id
947
pending = set([revision_id])
948
while len(pending) > 0:
950
result[node] = weave.get_parents(node)
951
for revision_id in result[node]:
952
if revision_id not in result:
953
pending.add(revision_id)
957
def get_revision_graph_with_ghosts(self, revision_ids=None):
958
"""Return a graph of the revisions with ghosts marked as applicable.
960
:param revision_ids: an iterable of revisions to graph or None for all.
961
:return: a Graph object with the graph reachable from revision_ids.
964
vf = self._get_revision_vf()
965
versions = set(vf.versions())
967
pending = set(self.all_revision_ids())
970
pending = set(revision_ids)
971
# special case NULL_REVISION
972
if NULL_REVISION in pending:
973
pending.remove(NULL_REVISION)
974
required = set(pending)
977
revision_id = pending.pop()
978
if not revision_id in versions:
979
if revision_id in required:
980
raise errors.NoSuchRevision(self, revision_id)
982
result.add_ghost(revision_id)
983
# mark it as done so we don't try for it again.
984
done.add(revision_id)
986
parent_ids = vf.get_parents_with_ghosts(revision_id)
987
for parent_id in parent_ids:
988
# is this queued or done ?
989
if (parent_id not in pending and
990
parent_id not in done):
992
pending.add(parent_id)
993
result.add_node(revision_id, parent_ids)
994
done.add(revision_id)
997
def _get_revision_vf(self):
998
""":return: a versioned file containing the revisions."""
999
vf = self._revision_store.get_revision_file(self.get_transaction())
1003
def reconcile(self, other=None, thorough=False):
1004
"""Reconcile this repository."""
1005
from bzrlib.reconcile import KnitReconciler
1006
reconciler = KnitReconciler(self, thorough=thorough)
1007
reconciler.reconcile()
1103
class RepositoryFormatRegistry(registry.Registry):
1104
"""Registry of RepositoryFormats.
1107
def get(self, format_string):
1108
r = registry.Registry.get(self, format_string)
1010
def revision_parents(self, revision_id):
1011
return self._get_revision_vf().get_parents(revision_id)
1014
class KnitRepository2(KnitRepository):
1016
def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1017
control_store, text_store):
1018
KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1019
_revision_store, control_store, text_store)
1020
self._serializer = xml6.serializer_v6
1022
def deserialise_inventory(self, revision_id, xml):
1023
"""Transform the xml into an inventory object.
1025
:param revision_id: The expected revision id of the inventory.
1026
:param xml: A serialised inventory.
1028
result = self._serializer.read_inventory_from_string(xml)
1029
assert result.root.revision is not None
1032
def serialise_inventory(self, inv):
1033
"""Transform the inventory object into XML text.
1035
:param revision_id: The expected revision id of the inventory.
1036
:param xml: A serialised inventory.
1038
assert inv.revision_id is not None
1039
assert inv.root.revision is not None
1040
return KnitRepository.serialise_inventory(self, inv)
1042
def get_commit_builder(self, branch, parents, config, timestamp=None,
1043
timezone=None, committer=None, revprops=None,
1045
"""Obtain a CommitBuilder for this repository.
1047
:param branch: Branch to commit to.
1048
:param parents: Revision ids of the parents of the new revision.
1049
:param config: Configuration to use.
1050
:param timestamp: Optional timestamp recorded for commit.
1051
:param timezone: Optional timezone for timestamp.
1052
:param committer: Optional committer to set for commit.
1053
:param revprops: Optional dictionary of revision properties.
1054
:param revision_id: Optional revision id.
1056
return RootCommitBuilder(self, parents, config, timestamp, timezone,
1057
committer, revprops, revision_id)
1114
format_registry = RepositoryFormatRegistry()
1115
"""Registry of formats, indexed by their identifying format string.
1117
This can contain either format instances themselves, or classes/factories that
1118
can be called to obtain one.
1122
#####################################################################
1123
# Repository Formats
1060
1125
class RepositoryFormat(object):
1061
1126
"""A repository format.
1198
1284
raise NotImplementedError(self.open)
1201
def register_format(klass, format):
1202
klass._formats[format.get_format_string()] = format
1205
def set_default_format(klass, format):
1206
klass._default_format = format
1209
def unregister_format(klass, format):
1210
assert klass._formats[format.get_format_string()] is format
1211
del klass._formats[format.get_format_string()]
1214
class PreSplitOutRepositoryFormat(RepositoryFormat):
1215
"""Base class for the pre split out repository formats."""
1217
rich_root_data = False
1219
def initialize(self, a_bzrdir, shared=False, _internal=False):
1220
"""Create a weave repository.
1222
TODO: when creating split out bzr branch formats, move this to a common
1223
base for Format5, Format6. or something like that.
1225
from bzrlib.weavefile import write_weave_v5
1226
from bzrlib.weave import Weave
1229
raise errors.IncompatibleFormat(self, a_bzrdir._format)
1232
# always initialized when the bzrdir is.
1233
return self.open(a_bzrdir, _found=True)
1235
# Create an empty weave
1237
write_weave_v5(Weave(), sio)
1238
empty_weave = sio.getvalue()
1240
mutter('creating repository in %s.', a_bzrdir.transport.base)
1241
dirs = ['revision-store', 'weaves']
1242
files = [('inventory.weave', StringIO(empty_weave)),
1245
# FIXME: RBC 20060125 don't peek under the covers
1246
# NB: no need to escape relative paths that are url safe.
1247
control_files = LockableFiles(a_bzrdir.transport, 'branch-lock',
1249
control_files.create_lock()
1250
control_files.lock_write()
1251
control_files._transport.mkdir_multi(dirs,
1252
mode=control_files._dir_mode)
1254
for file, content in files:
1255
control_files.put(file, content)
1257
control_files.unlock()
1258
return self.open(a_bzrdir, _found=True)
1260
def _get_control_store(self, repo_transport, control_files):
1261
"""Return the control store for this repository."""
1262
return self._get_versioned_file_store('',
1267
def _get_text_store(self, transport, control_files):
1268
"""Get a store for file texts for this format."""
1269
raise NotImplementedError(self._get_text_store)
1271
def open(self, a_bzrdir, _found=False):
1272
"""See RepositoryFormat.open()."""
1274
# we are being called directly and must probe.
1275
raise NotImplementedError
1277
repo_transport = a_bzrdir.get_repository_transport(None)
1278
control_files = a_bzrdir._control_files
1279
text_store = self._get_text_store(repo_transport, control_files)
1280
control_store = self._get_control_store(repo_transport, control_files)
1281
_revision_store = self._get_revision_store(repo_transport, control_files)
1282
return AllInOneRepository(_format=self,
1284
_revision_store=_revision_store,
1285
control_store=control_store,
1286
text_store=text_store)
1288
def check_conversion_target(self, target_format):
1292
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1293
"""Bzr repository format 4.
1295
This repository format has:
1297
- TextStores for texts, inventories,revisions.
1299
This format is deprecated: it indexes texts using a text id which is
1300
removed in format 5; initialization and write support for this format
1305
super(RepositoryFormat4, self).__init__()
1306
self._matchingbzrdir = bzrdir.BzrDirFormat4()
1308
def get_format_description(self):
1309
"""See RepositoryFormat.get_format_description()."""
1310
return "Repository format 4"
1312
def initialize(self, url, shared=False, _internal=False):
1313
"""Format 4 branches cannot be created."""
1314
raise errors.UninitializableFormat(self)
1316
def is_supported(self):
1317
"""Format 4 is not supported.
1319
It is not supported because the model changed from 4 to 5 and the
1320
conversion logic is expensive - so doing it on the fly was not
1325
def _get_control_store(self, repo_transport, control_files):
1326
"""Format 4 repositories have no formal control store at this point.
1328
This will cause any control-file-needing apis to fail - this is desired.
1332
def _get_revision_store(self, repo_transport, control_files):
1333
"""See RepositoryFormat._get_revision_store()."""
1334
from bzrlib.xml4 import serializer_v4
1335
return self._get_text_rev_store(repo_transport,
1338
serializer=serializer_v4)
1340
def _get_text_store(self, transport, control_files):
1341
"""See RepositoryFormat._get_text_store()."""
1344
class RepositoryFormat5(PreSplitOutRepositoryFormat):
1345
"""Bzr control format 5.
1347
This repository format has:
1348
- weaves for file texts and inventory
1350
- TextStores for revisions and signatures.
1354
super(RepositoryFormat5, self).__init__()
1355
self._matchingbzrdir = bzrdir.BzrDirFormat5()
1357
def get_format_description(self):
1358
"""See RepositoryFormat.get_format_description()."""
1359
return "Weave repository format 5"
1361
def _get_revision_store(self, repo_transport, control_files):
1362
"""See RepositoryFormat._get_revision_store()."""
1363
"""Return the revision store object for this a_bzrdir."""
1364
return self._get_text_rev_store(repo_transport,
1369
def _get_text_store(self, transport, control_files):
1370
"""See RepositoryFormat._get_text_store()."""
1371
return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
1374
class RepositoryFormat6(PreSplitOutRepositoryFormat):
1375
"""Bzr control format 6.
1377
This repository format has:
1378
- weaves for file texts and inventory
1379
- hash subdirectory based stores.
1380
- TextStores for revisions and signatures.
1384
super(RepositoryFormat6, self).__init__()
1385
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1387
def get_format_description(self):
1388
"""See RepositoryFormat.get_format_description()."""
1389
return "Weave repository format 6"
1391
def _get_revision_store(self, repo_transport, control_files):
1392
"""See RepositoryFormat._get_revision_store()."""
1393
return self._get_text_rev_store(repo_transport,
1399
def _get_text_store(self, transport, control_files):
1400
"""See RepositoryFormat._get_text_store()."""
1401
return self._get_versioned_file_store('weaves', transport, control_files)
1404
1287
class MetaDirRepositoryFormat(RepositoryFormat):
1405
1288
"""Common base class for the new repositories using the metadir layout."""
1407
1290
rich_root_data = False
1291
supports_tree_reference = False
1292
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1409
1294
def __init__(self):
1410
1295
super(MetaDirRepositoryFormat, self).__init__()
1411
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1413
1297
def _create_control_files(self, a_bzrdir):
1414
1298
"""Create the required files and the initial control_files object."""
1415
1299
# FIXME: RBC 20060125 don't peek under the covers
1416
1300
# NB: no need to escape relative paths that are url safe.
1417
1301
repository_transport = a_bzrdir.get_repository_transport(self)
1418
control_files = LockableFiles(repository_transport, 'lock', LockDir)
1302
control_files = lockable_files.LockableFiles(repository_transport,
1303
'lock', lockdir.LockDir)
1419
1304
control_files.create_lock()
1420
1305
return control_files
1436
1321
control_files.unlock()
1439
class RepositoryFormat7(MetaDirRepositoryFormat):
1440
"""Bzr repository 7.
1442
This repository format has:
1443
- weaves for file texts and inventory
1444
- hash subdirectory based stores.
1445
- TextStores for revisions and signatures.
1446
- a format marker of its own
1447
- an optional 'shared-storage' flag
1448
- an optional 'no-working-trees' flag
1451
def _get_control_store(self, repo_transport, control_files):
1452
"""Return the control store for this repository."""
1453
return self._get_versioned_file_store('',
1458
def get_format_string(self):
1459
"""See RepositoryFormat.get_format_string()."""
1460
return "Bazaar-NG Repository format 7"
1462
def get_format_description(self):
1463
"""See RepositoryFormat.get_format_description()."""
1464
return "Weave repository format 7"
1466
def check_conversion_target(self, target_format):
1469
def _get_revision_store(self, repo_transport, control_files):
1470
"""See RepositoryFormat._get_revision_store()."""
1471
return self._get_text_rev_store(repo_transport,
1478
def _get_text_store(self, transport, control_files):
1479
"""See RepositoryFormat._get_text_store()."""
1480
return self._get_versioned_file_store('weaves',
1484
def initialize(self, a_bzrdir, shared=False):
1485
"""Create a weave repository.
1487
:param shared: If true the repository will be initialized as a shared
1490
from bzrlib.weavefile import write_weave_v5
1491
from bzrlib.weave import Weave
1493
# Create an empty weave
1495
write_weave_v5(Weave(), sio)
1496
empty_weave = sio.getvalue()
1498
mutter('creating repository in %s.', a_bzrdir.transport.base)
1499
dirs = ['revision-store', 'weaves']
1500
files = [('inventory.weave', StringIO(empty_weave)),
1502
utf8_files = [('format', self.get_format_string())]
1504
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1505
return self.open(a_bzrdir=a_bzrdir, _found=True)
1507
def open(self, a_bzrdir, _found=False, _override_transport=None):
1508
"""See RepositoryFormat.open().
1510
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1511
repository at a slightly different url
1512
than normal. I.e. during 'upgrade'.
1515
format = RepositoryFormat.find_format(a_bzrdir)
1516
assert format.__class__ == self.__class__
1517
if _override_transport is not None:
1518
repo_transport = _override_transport
1520
repo_transport = a_bzrdir.get_repository_transport(None)
1521
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1522
text_store = self._get_text_store(repo_transport, control_files)
1523
control_store = self._get_control_store(repo_transport, control_files)
1524
_revision_store = self._get_revision_store(repo_transport, control_files)
1525
return MetaDirRepository(_format=self,
1527
control_files=control_files,
1528
_revision_store=_revision_store,
1529
control_store=control_store,
1530
text_store=text_store)
1533
class RepositoryFormatKnit(MetaDirRepositoryFormat):
1534
"""Bzr repository knit format (generalized).
1536
This repository format has:
1537
- knits for file texts and inventory
1538
- hash subdirectory based stores.
1539
- knits for revisions and signatures
1540
- TextStores for revisions and signatures.
1541
- a format marker of its own
1542
- an optional 'shared-storage' flag
1543
- an optional 'no-working-trees' flag
1547
def _get_control_store(self, repo_transport, control_files):
1548
"""Return the control store for this repository."""
1549
return VersionedFileStore(
1552
file_mode=control_files._file_mode,
1553
versionedfile_class=KnitVersionedFile,
1554
versionedfile_kwargs={'factory':KnitPlainFactory()},
1557
def _get_revision_store(self, repo_transport, control_files):
1558
"""See RepositoryFormat._get_revision_store()."""
1559
from bzrlib.store.revision.knit import KnitRevisionStore
1560
versioned_file_store = VersionedFileStore(
1562
file_mode=control_files._file_mode,
1565
versionedfile_class=KnitVersionedFile,
1566
versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory(),},
1569
return KnitRevisionStore(versioned_file_store)
1571
def _get_text_store(self, transport, control_files):
1572
"""See RepositoryFormat._get_text_store()."""
1573
return self._get_versioned_file_store('knits',
1576
versionedfile_class=KnitVersionedFile,
1577
versionedfile_kwargs={
1578
'create_parent_dir':True,
1579
'delay_create':True,
1580
'dir_mode':control_files._dir_mode,
1584
def initialize(self, a_bzrdir, shared=False):
1585
"""Create a knit format 1 repository.
1587
:param a_bzrdir: bzrdir to contain the new repository; must already
1589
:param shared: If true the repository will be initialized as a shared
1592
mutter('creating repository in %s.', a_bzrdir.transport.base)
1593
dirs = ['revision-store', 'knits']
1595
utf8_files = [('format', self.get_format_string())]
1597
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1598
repo_transport = a_bzrdir.get_repository_transport(None)
1599
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1600
control_store = self._get_control_store(repo_transport, control_files)
1601
transaction = transactions.WriteTransaction()
1602
# trigger a write of the inventory store.
1603
control_store.get_weave_or_empty('inventory', transaction)
1604
_revision_store = self._get_revision_store(repo_transport, control_files)
1605
_revision_store.has_revision_id('A', transaction)
1606
_revision_store.get_signature_file(transaction)
1607
return self.open(a_bzrdir=a_bzrdir, _found=True)
1609
def open(self, a_bzrdir, _found=False, _override_transport=None):
1610
"""See RepositoryFormat.open().
1612
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1613
repository at a slightly different url
1614
than normal. I.e. during 'upgrade'.
1617
format = RepositoryFormat.find_format(a_bzrdir)
1618
assert format.__class__ == self.__class__
1619
if _override_transport is not None:
1620
repo_transport = _override_transport
1622
repo_transport = a_bzrdir.get_repository_transport(None)
1623
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1624
text_store = self._get_text_store(repo_transport, control_files)
1625
control_store = self._get_control_store(repo_transport, control_files)
1626
_revision_store = self._get_revision_store(repo_transport, control_files)
1627
return KnitRepository(_format=self,
1629
control_files=control_files,
1630
_revision_store=_revision_store,
1631
control_store=control_store,
1632
text_store=text_store)
1635
class RepositoryFormatKnit1(RepositoryFormatKnit):
1636
"""Bzr repository knit format 1.
1638
This repository format has:
1639
- knits for file texts and inventory
1640
- hash subdirectory based stores.
1641
- knits for revisions and signatures
1642
- TextStores for revisions and signatures.
1643
- a format marker of its own
1644
- an optional 'shared-storage' flag
1645
- an optional 'no-working-trees' flag
1648
This format was introduced in bzr 0.8.
1650
def get_format_string(self):
1651
"""See RepositoryFormat.get_format_string()."""
1652
return "Bazaar-NG Knit Repository Format 1"
1654
def get_format_description(self):
1655
"""See RepositoryFormat.get_format_description()."""
1656
return "Knit repository format 1"
1658
def check_conversion_target(self, target_format):
1662
class RepositoryFormatKnit2(RepositoryFormatKnit):
1663
"""Bzr repository knit format 2.
1665
THIS FORMAT IS EXPERIMENTAL
1666
This repository format has:
1667
- knits for file texts and inventory
1668
- hash subdirectory based stores.
1669
- knits for revisions and signatures
1670
- TextStores for revisions and signatures.
1671
- a format marker of its own
1672
- an optional 'shared-storage' flag
1673
- an optional 'no-working-trees' flag
1675
- Support for recording full info about the tree root
1679
rich_root_data = True
1681
def get_format_string(self):
1682
"""See RepositoryFormat.get_format_string()."""
1683
return "Bazaar Knit Repository Format 2\n"
1685
def get_format_description(self):
1686
"""See RepositoryFormat.get_format_description()."""
1687
return "Knit repository format 2"
1689
def check_conversion_target(self, target_format):
1690
if not target_format.rich_root_data:
1691
raise errors.BadConversionTarget(
1692
'Does not support rich root data.', target_format)
1694
def open(self, a_bzrdir, _found=False, _override_transport=None):
1695
"""See RepositoryFormat.open().
1697
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1698
repository at a slightly different url
1699
than normal. I.e. during 'upgrade'.
1702
format = RepositoryFormat.find_format(a_bzrdir)
1703
assert format.__class__ == self.__class__
1704
if _override_transport is not None:
1705
repo_transport = _override_transport
1707
repo_transport = a_bzrdir.get_repository_transport(None)
1708
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1709
text_store = self._get_text_store(repo_transport, control_files)
1710
control_store = self._get_control_store(repo_transport, control_files)
1711
_revision_store = self._get_revision_store(repo_transport, control_files)
1712
return KnitRepository2(_format=self,
1714
control_files=control_files,
1715
_revision_store=_revision_store,
1716
control_store=control_store,
1717
text_store=text_store)
1721
1324
# formats which have no format string are not discoverable
1722
# and not independently creatable, so are not registered.
1723
RepositoryFormat.register_format(RepositoryFormat7())
1724
_default_format = RepositoryFormatKnit1()
1725
RepositoryFormat.register_format(_default_format)
1726
RepositoryFormat.register_format(RepositoryFormatKnit2())
1727
RepositoryFormat.set_default_format(_default_format)
1728
_legacy_formats = [RepositoryFormat4(),
1729
RepositoryFormat5(),
1730
RepositoryFormat6()]
1325
# and not independently creatable, so are not registered. They're
1326
# all in bzrlib.repofmt.weaverepo now. When an instance of one of these is
1327
# needed, it's constructed directly by the BzrDir. Non-native formats where
1328
# the repository is not separately opened are similar.
1330
format_registry.register_lazy(
1331
'Bazaar-NG Repository format 7',
1332
'bzrlib.repofmt.weaverepo',
1335
# KEEP in sync with bzrdir.format_registry default, which controls the overall
1336
# default control directory format
1338
format_registry.register_lazy(
1339
'Bazaar-NG Knit Repository Format 1',
1340
'bzrlib.repofmt.knitrepo',
1341
'RepositoryFormatKnit1',
1343
format_registry.default_key = 'Bazaar-NG Knit Repository Format 1'
1345
format_registry.register_lazy(
1346
'Bazaar Knit Repository Format 3 (bzr 0.15)\n',
1347
'bzrlib.repofmt.knitrepo',
1348
'RepositoryFormatKnit3',
1733
1352
class InterRepository(InterObject):
1872
1504
@needs_write_lock
1873
def copy_content(self, revision_id=None, basis=None):
1505
def copy_content(self, revision_id=None):
1874
1506
"""See InterRepository.copy_content()."""
1875
1507
# weave specific optimised path:
1876
if basis is not None:
1877
# copy the basis in, then fetch remaining data.
1878
basis.copy_content_into(self.target, revision_id)
1879
# the basis copy_content_into could miss-set this.
1508
# TODO: jam 20070210 Internal, should be an assert, not translate
1509
revision_id = osutils.safe_revision_id(revision_id)
1511
self.target.set_make_working_trees(self.source.make_working_trees())
1512
except NotImplementedError:
1514
# FIXME do not peek!
1515
if self.source.control_files._transport.listable():
1516
pb = ui.ui_factory.nested_progress_bar()
1881
self.target.set_make_working_trees(self.source.make_working_trees())
1882
except NotImplementedError:
1518
self.target.weave_store.copy_all_ids(
1519
self.source.weave_store,
1521
from_transaction=self.source.get_transaction(),
1522
to_transaction=self.target.get_transaction())
1523
pb.update('copying inventory', 0, 1)
1524
self.target.control_weaves.copy_multi(
1525
self.source.control_weaves, ['inventory'],
1526
from_transaction=self.source.get_transaction(),
1527
to_transaction=self.target.get_transaction())
1528
self.target._revision_store.text_store.copy_all_ids(
1529
self.source._revision_store.text_store,
1884
1534
self.target.fetch(self.source, revision_id=revision_id)
1887
self.target.set_make_working_trees(self.source.make_working_trees())
1888
except NotImplementedError:
1890
# FIXME do not peek!
1891
if self.source.control_files._transport.listable():
1892
pb = ui.ui_factory.nested_progress_bar()
1894
self.target.weave_store.copy_all_ids(
1895
self.source.weave_store,
1897
from_transaction=self.source.get_transaction(),
1898
to_transaction=self.target.get_transaction())
1899
pb.update('copying inventory', 0, 1)
1900
self.target.control_weaves.copy_multi(
1901
self.source.control_weaves, ['inventory'],
1902
from_transaction=self.source.get_transaction(),
1903
to_transaction=self.target.get_transaction())
1904
self.target._revision_store.text_store.copy_all_ids(
1905
self.source._revision_store.text_store,
1910
self.target.fetch(self.source, revision_id=revision_id)
1912
1536
@needs_write_lock
1913
1537
def fetch(self, revision_id=None, pb=None):
2103
1739
return f.count_copied, f.failed_revisions
1742
class InterRemoteRepository(InterRepository):
1743
"""Code for converting between RemoteRepository objects.
1745
This just gets an non-remote repository from the RemoteRepository, and calls
1746
InterRepository.get again.
1749
def __init__(self, source, target):
1750
if isinstance(source, remote.RemoteRepository):
1751
source._ensure_real()
1752
real_source = source._real_repository
1754
real_source = source
1755
if isinstance(target, remote.RemoteRepository):
1756
target._ensure_real()
1757
real_target = target._real_repository
1759
real_target = target
1760
self.real_inter = InterRepository.get(real_source, real_target)
1763
def is_compatible(source, target):
1764
if isinstance(source, remote.RemoteRepository):
1766
if isinstance(target, remote.RemoteRepository):
1770
def copy_content(self, revision_id=None):
1771
self.real_inter.copy_content(revision_id=revision_id)
1773
def fetch(self, revision_id=None, pb=None):
1774
self.real_inter.fetch(revision_id=revision_id, pb=pb)
1777
def _get_repo_format_to_test(self):
2106
1781
InterRepository.register_optimiser(InterSameDataRepository)
2107
1782
InterRepository.register_optimiser(InterWeaveRepo)
2108
1783
InterRepository.register_optimiser(InterKnitRepo)
2109
1784
InterRepository.register_optimiser(InterModel1and2)
2110
1785
InterRepository.register_optimiser(InterKnit1and2)
2113
class RepositoryTestProviderAdapter(object):
2114
"""A tool to generate a suite testing multiple repository formats at once.
2116
This is done by copying the test once for each transport and injecting
2117
the transport_server, transport_readonly_server, and bzrdir_format and
2118
repository_format classes into each copy. Each copy is also given a new id()
2119
to make it easy to identify.
2122
def __init__(self, transport_server, transport_readonly_server, formats):
2123
self._transport_server = transport_server
2124
self._transport_readonly_server = transport_readonly_server
2125
self._formats = formats
2127
def adapt(self, test):
2128
result = TestSuite()
2129
for repository_format, bzrdir_format in self._formats:
2130
new_test = deepcopy(test)
2131
new_test.transport_server = self._transport_server
2132
new_test.transport_readonly_server = self._transport_readonly_server
2133
new_test.bzrdir_format = bzrdir_format
2134
new_test.repository_format = repository_format
2135
def make_new_test_id():
2136
new_id = "%s(%s)" % (new_test.id(), repository_format.__class__.__name__)
2137
return lambda: new_id
2138
new_test.id = make_new_test_id()
2139
result.addTest(new_test)
2143
class InterRepositoryTestProviderAdapter(object):
2144
"""A tool to generate a suite testing multiple inter repository formats.
2146
This is done by copying the test once for each interrepo provider and injecting
2147
the transport_server, transport_readonly_server, repository_format and
2148
repository_to_format classes into each copy.
2149
Each copy is also given a new id() to make it easy to identify.
2152
def __init__(self, transport_server, transport_readonly_server, formats):
2153
self._transport_server = transport_server
2154
self._transport_readonly_server = transport_readonly_server
2155
self._formats = formats
2157
def adapt(self, test):
2158
result = TestSuite()
2159
for interrepo_class, repository_format, repository_format_to in self._formats:
2160
new_test = deepcopy(test)
2161
new_test.transport_server = self._transport_server
2162
new_test.transport_readonly_server = self._transport_readonly_server
2163
new_test.interrepo_class = interrepo_class
2164
new_test.repository_format = repository_format
2165
new_test.repository_format_to = repository_format_to
2166
def make_new_test_id():
2167
new_id = "%s(%s)" % (new_test.id(), interrepo_class.__name__)
2168
return lambda: new_id
2169
new_test.id = make_new_test_id()
2170
result.addTest(new_test)
2174
def default_test_list():
2175
"""Generate the default list of interrepo permutations to test."""
2177
# test the default InterRepository between format 6 and the current
2179
# XXX: robertc 20060220 reinstate this when there are two supported
2180
# formats which do not have an optimal code path between them.
2181
#result.append((InterRepository,
2182
# RepositoryFormat6(),
2183
# RepositoryFormatKnit1()))
2184
for optimiser in InterRepository._optimisers:
2185
if optimiser._matching_repo_format is not None:
2186
result.append((optimiser,
2187
optimiser._matching_repo_format,
2188
optimiser._matching_repo_format
2190
# if there are specific combinations we want to use, we can add them
2192
result.append((InterModel1and2, RepositoryFormat5(),
2193
RepositoryFormatKnit2()))
2194
result.append((InterKnit1and2, RepositoryFormatKnit1(),
2195
RepositoryFormatKnit2()))
1786
InterRepository.register_optimiser(InterRemoteRepository)
2199
1789
class CopyConverter(object):