19
19
At format 7 this was split out into Branch, Repository and Checkout control
22
Note: This module has a lot of ``open`` functions/methods that return
23
references to in-memory objects. As a rule, there are no matching ``close``
24
methods. To free any associated resources, simply stop referencing the
23
# TODO: remove unittest dependency; put that stuff inside the test suite
25
# TODO: Can we move specific formats into separate modules to make this file
28
# TODO: Move old formats into a plugin to make this file smaller.
28
30
from cStringIO import StringIO
32
34
from bzrlib.lazy_import import lazy_import
33
35
lazy_import(globals(), """
34
from copy import deepcopy
35
36
from stat import S_ISDIR
38
from warnings import warn
39
41
from bzrlib import (
213
227
t = get_transport(url)
216
# TODO: Should take a Transport
218
def create(cls, base, format=None):
231
def create(cls, base, format=None, possible_transports=None):
219
232
"""Create a new BzrDir at the url 'base'.
221
This will call the current default formats initialize with base
222
as the only parameter.
224
234
:param format: If supplied, the format of branch to create. If not
225
235
supplied, the default is used.
236
:param possible_transports: If supplied, a list of transports that
237
can be reused to share a remote connection.
227
239
if cls is not BzrDir:
228
240
raise AssertionError("BzrDir.create always creates the default"
229
241
" format, not one of %r" % cls)
230
t = get_transport(base)
242
t = get_transport(base, possible_transports)
232
244
if format is None:
233
245
format = BzrDirFormat.get_default_format()
234
return format.initialize(safe_unicode(base))
246
return format.initialize_on_transport(t)
249
def find_bzrdirs(transport, evaluate=None, list_current=None):
250
"""Find bzrdirs recursively from current location.
252
This is intended primarily as a building block for more sophisticated
253
functionality, like finding trees under a directory, or finding
254
branches that use a given repository.
255
:param evaluate: An optional callable that yields recurse, value,
256
where recurse controls whether this bzrdir is recursed into
257
and value is the value to yield. By default, all bzrdirs
258
are recursed into, and the return value is the bzrdir.
259
:param list_current: if supplied, use this function to list the current
260
directory, instead of Transport.list_dir
261
:return: a generator of found bzrdirs, or whatever evaluate returns.
263
if list_current is None:
264
def list_current(transport):
265
return transport.list_dir('')
267
def evaluate(bzrdir):
270
pending = [transport]
271
while len(pending) > 0:
272
current_transport = pending.pop()
275
bzrdir = BzrDir.open_from_transport(current_transport)
276
except errors.NotBranchError:
279
recurse, value = evaluate(bzrdir)
282
subdirs = list_current(current_transport)
283
except errors.NoSuchFile:
286
for subdir in sorted(subdirs, reverse=True):
287
pending.append(current_transport.clone(subdir))
290
def find_branches(transport):
291
"""Find all branches under a transport.
293
This will find all branches below the transport, including branches
294
inside other branches. Where possible, it will use
295
Repository.find_branches.
297
To list all the branches that use a particular Repository, see
298
Repository.find_branches
300
def evaluate(bzrdir):
302
repository = bzrdir.open_repository()
303
except errors.NoRepositoryPresent:
306
return False, (None, repository)
308
branch = bzrdir.open_branch()
309
except errors.NotBranchError:
310
return True, (None, None)
312
return True, (branch, None)
314
for branch, repo in BzrDir.find_bzrdirs(transport, evaluate=evaluate):
316
branches.extend(repo.find_branches())
317
if branch is not None:
318
branches.append(branch)
322
def destroy_repository(self):
323
"""Destroy the repository in this BzrDir"""
324
raise NotImplementedError(self.destroy_repository)
236
326
def create_branch(self):
237
327
"""Create a branch in this BzrDir.
239
The bzrdirs format will control what branch format is created.
329
The bzrdir's format will control what branch format is created.
240
330
For more control see BranchFormatXX.create(a_bzrdir).
242
332
raise NotImplementedError(self.create_branch)
334
def destroy_branch(self):
335
"""Destroy the branch in this BzrDir"""
336
raise NotImplementedError(self.destroy_branch)
245
339
def create_branch_and_repo(base, force_new_repo=False, format=None):
246
340
"""Create a new BzrDir, Branch and Repository at the url 'base'.
248
This will use the current default BzrDirFormat, and use whatever
342
This will use the current default BzrDirFormat unless one is
343
specified, and use whatever
249
344
repository format that that uses via bzrdir.create_branch and
250
345
create_repository. If a shared repository is available that is used
272
369
def create_branch_convenience(base, force_new_repo=False,
273
force_new_tree=None, format=None):
370
force_new_tree=None, format=None,
371
possible_transports=None):
274
372
"""Create a new BzrDir, Branch and Repository at the url 'base'.
276
374
This is a convenience function - it will use an existing repository
277
375
if possible, can be told explicitly whether to create a working tree or
280
This will use the current default BzrDirFormat, and use whatever
378
This will use the current default BzrDirFormat unless one is
379
specified, and use whatever
281
380
repository format that that uses via bzrdir.create_branch and
282
381
create_repository. If a shared repository is available that is used
283
382
preferentially. Whatever repository is used, its tree creation policy
292
391
:param force_new_repo: If True a new repository is always created.
293
392
:param force_new_tree: If True or False force creation of a tree or
294
393
prevent such creation respectively.
295
:param format: Override for the for the bzrdir format to create
394
:param format: Override for the bzrdir format to create.
395
:param possible_transports: An optional reusable transports list.
297
397
if force_new_tree:
298
398
# check for non local urls
299
t = get_transport(safe_unicode(base))
399
t = get_transport(base, possible_transports)
300
400
if not isinstance(t, LocalTransport):
301
401
raise errors.NotLocalUrl(base)
302
bzrdir = BzrDir.create(base, format)
402
bzrdir = BzrDir.create(base, format, possible_transports)
303
403
repo = bzrdir._find_or_create_repository(force_new_repo)
304
404
result = bzrdir.create_branch()
305
if force_new_tree or (repo.make_working_trees() and
405
if force_new_tree or (repo.make_working_trees() and
306
406
force_new_tree is None):
308
408
bzrdir.create_workingtree()
309
409
except errors.NotLocalUrl:
414
@deprecated_function(zero_ninetyone)
314
415
def create_repository(base, shared=False, format=None):
315
416
"""Create a new BzrDir and Repository at the url 'base'.
336
440
'base' must be a local path or a file:// url.
338
This will use the current default BzrDirFormat, and use whatever
442
This will use the current default BzrDirFormat unless one is
443
specified, and use whatever
339
444
repository format that that uses for bzrdirformat.create_workingtree,
340
445
create_branch and create_repository.
447
:param format: Override for the bzrdir format to create.
342
448
:return: The WorkingTree object.
344
t = get_transport(safe_unicode(base))
450
t = get_transport(base)
345
451
if not isinstance(t, LocalTransport):
346
452
raise errors.NotLocalUrl(base)
347
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
453
bzrdir = BzrDir.create_branch_and_repo(base,
348
454
force_new_repo=True,
349
455
format=format).bzrdir
350
456
return bzrdir.create_workingtree()
352
def create_workingtree(self, revision_id=None):
458
def create_workingtree(self, revision_id=None, from_branch=None,
459
accelerator_tree=None):
353
460
"""Create a working tree at this BzrDir.
355
revision_id: create it as of this revision id.
462
:param revision_id: create it as of this revision id.
463
:param from_branch: override bzrdir branch (for lightweight checkouts)
464
:param accelerator_tree: A tree which can be used for retrieving file
465
contents more quickly than the revision tree, i.e. a workingtree.
466
The revision tree will be used for cases where accelerator_tree's
467
content is different.
357
469
raise NotImplementedError(self.create_workingtree)
359
def retire_bzrdir(self):
471
def retire_bzrdir(self, limit=10000):
360
472
"""Permanently disable the bzrdir.
362
474
This is done by renaming it to give the user some ability to recover
556
674
note('%s is%s redirected to %s',
557
675
transport.base, e.permanently, target)
558
676
# Let's try with a new transport
559
qualified_target = e.get_target_url()[:-len(relpath)]
560
677
# FIXME: If 'transport' has a qualifier, this should
561
678
# be applied again to the new transport *iff* the
562
# schemes used are the same. It's a bit tricky to
563
# verify, so I'll punt for now
679
# schemes used are the same. Uncomment this code
680
# once the function (and tests) exist.
564
681
# -- vila20070212
682
#target = urlutils.copy_url_qualifiers(original, target)
565
683
return get_transport(target)
585
703
raise NotImplementedError(self.open_branch)
588
def open_containing(url):
706
def open_containing(url, possible_transports=None):
589
707
"""Open an existing branch which contains url.
591
709
:param url: url to search from.
592
710
See open_containing_from_transport for more detail.
594
return BzrDir.open_containing_from_transport(get_transport(url))
712
transport = get_transport(url, possible_transports)
713
return BzrDir.open_containing_from_transport(transport)
597
716
def open_containing_from_transport(a_transport):
598
"""Open an existing branch which contains a_transport.base
717
"""Open an existing branch which contains a_transport.base.
600
719
This probes for a branch at a_transport, and searches upwards from there.
626
745
raise errors.NotBranchError(path=url)
627
746
a_transport = new_t
748
def _get_tree_branch(self):
749
"""Return the branch and tree, if any, for this bzrdir.
751
Return None for tree if not present.
752
Raise NotBranchError if no branch is present.
753
:return: (tree, branch)
756
tree = self.open_workingtree()
757
except (errors.NoWorkingTree, errors.NotLocalUrl):
759
branch = self.open_branch()
765
def open_tree_or_branch(klass, location):
766
"""Return the branch and working tree at a location.
768
If there is no tree at the location, tree will be None.
769
If there is no branch at the location, an exception will be
771
:return: (tree, branch)
773
bzrdir = klass.open(location)
774
return bzrdir._get_tree_branch()
630
777
def open_containing_tree_or_branch(klass, location):
631
778
"""Return the branch and working tree contained by a location.
637
784
relpath is the portion of the path that is contained by the branch.
639
786
bzrdir, relpath = klass.open_containing(location)
641
tree = bzrdir.open_workingtree()
642
except (errors.NoWorkingTree, errors.NotLocalUrl):
644
branch = bzrdir.open_branch()
787
tree, branch = bzrdir._get_tree_branch()
647
788
return tree, branch, relpath
649
790
def open_repository(self, _unsupported=False):
650
791
"""Open the repository object at this BzrDir if one is present.
652
This will not follow the Branch object pointer - its strictly a direct
793
This will not follow the Branch object pointer - it's strictly a direct
653
794
open facility. Most client code should use open_branch().repository to
654
795
get at a repository.
656
_unsupported is a private parameter, not part of the api.
797
:param _unsupported: a private parameter, not part of the api.
657
798
TODO: static convenience version of this?
659
800
raise NotImplementedError(self.open_repository)
661
802
def open_workingtree(self, _unsupported=False,
662
recommend_upgrade=True):
803
recommend_upgrade=True, from_branch=None):
663
804
"""Open the workingtree object at this BzrDir if one is present.
665
806
:param recommend_upgrade: Optional keyword parameter, when True (the
666
807
default), emit through the ui module a recommendation that the user
667
808
upgrade the working tree when the workingtree being opened is old
668
809
(but still fully supported).
810
:param from_branch: override bzrdir branch (for lightweight checkouts)
670
812
raise NotImplementedError(self.open_workingtree)
788
935
result.create_repository()
789
936
elif source_repository is not None and result_repo is None:
790
937
# have source, and want to make a new target repo
791
result_repo = source_repository.sprout(result, revision_id=revision_id)
938
result_repo = source_repository.sprout(result,
939
revision_id=revision_id)
793
941
# fetch needed content into target.
794
942
if source_repository is not None:
795
943
# would rather do
796
# source_repository.copy_content_into(result_repo, revision_id=revision_id)
944
# source_repository.copy_content_into(result_repo,
945
# revision_id=revision_id)
797
946
# so we can override the copy method
798
947
result_repo.fetch(source_repository, revision_id=revision_id)
799
948
if source_branch is not None:
800
949
source_branch.sprout(result, revision_id=revision_id)
802
951
result.create_branch()
803
# TODO: jam 20060426 we probably need a test in here in the
804
# case that the newly sprouted branch is a remote one
805
if result_repo is None or result_repo.make_working_trees():
806
wt = result.create_workingtree()
952
if isinstance(target_transport, LocalTransport) and (
953
result_repo is None or result_repo.make_working_trees()):
954
wt = result.create_workingtree(accelerator_tree=accelerator_tree)
809
957
if wt.path2id('') is None:
883
1031
"""See BzrDir.create_branch."""
884
1032
return self.open_branch()
1034
def destroy_branch(self):
1035
"""See BzrDir.destroy_branch."""
1036
raise errors.UnsupportedOperation(self.destroy_branch, self)
886
1038
def create_repository(self, shared=False):
887
1039
"""See BzrDir.create_repository."""
889
1041
raise errors.IncompatibleFormat('shared repository', self._format)
890
1042
return self.open_repository()
892
def create_workingtree(self, revision_id=None):
1044
def destroy_repository(self):
1045
"""See BzrDir.destroy_repository."""
1046
raise errors.UnsupportedOperation(self.destroy_repository, self)
1048
def create_workingtree(self, revision_id=None, from_branch=None,
1049
accelerator_tree=None):
893
1050
"""See BzrDir.create_workingtree."""
894
1051
# this looks buggy but is not -really-
895
1052
# because this format creates the workingtree when the bzrdir is
1058
1217
"""See BzrDir.create_branch."""
1059
1218
return self._format.get_branch_format().initialize(self)
1220
def destroy_branch(self):
1221
"""See BzrDir.create_branch."""
1222
self.transport.delete_tree('branch')
1061
1224
def create_repository(self, shared=False):
1062
1225
"""See BzrDir.create_repository."""
1063
1226
return self._format.repository_format.initialize(self, shared)
1065
def create_workingtree(self, revision_id=None):
1228
def destroy_repository(self):
1229
"""See BzrDir.destroy_repository."""
1230
self.transport.delete_tree('repository')
1232
def create_workingtree(self, revision_id=None, from_branch=None,
1233
accelerator_tree=None):
1066
1234
"""See BzrDir.create_workingtree."""
1067
from bzrlib.workingtree import WorkingTreeFormat
1068
return self._format.workingtree_format.initialize(self, revision_id)
1235
return self._format.workingtree_format.initialize(
1236
self, revision_id, from_branch=from_branch,
1237
accelerator_tree=accelerator_tree)
1070
1239
def destroy_workingtree(self):
1071
1240
"""See BzrDir.destroy_workingtree."""
1072
1241
wt = self.open_workingtree(recommend_upgrade=False)
1073
1242
repository = wt.branch.repository
1074
1243
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1075
wt.revert([], old_tree=empty)
1244
wt.revert(old_tree=empty)
1076
1245
self.destroy_workingtree_metadata()
1078
1247
def destroy_workingtree_metadata(self):
1700
1873
BzrDirFormat._default_format = __default_format
1703
class BzrDirTestProviderAdapter(object):
1704
"""A tool to generate a suite testing multiple bzrdir formats at once.
1706
This is done by copying the test once for each transport and injecting
1707
the transport_server, transport_readonly_server, and bzrdir_format
1708
classes into each copy. Each copy is also given a new id() to make it
1712
def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1714
"""Create an object to adapt tests.
1716
:param vfs_server: A factory to create a Transport Server which has
1717
all the VFS methods working, and is writable.
1719
self._vfs_factory = vfs_factory
1720
self._transport_server = transport_server
1721
self._transport_readonly_server = transport_readonly_server
1722
self._formats = formats
1724
def adapt(self, test):
1725
result = unittest.TestSuite()
1726
for format in self._formats:
1727
new_test = deepcopy(test)
1728
new_test.vfs_transport_factory = self._vfs_factory
1729
new_test.transport_server = self._transport_server
1730
new_test.transport_readonly_server = self._transport_readonly_server
1731
new_test.bzrdir_format = format
1732
def make_new_test_id():
1733
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1734
return lambda: new_id
1735
new_test.id = make_new_test_id()
1736
result.addTest(new_test)
1740
1876
class Converter(object):
1741
1877
"""Converts a disk format object from one format to another."""
1914
2050
present_parents = [p for p in rev.parent_ids
1915
2051
if p not in self.absent_revisions]
1916
2052
self._convert_revision_contents(rev, inv, present_parents)
1917
self._store_new_weave(rev, inv, present_parents)
2053
self._store_new_inv(rev, inv, present_parents)
1918
2054
self.converted_revs.add(rev_id)
1920
def _store_new_weave(self, rev, inv, present_parents):
2056
def _store_new_inv(self, rev, inv, present_parents):
1921
2057
# the XML is now updated with text versions
1923
2059
entries = inv.iter_entries()
1959
2095
w = Weave(file_id)
1960
2096
self.text_weaves[file_id] = w
1961
2097
text_changed = False
1962
previous_entries = ie.find_previous_heads(parent_invs,
1966
for old_revision in previous_entries:
1967
# if this fails, its a ghost ?
1968
assert old_revision in self.converted_revs, \
1969
"Revision {%s} not in converted_revs" % old_revision
2098
parent_candiate_entries = ie.parent_candidates(parent_invs)
2099
for old_revision in parent_candiate_entries.keys():
2100
# if this fails, its a ghost ?
2101
assert old_revision in self.converted_revs, \
2102
"Revision {%s} not in converted_revs" % old_revision
2103
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
2104
# XXX: Note that this is unordered - and this is tolerable because
2105
# the previous code was also unordered.
2106
previous_entries = dict((head, parent_candiate_entries[head]) for head
1970
2108
self.snapshot_ie(previous_entries, ie, w, rev_id)
1972
2110
assert getattr(ie, 'revision', None) is not None
2112
@symbol_versioning.deprecated_method(symbol_versioning.one_one)
2113
def get_parents(self, revision_ids):
2114
for revision_id in revision_ids:
2115
yield self.revisions[revision_id].parent_ids
2117
def get_parent_map(self, revision_ids):
2118
"""See graph._StackedParentsProvider.get_parent_map"""
2119
return dict((revision_id, self.revisions[revision_id])
2120
for revision_id in revision_ids
2121
if revision_id in self.revisions)
1974
2123
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1975
2124
# TODO: convert this logic, which is ~= snapshot to
1976
2125
# a call to:. This needs the path figured out. rather than a work_tree
2258
2407
def initialize_on_transport(self, transport):
2260
2409
# hand off the request to the smart server
2261
medium = transport.get_smart_medium()
2410
shared_medium = transport.get_shared_medium()
2262
2411
except errors.NoSmartMedium:
2263
2412
# TODO: lookup the local format from a server hint.
2264
2413
local_dir_format = BzrDirMetaFormat1()
2265
2414
return local_dir_format.initialize_on_transport(transport)
2266
client = _SmartClient(medium)
2415
client = _SmartClient(shared_medium)
2267
2416
path = client.remote_path_from_transport(transport)
2268
response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
2417
response = _SmartClient(shared_medium).call('BzrDirFormat.initialize',
2269
2419
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2270
2420
return remote.RemoteBzrDir(transport)
2338
2490
if repository_format is not None:
2339
2491
bd.repository_format = _load(repository_format)
2341
self.register(key, helper, help, native, deprecated, hidden)
2493
self.register(key, helper, help, native, deprecated, hidden,
2343
2496
def register(self, key, factory, help, native=True, deprecated=False,
2497
hidden=False, experimental=False):
2345
2498
"""Register a BzrDirFormat factory.
2347
2500
The factory must be a callable that takes one parameter: the key.
2351
2504
supplied directly.
2353
2506
registry.Registry.register(self, key, factory, help,
2354
BzrDirFormatInfo(native, deprecated, hidden))
2507
BzrDirFormatInfo(native, deprecated, hidden, experimental))
2356
2509
def register_lazy(self, key, module_name, member_name, help, native=True,
2357
deprecated=False, hidden=False):
2510
deprecated=False, hidden=False, experimental=False):
2358
2511
registry.Registry.register_lazy(self, key, module_name, member_name,
2359
help, BzrDirFormatInfo(native, deprecated, hidden))
2512
help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
2361
2514
def set_default(self, key):
2362
2515
"""Set the 'default' key to be a clone of the supplied key.
2404
2555
def wrapped(key, help, info):
2405
2556
if info.native:
2406
2557
help = '(native) ' + help
2407
return ' %s:\n%s\n\n' % (key,
2558
return ':%s:\n%s\n\n' % (key,
2408
2559
textwrap.fill(help, initial_indent=' ',
2409
2560
subsequent_indent=' '))
2410
output += wrapped('%s/default' % default_realkey, default_help,
2411
self.get_info('default'))
2561
if default_realkey is not None:
2562
output += wrapped(default_realkey, '(default) %s' % default_help,
2563
self.get_info('default'))
2412
2564
deprecated_pairs = []
2565
experimental_pairs = []
2413
2566
for key, help in help_pairs:
2414
2567
info = self.get_info(key)
2415
2568
if info.hidden:
2417
2570
elif info.deprecated:
2418
2571
deprecated_pairs.append((key, help))
2572
elif info.experimental:
2573
experimental_pairs.append((key, help))
2420
2575
output += wrapped(key, help, info)
2576
if len(experimental_pairs) > 0:
2577
output += "Experimental formats are shown below.\n\n"
2578
for key, help in experimental_pairs:
2579
info = self.get_info(key)
2580
output += wrapped(key, help, info)
2421
2581
if len(deprecated_pairs) > 0:
2422
output += "Deprecated formats\n------------------\n\n"
2582
output += "Deprecated formats are shown below.\n\n"
2423
2583
for key, help in deprecated_pairs:
2424
2584
info = self.get_info(key)
2425
2585
output += wrapped(key, help, info)
2469
2637
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2472
format_registry.set_default('dirstate')
2640
format_registry.register_metadir('pack-0.92',
2641
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
2642
help='New in 0.92: Pack-based format with data compatible with '
2643
'dirstate-tags format repositories. Interoperates with '
2644
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2645
'Previously called knitpack-experimental. '
2646
'For more information, see '
2647
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2648
branch_format='bzrlib.branch.BzrBranchFormat6',
2649
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2652
format_registry.register_metadir('pack-0.92-subtree',
2653
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
2654
help='New in 0.92: Pack-based format with data compatible with '
2655
'dirstate-with-subtree format repositories. Interoperates with '
2656
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2657
'Previously called knitpack-experimental. '
2658
'For more information, see '
2659
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2660
branch_format='bzrlib.branch.BzrBranchFormat6',
2661
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2665
format_registry.register_metadir('rich-root-pack',
2666
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack4',
2667
help='New in 1.0: Pack-based format with data compatible with '
2668
'rich-root format repositories. Incompatible with'
2670
branch_format='bzrlib.branch.BzrBranchFormat6',
2671
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2675
format_registry.set_default('pack-0.92')