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
17
19
from cStringIO import StringIO
19
from bzrlib.lazy_import import lazy_import
20
lazy_import(globals(), """
22
from unittest import TestSuite
25
24
from bzrlib import (
37
revision as _mod_revision,
36
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
from bzrlib.errors import InvalidRevisionId
38
from bzrlib.graph import Graph
39
from bzrlib.inter import InterObject
40
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
42
47
from bzrlib.revisiontree import RevisionTree
43
from bzrlib.store.versioned import VersionedFileStore
48
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
44
49
from bzrlib.store.text import TextStore
50
from bzrlib import symbol_versioning
51
from bzrlib.symbol_versioning import (deprecated_method,
45
54
from bzrlib.testament import Testament
49
from bzrlib.decorators import needs_read_lock, needs_write_lock
50
from bzrlib.inter import InterObject
51
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
52
from bzrlib.symbol_versioning import (
56
55
from bzrlib.trace import mutter, note, warning
56
from bzrlib.tsort import topo_sort
57
from bzrlib.weave import WeaveFile
59
60
# Old formats display a warning, but only once
60
61
_deprecation_warning_done = False
63
######################################################################
66
64
class Repository(object):
67
65
"""Repository holding history for one or more branches.
78
_file_ids_altered_regex = lazy_regex.lazy_compile(
79
r'file_id="(?P<file_id>[^"]+)"'
80
r'.*revision="(?P<revision_id>[^"]+)"'
84
def add_inventory(self, revision_id, inv, parents):
85
"""Add the inventory inv to the repository as revision_id.
77
def add_inventory(self, revid, inv, parents):
78
"""Add the inventory inv to the repository as revid.
87
:param parents: The revision ids of the parents that revision_id
80
:param parents: The revision ids of the parents that revid
88
81
is known to have and are in the repository already.
90
83
returns the sha1 of the serialized inventory.
92
revision_id = osutils.safe_revision_id(revision_id)
93
_mod_revision.check_not_reserved_id(revision_id)
94
assert inv.revision_id is None or inv.revision_id == revision_id, \
85
assert inv.revision_id is None or inv.revision_id == revid, \
95
86
"Mismatch between inventory revision" \
96
" id and insertion revid (%r, %r)" % (inv.revision_id, revision_id)
87
" id and insertion revid (%r, %r)" % (inv.revision_id, revid)
97
88
assert inv.root is not None
98
89
inv_text = self.serialise_inventory(inv)
99
90
inv_sha1 = osutils.sha_string(inv_text)
100
91
inv_vf = self.control_weaves.get_weave('inventory',
101
92
self.get_transaction())
102
self._inventory_add_lines(inv_vf, revision_id, parents,
103
osutils.split_lines(inv_text))
93
self._inventory_add_lines(inv_vf, revid, parents, osutils.split_lines(inv_text))
106
def _inventory_add_lines(self, inv_vf, revision_id, parents, lines):
96
def _inventory_add_lines(self, inv_vf, revid, parents, lines):
107
97
final_parents = []
108
98
for parent in parents:
109
99
if parent in inv_vf:
110
100
final_parents.append(parent)
112
inv_vf.add_lines(revision_id, final_parents, lines)
102
inv_vf.add_lines(revid, final_parents, lines)
114
104
@needs_write_lock
115
def add_revision(self, revision_id, rev, inv=None, config=None):
116
"""Add rev to the revision store as revision_id.
105
def add_revision(self, rev_id, rev, inv=None, config=None):
106
"""Add rev to the revision store as rev_id.
118
:param revision_id: the revision id to use.
108
:param rev_id: the revision id to use.
119
109
:param rev: The revision object.
120
110
:param inv: The inventory for the revision. if None, it will be looked
121
111
up in the inventory storer
256
224
def get_physical_lock_status(self):
257
225
return self.control_files.get_physical_lock_status()
259
def leave_lock_in_place(self):
260
"""Tell this repository not to release the physical lock when this
263
If lock_write doesn't return a token, then this method is not supported.
265
self.control_files.leave_in_place()
267
def dont_leave_lock_in_place(self):
268
"""Tell this repository to release the physical lock when this
269
object is unlocked, even if it didn't originally acquire it.
271
If lock_write doesn't return a token, then this method is not supported.
273
self.control_files.dont_leave_in_place()
276
def gather_stats(self, revid=None, committers=None):
277
"""Gather statistics from a revision id.
279
:param revid: The revision id to gather statistics from, if None, then
280
no revision specific statistics are gathered.
281
:param committers: Optional parameter controlling whether to grab
282
a count of committers from the revision specific statistics.
283
:return: A dictionary of statistics. Currently this contains:
284
committers: The number of committers if requested.
285
firstrev: A tuple with timestamp, timezone for the penultimate left
286
most ancestor of revid, if revid is not the NULL_REVISION.
287
latestrev: A tuple with timestamp, timezone for revid, if revid is
288
not the NULL_REVISION.
289
revisions: The total revision count in the repository.
290
size: An estimate disk size of the repository in bytes.
293
if revid and committers:
294
result['committers'] = 0
295
if revid and revid != _mod_revision.NULL_REVISION:
297
all_committers = set()
298
revisions = self.get_ancestry(revid)
299
# pop the leading None
301
first_revision = None
303
# ignore the revisions in the middle - just grab first and last
304
revisions = revisions[0], revisions[-1]
305
for revision in self.get_revisions(revisions):
306
if not first_revision:
307
first_revision = revision
309
all_committers.add(revision.committer)
310
last_revision = revision
312
result['committers'] = len(all_committers)
313
result['firstrev'] = (first_revision.timestamp,
314
first_revision.timezone)
315
result['latestrev'] = (last_revision.timestamp,
316
last_revision.timezone)
318
# now gather global repository information
319
if self.bzrdir.root_transport.listable():
320
c, t = self._revision_store.total_size(self.get_transaction())
321
result['revisions'] = c
326
228
def missing_revision_ids(self, other, revision_id=None):
327
229
"""Return the revision ids that other has that this does not.
548
426
# revisions. We don't need to see all lines in the inventory because
549
427
# only those added in an inventory in rev X can contain a revision=X
551
unescape_revid_cache = {}
552
unescape_fileid_cache = {}
554
# jam 20061218 In a big fetch, this handles hundreds of thousands
555
# of lines, so it has had a lot of inlining and optimizing done.
556
# Sorry that it is a little bit messy.
557
# Move several functions to be local variables, since this is a long
559
search = self._file_ids_altered_regex.search
560
unescape = _unescape_xml
561
setdefault = result.setdefault
562
pb = ui.ui_factory.nested_progress_bar()
564
for line in w.iter_lines_added_or_present_in_versions(
565
selected_revision_ids, pb=pb):
569
# One call to match.group() returning multiple items is quite a
570
# bit faster than 2 calls to match.group() each returning 1
571
file_id, revision_id = match.group('file_id', 'revision_id')
573
# Inlining the cache lookups helps a lot when you make 170,000
574
# lines and 350k ids, versus 8.4 unique ids.
575
# Using a cache helps in 2 ways:
576
# 1) Avoids unnecessary decoding calls
577
# 2) Re-uses cached strings, which helps in future set and
579
# (2) is enough that removing encoding entirely along with
580
# the cache (so we are using plain strings) results in no
581
# performance improvement.
583
revision_id = unescape_revid_cache[revision_id]
585
unescaped = unescape(revision_id)
586
unescape_revid_cache[revision_id] = unescaped
587
revision_id = unescaped
589
if revision_id in selected_revision_ids:
591
file_id = unescape_fileid_cache[file_id]
593
unescaped = unescape(file_id)
594
unescape_fileid_cache[file_id] = unescaped
596
setdefault(file_id, set()).add(revision_id)
429
for line in w.iter_lines_added_or_present_in_versions(selected_revision_ids):
430
start = line.find('file_id="')+9
431
if start < 9: continue
432
end = line.find('"', start)
434
file_id = _unescape_xml(line[start:end])
436
start = line.find('revision="')+10
437
if start < 10: continue
438
end = line.find('"', start)
440
revision_id = _unescape_xml(line[start:end])
441
if revision_id in selected_revision_ids:
442
result.setdefault(file_id, set()).add(revision_id)
909
709
warning("Format %s for %s is deprecated - please use 'bzr upgrade' to get better performance"
910
710
% (self._format, self.bzrdir.transport.base))
912
def supports_rich_root(self):
913
return self._format.rich_root_data
915
def _check_ascii_revisionid(self, revision_id, method):
916
"""Private helper for ascii-only repositories."""
917
# weave repositories refuse to store revisionids that are non-ascii.
918
if revision_id is not None:
919
# weaves require ascii revision ids.
920
if isinstance(revision_id, unicode):
922
revision_id.encode('ascii')
923
except UnicodeEncodeError:
924
raise errors.NonAsciiRevisionId(method, self)
927
revision_id.decode('ascii')
928
except UnicodeDecodeError:
929
raise errors.NonAsciiRevisionId(method, self)
933
# remove these delegates a while after bzr 0.15
934
def __make_delegated(name, from_module):
935
def _deprecated_repository_forwarder():
936
symbol_versioning.warn('%s moved to %s in bzr 0.15'
937
% (name, from_module),
940
m = __import__(from_module, globals(), locals(), [name])
942
return getattr(m, name)
943
except AttributeError:
944
raise AttributeError('module %s has no name %s'
946
globals()[name] = _deprecated_repository_forwarder
949
'AllInOneRepository',
950
'WeaveMetaDirRepository',
951
'PreSplitOutRepositoryFormat',
957
__make_delegated(_name, 'bzrlib.repofmt.weaverepo')
961
'RepositoryFormatKnit',
962
'RepositoryFormatKnit1',
964
__make_delegated(_name, 'bzrlib.repofmt.knitrepo')
713
class AllInOneRepository(Repository):
714
"""Legacy support - the repository behaviour for all-in-one branches."""
716
def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
717
# we reuse one control files instance.
718
dir_mode = a_bzrdir._control_files._dir_mode
719
file_mode = a_bzrdir._control_files._file_mode
721
def get_store(name, compressed=True, prefixed=False):
722
# FIXME: This approach of assuming stores are all entirely compressed
723
# or entirely uncompressed is tidy, but breaks upgrade from
724
# some existing branches where there's a mixture; we probably
725
# still want the option to look for both.
726
relpath = a_bzrdir._control_files._escape(name)
727
store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
728
prefixed=prefixed, compressed=compressed,
731
#if self._transport.should_cache():
732
# cache_path = os.path.join(self.cache_root, name)
733
# os.mkdir(cache_path)
734
# store = bzrlib.store.CachedStore(store, cache_path)
737
# not broken out yet because the controlweaves|inventory_store
738
# and text_store | weave_store bits are still different.
739
if isinstance(_format, RepositoryFormat4):
740
# cannot remove these - there is still no consistent api
741
# which allows access to this old info.
742
self.inventory_store = get_store('inventory-store')
743
text_store = get_store('text-store')
744
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
748
"""AllInOne repositories cannot be shared."""
752
def set_make_working_trees(self, new_value):
753
"""Set the policy flag for making working trees when creating branches.
755
This only applies to branches that use this repository.
757
The default is 'True'.
758
:param new_value: True to restore the default, False to disable making
761
raise NotImplementedError(self.set_make_working_trees)
763
def make_working_trees(self):
764
"""Returns the policy for making working trees on new branches."""
967
768
def install_revision(repository, rev, revision_tree):
1053
852
return not self.control_files._transport.has('no-working-trees')
1056
class RepositoryFormatRegistry(registry.Registry):
1057
"""Registry of RepositoryFormats.
1060
def get(self, format_string):
1061
r = registry.Registry.get(self, format_string)
855
class KnitRepository(MetaDirRepository):
856
"""Knit format repository."""
858
def _warn_if_deprecated(self):
859
# This class isn't deprecated
862
def _inventory_add_lines(self, inv_vf, revid, parents, lines):
863
inv_vf.add_lines_with_ghosts(revid, parents, lines)
866
def _all_revision_ids(self):
867
"""See Repository.all_revision_ids()."""
868
# Knits get the revision graph from the index of the revision knit, so
869
# it's always possible even if they're on an unlistable transport.
870
return self._revision_store.all_revision_ids(self.get_transaction())
872
def fileid_involved_between_revs(self, from_revid, to_revid):
873
"""Find file_id(s) which are involved in the changes between revisions.
875
This determines the set of revisions which are involved, and then
876
finds all file ids affected by those revisions.
878
vf = self._get_revision_vf()
879
from_set = set(vf.get_ancestry(from_revid))
880
to_set = set(vf.get_ancestry(to_revid))
881
changed = to_set.difference(from_set)
882
return self._fileid_involved_by_set(changed)
884
def fileid_involved(self, last_revid=None):
885
"""Find all file_ids modified in the ancestry of last_revid.
887
:param last_revid: If None, last_revision() will be used.
890
changed = set(self.all_revision_ids())
892
changed = set(self.get_ancestry(last_revid))
895
return self._fileid_involved_by_set(changed)
898
def get_ancestry(self, revision_id):
899
"""Return a list of revision-ids integrated by a revision.
901
This is topologically sorted.
903
if revision_id is None:
905
vf = self._get_revision_vf()
907
return [None] + vf.get_ancestry(revision_id)
908
except errors.RevisionNotPresent:
909
raise errors.NoSuchRevision(self, revision_id)
912
def get_revision(self, revision_id):
913
"""Return the Revision object for a named revision"""
914
return self.get_revision_reconcile(revision_id)
917
def get_revision_graph(self, revision_id=None):
918
"""Return a dictionary containing the revision graph.
920
:param revision_id: The revision_id to get a graph from. If None, then
921
the entire revision graph is returned. This is a deprecated mode of
922
operation and will be removed in the future.
923
:return: a dictionary of revision_id->revision_parents_list.
925
# special case NULL_REVISION
926
if revision_id == NULL_REVISION:
928
weave = self._get_revision_vf()
929
entire_graph = weave.get_graph()
930
if revision_id is None:
931
return weave.get_graph()
932
elif revision_id not in weave:
933
raise errors.NoSuchRevision(self, revision_id)
935
# add what can be reached from revision_id
937
pending = set([revision_id])
938
while len(pending) > 0:
940
result[node] = weave.get_parents(node)
941
for revision_id in result[node]:
942
if revision_id not in result:
943
pending.add(revision_id)
947
def get_revision_graph_with_ghosts(self, revision_ids=None):
948
"""Return a graph of the revisions with ghosts marked as applicable.
950
:param revision_ids: an iterable of revisions to graph or None for all.
951
:return: a Graph object with the graph reachable from revision_ids.
954
vf = self._get_revision_vf()
955
versions = set(vf.versions())
957
pending = set(self.all_revision_ids())
960
pending = set(revision_ids)
961
# special case NULL_REVISION
962
if NULL_REVISION in pending:
963
pending.remove(NULL_REVISION)
964
required = set(pending)
967
revision_id = pending.pop()
968
if not revision_id in versions:
969
if revision_id in required:
970
raise errors.NoSuchRevision(self, revision_id)
972
result.add_ghost(revision_id)
973
# mark it as done so we don't try for it again.
974
done.add(revision_id)
976
parent_ids = vf.get_parents_with_ghosts(revision_id)
977
for parent_id in parent_ids:
978
# is this queued or done ?
979
if (parent_id not in pending and
980
parent_id not in done):
982
pending.add(parent_id)
983
result.add_node(revision_id, parent_ids)
984
done.add(revision_id)
987
def _get_revision_vf(self):
988
""":return: a versioned file containing the revisions."""
989
vf = self._revision_store.get_revision_file(self.get_transaction())
993
def reconcile(self, other=None, thorough=False):
994
"""Reconcile this repository."""
995
from bzrlib.reconcile import KnitReconciler
996
reconciler = KnitReconciler(self, thorough=thorough)
997
reconciler.reconcile()
1067
format_registry = RepositoryFormatRegistry()
1068
"""Registry of formats, indexed by their identifying format string.
1070
This can contain either format instances themselves, or classes/factories that
1071
can be called to obtain one.
1075
#####################################################################
1076
# Repository Formats
1000
def revision_parents(self, revision_id):
1001
return self._get_revision_vf().get_parents(revision_id)
1004
class KnitRepository2(KnitRepository):
1006
def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1007
control_store, text_store):
1008
KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1009
_revision_store, control_store, text_store)
1010
self._serializer = xml6.serializer_v6
1012
def deserialise_inventory(self, revision_id, xml):
1013
"""Transform the xml into an inventory object.
1015
:param revision_id: The expected revision id of the inventory.
1016
:param xml: A serialised inventory.
1018
result = self._serializer.read_inventory_from_string(xml)
1019
assert result.root.revision is not None
1022
def serialise_inventory(self, inv):
1023
"""Transform the inventory object into XML text.
1025
:param revision_id: The expected revision id of the inventory.
1026
:param xml: A serialised inventory.
1028
assert inv.revision_id is not None
1029
assert inv.root.revision is not None
1030
return KnitRepository.serialise_inventory(self, inv)
1032
def get_commit_builder(self, branch, parents, config, timestamp=None,
1033
timezone=None, committer=None, revprops=None,
1035
"""Obtain a CommitBuilder for this repository.
1037
:param branch: Branch to commit to.
1038
:param parents: Revision ids of the parents of the new revision.
1039
:param config: Configuration to use.
1040
:param timestamp: Optional timestamp recorded for commit.
1041
:param timezone: Optional timezone for timestamp.
1042
:param committer: Optional committer to set for commit.
1043
:param revprops: Optional dictionary of revision properties.
1044
:param revision_id: Optional revision id.
1046
return RootCommitBuilder(self, parents, config, timestamp, timezone,
1047
committer, revprops, revision_id)
1078
1050
class RepositoryFormat(object):
1079
1051
"""A repository format.
1235
1188
raise NotImplementedError(self.open)
1191
def register_format(klass, format):
1192
klass._formats[format.get_format_string()] = format
1195
def set_default_format(klass, format):
1196
klass._default_format = format
1199
def unregister_format(klass, format):
1200
assert klass._formats[format.get_format_string()] is format
1201
del klass._formats[format.get_format_string()]
1204
class PreSplitOutRepositoryFormat(RepositoryFormat):
1205
"""Base class for the pre split out repository formats."""
1207
rich_root_data = False
1209
def initialize(self, a_bzrdir, shared=False, _internal=False):
1210
"""Create a weave repository.
1212
TODO: when creating split out bzr branch formats, move this to a common
1213
base for Format5, Format6. or something like that.
1215
from bzrlib.weavefile import write_weave_v5
1216
from bzrlib.weave import Weave
1219
raise errors.IncompatibleFormat(self, a_bzrdir._format)
1222
# always initialized when the bzrdir is.
1223
return self.open(a_bzrdir, _found=True)
1225
# Create an empty weave
1227
write_weave_v5(Weave(), sio)
1228
empty_weave = sio.getvalue()
1230
mutter('creating repository in %s.', a_bzrdir.transport.base)
1231
dirs = ['revision-store', 'weaves']
1232
files = [('inventory.weave', StringIO(empty_weave)),
1235
# FIXME: RBC 20060125 don't peek under the covers
1236
# NB: no need to escape relative paths that are url safe.
1237
control_files = LockableFiles(a_bzrdir.transport, 'branch-lock',
1239
control_files.create_lock()
1240
control_files.lock_write()
1241
control_files._transport.mkdir_multi(dirs,
1242
mode=control_files._dir_mode)
1244
for file, content in files:
1245
control_files.put(file, content)
1247
control_files.unlock()
1248
return self.open(a_bzrdir, _found=True)
1250
def _get_control_store(self, repo_transport, control_files):
1251
"""Return the control store for this repository."""
1252
return self._get_versioned_file_store('',
1257
def _get_text_store(self, transport, control_files):
1258
"""Get a store for file texts for this format."""
1259
raise NotImplementedError(self._get_text_store)
1261
def open(self, a_bzrdir, _found=False):
1262
"""See RepositoryFormat.open()."""
1264
# we are being called directly and must probe.
1265
raise NotImplementedError
1267
repo_transport = a_bzrdir.get_repository_transport(None)
1268
control_files = a_bzrdir._control_files
1269
text_store = self._get_text_store(repo_transport, control_files)
1270
control_store = self._get_control_store(repo_transport, control_files)
1271
_revision_store = self._get_revision_store(repo_transport, control_files)
1272
return AllInOneRepository(_format=self,
1274
_revision_store=_revision_store,
1275
control_store=control_store,
1276
text_store=text_store)
1278
def check_conversion_target(self, target_format):
1282
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1283
"""Bzr repository format 4.
1285
This repository format has:
1287
- TextStores for texts, inventories,revisions.
1289
This format is deprecated: it indexes texts using a text id which is
1290
removed in format 5; initialization and write support for this format
1295
super(RepositoryFormat4, self).__init__()
1296
self._matchingbzrdir = bzrdir.BzrDirFormat4()
1298
def get_format_description(self):
1299
"""See RepositoryFormat.get_format_description()."""
1300
return "Repository format 4"
1302
def initialize(self, url, shared=False, _internal=False):
1303
"""Format 4 branches cannot be created."""
1304
raise errors.UninitializableFormat(self)
1306
def is_supported(self):
1307
"""Format 4 is not supported.
1309
It is not supported because the model changed from 4 to 5 and the
1310
conversion logic is expensive - so doing it on the fly was not
1315
def _get_control_store(self, repo_transport, control_files):
1316
"""Format 4 repositories have no formal control store at this point.
1318
This will cause any control-file-needing apis to fail - this is desired.
1322
def _get_revision_store(self, repo_transport, control_files):
1323
"""See RepositoryFormat._get_revision_store()."""
1324
from bzrlib.xml4 import serializer_v4
1325
return self._get_text_rev_store(repo_transport,
1328
serializer=serializer_v4)
1330
def _get_text_store(self, transport, control_files):
1331
"""See RepositoryFormat._get_text_store()."""
1334
class RepositoryFormat5(PreSplitOutRepositoryFormat):
1335
"""Bzr control format 5.
1337
This repository format has:
1338
- weaves for file texts and inventory
1340
- TextStores for revisions and signatures.
1344
super(RepositoryFormat5, self).__init__()
1345
self._matchingbzrdir = bzrdir.BzrDirFormat5()
1347
def get_format_description(self):
1348
"""See RepositoryFormat.get_format_description()."""
1349
return "Weave repository format 5"
1351
def _get_revision_store(self, repo_transport, control_files):
1352
"""See RepositoryFormat._get_revision_store()."""
1353
"""Return the revision store object for this a_bzrdir."""
1354
return self._get_text_rev_store(repo_transport,
1359
def _get_text_store(self, transport, control_files):
1360
"""See RepositoryFormat._get_text_store()."""
1361
return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
1364
class RepositoryFormat6(PreSplitOutRepositoryFormat):
1365
"""Bzr control format 6.
1367
This repository format has:
1368
- weaves for file texts and inventory
1369
- hash subdirectory based stores.
1370
- TextStores for revisions and signatures.
1374
super(RepositoryFormat6, self).__init__()
1375
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1377
def get_format_description(self):
1378
"""See RepositoryFormat.get_format_description()."""
1379
return "Weave repository format 6"
1381
def _get_revision_store(self, repo_transport, control_files):
1382
"""See RepositoryFormat._get_revision_store()."""
1383
return self._get_text_rev_store(repo_transport,
1389
def _get_text_store(self, transport, control_files):
1390
"""See RepositoryFormat._get_text_store()."""
1391
return self._get_versioned_file_store('weaves', transport, control_files)
1238
1394
class MetaDirRepositoryFormat(RepositoryFormat):
1239
1395
"""Common base class for the new repositories using the metadir layout."""
1241
1397
rich_root_data = False
1242
supports_tree_reference = False
1243
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1245
1399
def __init__(self):
1246
1400
super(MetaDirRepositoryFormat, self).__init__()
1401
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1248
1403
def _create_control_files(self, a_bzrdir):
1249
1404
"""Create the required files and the initial control_files object."""
1250
1405
# FIXME: RBC 20060125 don't peek under the covers
1251
1406
# NB: no need to escape relative paths that are url safe.
1252
1407
repository_transport = a_bzrdir.get_repository_transport(self)
1253
control_files = lockable_files.LockableFiles(repository_transport,
1254
'lock', lockdir.LockDir)
1408
control_files = LockableFiles(repository_transport, 'lock', LockDir)
1255
1409
control_files.create_lock()
1256
1410
return control_files
1272
1426
control_files.unlock()
1429
class RepositoryFormat7(MetaDirRepositoryFormat):
1430
"""Bzr repository 7.
1432
This repository format has:
1433
- weaves for file texts and inventory
1434
- hash subdirectory based stores.
1435
- TextStores for revisions and signatures.
1436
- a format marker of its own
1437
- an optional 'shared-storage' flag
1438
- an optional 'no-working-trees' flag
1441
def _get_control_store(self, repo_transport, control_files):
1442
"""Return the control store for this repository."""
1443
return self._get_versioned_file_store('',
1448
def get_format_string(self):
1449
"""See RepositoryFormat.get_format_string()."""
1450
return "Bazaar-NG Repository format 7"
1452
def get_format_description(self):
1453
"""See RepositoryFormat.get_format_description()."""
1454
return "Weave repository format 7"
1456
def check_conversion_target(self, target_format):
1459
def _get_revision_store(self, repo_transport, control_files):
1460
"""See RepositoryFormat._get_revision_store()."""
1461
return self._get_text_rev_store(repo_transport,
1468
def _get_text_store(self, transport, control_files):
1469
"""See RepositoryFormat._get_text_store()."""
1470
return self._get_versioned_file_store('weaves',
1474
def initialize(self, a_bzrdir, shared=False):
1475
"""Create a weave repository.
1477
:param shared: If true the repository will be initialized as a shared
1480
from bzrlib.weavefile import write_weave_v5
1481
from bzrlib.weave import Weave
1483
# Create an empty weave
1485
write_weave_v5(Weave(), sio)
1486
empty_weave = sio.getvalue()
1488
mutter('creating repository in %s.', a_bzrdir.transport.base)
1489
dirs = ['revision-store', 'weaves']
1490
files = [('inventory.weave', StringIO(empty_weave)),
1492
utf8_files = [('format', self.get_format_string())]
1494
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1495
return self.open(a_bzrdir=a_bzrdir, _found=True)
1497
def open(self, a_bzrdir, _found=False, _override_transport=None):
1498
"""See RepositoryFormat.open().
1500
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1501
repository at a slightly different url
1502
than normal. I.e. during 'upgrade'.
1505
format = RepositoryFormat.find_format(a_bzrdir)
1506
assert format.__class__ == self.__class__
1507
if _override_transport is not None:
1508
repo_transport = _override_transport
1510
repo_transport = a_bzrdir.get_repository_transport(None)
1511
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1512
text_store = self._get_text_store(repo_transport, control_files)
1513
control_store = self._get_control_store(repo_transport, control_files)
1514
_revision_store = self._get_revision_store(repo_transport, control_files)
1515
return MetaDirRepository(_format=self,
1517
control_files=control_files,
1518
_revision_store=_revision_store,
1519
control_store=control_store,
1520
text_store=text_store)
1523
class RepositoryFormatKnit(MetaDirRepositoryFormat):
1524
"""Bzr repository knit format (generalized).
1526
This repository format has:
1527
- knits for file texts and inventory
1528
- hash subdirectory based stores.
1529
- knits for revisions and signatures
1530
- TextStores for revisions and signatures.
1531
- a format marker of its own
1532
- an optional 'shared-storage' flag
1533
- an optional 'no-working-trees' flag
1537
def _get_control_store(self, repo_transport, control_files):
1538
"""Return the control store for this repository."""
1539
return VersionedFileStore(
1542
file_mode=control_files._file_mode,
1543
versionedfile_class=KnitVersionedFile,
1544
versionedfile_kwargs={'factory':KnitPlainFactory()},
1547
def _get_revision_store(self, repo_transport, control_files):
1548
"""See RepositoryFormat._get_revision_store()."""
1549
from bzrlib.store.revision.knit import KnitRevisionStore
1550
versioned_file_store = VersionedFileStore(
1552
file_mode=control_files._file_mode,
1555
versionedfile_class=KnitVersionedFile,
1556
versionedfile_kwargs={'delta':False, 'factory':KnitPlainFactory(),},
1559
return KnitRevisionStore(versioned_file_store)
1561
def _get_text_store(self, transport, control_files):
1562
"""See RepositoryFormat._get_text_store()."""
1563
return self._get_versioned_file_store('knits',
1566
versionedfile_class=KnitVersionedFile,
1567
versionedfile_kwargs={
1568
'create_parent_dir':True,
1569
'delay_create':True,
1570
'dir_mode':control_files._dir_mode,
1574
def initialize(self, a_bzrdir, shared=False):
1575
"""Create a knit format 1 repository.
1577
:param a_bzrdir: bzrdir to contain the new repository; must already
1579
:param shared: If true the repository will be initialized as a shared
1582
mutter('creating repository in %s.', a_bzrdir.transport.base)
1583
dirs = ['revision-store', 'knits']
1585
utf8_files = [('format', self.get_format_string())]
1587
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1588
repo_transport = a_bzrdir.get_repository_transport(None)
1589
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1590
control_store = self._get_control_store(repo_transport, control_files)
1591
transaction = transactions.WriteTransaction()
1592
# trigger a write of the inventory store.
1593
control_store.get_weave_or_empty('inventory', transaction)
1594
_revision_store = self._get_revision_store(repo_transport, control_files)
1595
_revision_store.has_revision_id('A', transaction)
1596
_revision_store.get_signature_file(transaction)
1597
return self.open(a_bzrdir=a_bzrdir, _found=True)
1599
def open(self, a_bzrdir, _found=False, _override_transport=None):
1600
"""See RepositoryFormat.open().
1602
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1603
repository at a slightly different url
1604
than normal. I.e. during 'upgrade'.
1607
format = RepositoryFormat.find_format(a_bzrdir)
1608
assert format.__class__ == self.__class__
1609
if _override_transport is not None:
1610
repo_transport = _override_transport
1612
repo_transport = a_bzrdir.get_repository_transport(None)
1613
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1614
text_store = self._get_text_store(repo_transport, control_files)
1615
control_store = self._get_control_store(repo_transport, control_files)
1616
_revision_store = self._get_revision_store(repo_transport, control_files)
1617
return KnitRepository(_format=self,
1619
control_files=control_files,
1620
_revision_store=_revision_store,
1621
control_store=control_store,
1622
text_store=text_store)
1625
class RepositoryFormatKnit1(RepositoryFormatKnit):
1626
"""Bzr repository knit format 1.
1628
This repository format has:
1629
- knits for file texts and inventory
1630
- hash subdirectory based stores.
1631
- knits for revisions and signatures
1632
- TextStores for revisions and signatures.
1633
- a format marker of its own
1634
- an optional 'shared-storage' flag
1635
- an optional 'no-working-trees' flag
1638
This format was introduced in bzr 0.8.
1640
def get_format_string(self):
1641
"""See RepositoryFormat.get_format_string()."""
1642
return "Bazaar-NG Knit Repository Format 1"
1644
def get_format_description(self):
1645
"""See RepositoryFormat.get_format_description()."""
1646
return "Knit repository format 1"
1648
def check_conversion_target(self, target_format):
1652
class RepositoryFormatKnit2(RepositoryFormatKnit):
1653
"""Bzr repository knit format 2.
1655
THIS FORMAT IS EXPERIMENTAL
1656
This repository format has:
1657
- knits for file texts and inventory
1658
- hash subdirectory based stores.
1659
- knits for revisions and signatures
1660
- TextStores for revisions and signatures.
1661
- a format marker of its own
1662
- an optional 'shared-storage' flag
1663
- an optional 'no-working-trees' flag
1665
- Support for recording full info about the tree root
1669
rich_root_data = True
1671
def get_format_string(self):
1672
"""See RepositoryFormat.get_format_string()."""
1673
return "Bazaar Knit Repository Format 2\n"
1675
def get_format_description(self):
1676
"""See RepositoryFormat.get_format_description()."""
1677
return "Knit repository format 2"
1679
def check_conversion_target(self, target_format):
1680
if not target_format.rich_root_data:
1681
raise errors.BadConversionTarget(
1682
'Does not support rich root data.', target_format)
1684
def open(self, a_bzrdir, _found=False, _override_transport=None):
1685
"""See RepositoryFormat.open().
1687
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1688
repository at a slightly different url
1689
than normal. I.e. during 'upgrade'.
1692
format = RepositoryFormat.find_format(a_bzrdir)
1693
assert format.__class__ == self.__class__
1694
if _override_transport is not None:
1695
repo_transport = _override_transport
1697
repo_transport = a_bzrdir.get_repository_transport(None)
1698
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1699
text_store = self._get_text_store(repo_transport, control_files)
1700
control_store = self._get_control_store(repo_transport, control_files)
1701
_revision_store = self._get_revision_store(repo_transport, control_files)
1702
return KnitRepository2(_format=self,
1704
control_files=control_files,
1705
_revision_store=_revision_store,
1706
control_store=control_store,
1707
text_store=text_store)
1275
1711
# formats which have no format string are not discoverable
1276
# and not independently creatable, so are not registered. They're
1277
# all in bzrlib.repofmt.weaverepo now. When an instance of one of these is
1278
# needed, it's constructed directly by the BzrDir. Non-native formats where
1279
# the repository is not separately opened are similar.
1281
format_registry.register_lazy(
1282
'Bazaar-NG Repository format 7',
1283
'bzrlib.repofmt.weaverepo',
1286
# KEEP in sync with bzrdir.format_registry default, which controls the overall
1287
# default control directory format
1289
format_registry.register_lazy(
1290
'Bazaar-NG Knit Repository Format 1',
1291
'bzrlib.repofmt.knitrepo',
1292
'RepositoryFormatKnit1',
1294
format_registry.default_key = 'Bazaar-NG Knit Repository Format 1'
1296
format_registry.register_lazy(
1297
'Bazaar Knit Repository Format 3 (bzr 0.15)\n',
1298
'bzrlib.repofmt.knitrepo',
1299
'RepositoryFormatKnit3',
1712
# and not independently creatable, so are not registered.
1713
RepositoryFormat.register_format(RepositoryFormat7())
1714
_default_format = RepositoryFormatKnit1()
1715
RepositoryFormat.register_format(_default_format)
1716
RepositoryFormat.register_format(RepositoryFormatKnit2())
1717
RepositoryFormat.set_default_format(_default_format)
1718
_legacy_formats = [RepositoryFormat4(),
1719
RepositoryFormat5(),
1720
RepositoryFormat6()]
1303
1723
class InterRepository(InterObject):
1452
1862
@needs_write_lock
1453
def copy_content(self, revision_id=None):
1863
def copy_content(self, revision_id=None, basis=None):
1454
1864
"""See InterRepository.copy_content()."""
1455
1865
# weave specific optimised path:
1456
# TODO: jam 20070210 Internal, should be an assert, not translate
1457
revision_id = osutils.safe_revision_id(revision_id)
1459
self.target.set_make_working_trees(self.source.make_working_trees())
1460
except NotImplementedError:
1462
# FIXME do not peek!
1463
if self.source.control_files._transport.listable():
1464
pb = ui.ui_factory.nested_progress_bar()
1866
if basis is not None:
1867
# copy the basis in, then fetch remaining data.
1868
basis.copy_content_into(self.target, revision_id)
1869
# the basis copy_content_into could miss-set this.
1466
self.target.weave_store.copy_all_ids(
1467
self.source.weave_store,
1469
from_transaction=self.source.get_transaction(),
1470
to_transaction=self.target.get_transaction())
1471
pb.update('copying inventory', 0, 1)
1472
self.target.control_weaves.copy_multi(
1473
self.source.control_weaves, ['inventory'],
1474
from_transaction=self.source.get_transaction(),
1475
to_transaction=self.target.get_transaction())
1476
self.target._revision_store.text_store.copy_all_ids(
1477
self.source._revision_store.text_store,
1871
self.target.set_make_working_trees(self.source.make_working_trees())
1872
except NotImplementedError:
1482
1874
self.target.fetch(self.source, revision_id=revision_id)
1877
self.target.set_make_working_trees(self.source.make_working_trees())
1878
except NotImplementedError:
1880
# FIXME do not peek!
1881
if self.source.control_files._transport.listable():
1882
pb = ui.ui_factory.nested_progress_bar()
1884
self.target.weave_store.copy_all_ids(
1885
self.source.weave_store,
1887
from_transaction=self.source.get_transaction(),
1888
to_transaction=self.target.get_transaction())
1889
pb.update('copying inventory', 0, 1)
1890
self.target.control_weaves.copy_multi(
1891
self.source.control_weaves, ['inventory'],
1892
from_transaction=self.source.get_transaction(),
1893
to_transaction=self.target.get_transaction())
1894
self.target._revision_store.text_store.copy_all_ids(
1895
self.source._revision_store.text_store,
1900
self.target.fetch(self.source, revision_id=revision_id)
1484
1902
@needs_write_lock
1485
1903
def fetch(self, revision_id=None, pb=None):