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: 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.
26
30
from cStringIO import StringIO
87
92
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
89
a transport connected to the directory this bzr was opened from.
94
a transport connected to the directory this bzr was opened from
95
(i.e. the parent directory holding the .bzr directory).
92
98
def break_lock(self):
149
155
def clone(self, url, revision_id=None, force_new_repo=False):
150
156
"""Clone this bzrdir and its contents to url verbatim.
152
If urls last component does not exist, it will be created.
158
If url's last component does not exist, it will be created.
154
160
if revision_id is not None, then the clone operation may tune
155
161
itself to download less data.
218
224
def create(cls, base, format=None, possible_transports=None):
219
225
"""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
227
:param format: If supplied, the format of branch to create. If not
225
228
supplied, the default is used.
226
229
:param possible_transports: If supplied, a list of transports that
234
237
if format is None:
235
238
format = BzrDirFormat.get_default_format()
236
return format.initialize(base, possible_transports)
239
return format.initialize_on_transport(t)
238
241
def create_branch(self):
239
242
"""Create a branch in this BzrDir.
241
The bzrdirs format will control what branch format is created.
244
The bzrdir's format will control what branch format is created.
242
245
For more control see BranchFormatXX.create(a_bzrdir).
244
247
raise NotImplementedError(self.create_branch)
249
def destroy_branch(self):
250
"""Destroy the branch in this BzrDir"""
251
raise NotImplementedError(self.destroy_branch)
247
254
def create_branch_and_repo(base, force_new_repo=False, format=None):
248
255
"""Create a new BzrDir, Branch and Repository at the url 'base'.
250
This will use the current default BzrDirFormat, and use whatever
257
This will use the current default BzrDirFormat unless one is
258
specified, and use whatever
251
259
repository format that that uses via bzrdir.create_branch and
252
260
create_repository. If a shared repository is available that is used
257
265
:param base: The URL to create the branch at.
258
266
:param force_new_repo: If True a new repository is always created.
267
:param format: If supplied, the format of branch to create. If not
268
supplied, the default is used.
260
270
bzrdir = BzrDir.create(base, format)
261
271
bzrdir._find_or_create_repository(force_new_repo)
280
290
if possible, can be told explicitly whether to create a working tree or
283
This will use the current default BzrDirFormat, and use whatever
293
This will use the current default BzrDirFormat unless one is
294
specified, and use whatever
284
295
repository format that that uses via bzrdir.create_branch and
285
296
create_repository. If a shared repository is available that is used
286
297
preferentially. Whatever repository is used, its tree creation policy
295
306
:param force_new_repo: If True a new repository is always created.
296
307
:param force_new_tree: If True or False force creation of a tree or
297
308
prevent such creation respectively.
298
:param format: Override for the for the bzrdir format to create.
309
:param format: Override for the bzrdir format to create.
299
310
:param possible_transports: An optional reusable transports list.
301
312
if force_new_tree:
344
355
'base' must be a local path or a file:// url.
346
This will use the current default BzrDirFormat, and use whatever
357
This will use the current default BzrDirFormat unless one is
358
specified, and use whatever
347
359
repository format that that uses for bzrdirformat.create_workingtree,
348
360
create_branch and create_repository.
362
:param format: Override for the bzrdir format to create.
350
363
:return: The WorkingTree object.
352
365
t = get_transport(base)
365
378
raise NotImplementedError(self.create_workingtree)
367
def retire_bzrdir(self):
380
def retire_bzrdir(self, limit=10000):
368
381
"""Permanently disable the bzrdir.
370
383
This is done by renaming it to give the user some ability to recover
373
386
This will have horrible consequences if anyone has anything locked or
388
:param limit: number of times to retry
376
for i in xrange(10000):
378
393
to_path = '.bzr.retired.%d' % i
379
394
self.root_transport.rename('.bzr', to_path)
380
395
note("renamed %s to %s"
381
396
% (self.root_transport.abspath('.bzr'), to_path))
383
398
except (errors.TransportError, IOError, errors.PathError):
386
405
def destroy_workingtree(self):
387
406
"""Destroy the working tree at this BzrDir.
399
418
raise NotImplementedError(self.destroy_workingtree_metadata)
401
420
def find_repository(self):
402
"""Find the repository that should be used for a_bzrdir.
421
"""Find the repository that should be used.
404
423
This does not require a branch as we use it to find the repo for
405
424
new branches as well as to hook existing branches up to their
452
471
a format string, and vice versa.
454
473
If branch_format is None, the transport is returned with no
455
checking. if it is not None, then the returned transport is
474
checking. If it is not None, then the returned transport is
456
475
guaranteed to point to an existing directory ready for use.
458
477
raise NotImplementedError(self.get_branch_transport)
465
484
a format string, and vice versa.
467
486
If repository_format is None, the transport is returned with no
468
checking. if it is not None, then the returned transport is
487
checking. If it is not None, then the returned transport is
469
488
guaranteed to point to an existing directory ready for use.
471
490
raise NotImplementedError(self.get_repository_transport)
478
497
format string, and vice versa.
480
499
If workingtree_format is None, the transport is returned with no
481
checking. if it is not None, then the returned transport is
500
checking. If it is not None, then the returned transport is
482
501
guaranteed to point to an existing directory ready for use.
484
503
raise NotImplementedError(self.get_workingtree_transport)
510
529
# this might be better on the BzrDirFormat class because it refers to
511
530
# all the possible bzrdir disk formats.
512
531
# This method is tested via the workingtree is_control_filename tests-
513
# it was extracted from WorkingTree.is_control_filename. If the methods
514
# contract is extended beyond the current trivial implementation please
532
# it was extracted from WorkingTree.is_control_filename. If the method's
533
# contract is extended beyond the current trivial implementation, please
515
534
# add new tests for it to the appropriate place.
516
535
return filename == '.bzr' or filename.startswith('.bzr/')
532
551
return BzrDir.open(base, _unsupported=True)
535
def open(base, _unsupported=False):
536
"""Open an existing bzrdir, rooted at 'base' (url)
554
def open(base, _unsupported=False, possible_transports=None):
555
"""Open an existing bzrdir, rooted at 'base' (url).
538
_unsupported is a private parameter to the BzrDir class.
557
:param _unsupported: a private parameter to the BzrDir class.
540
t = get_transport(base)
559
t = get_transport(base, possible_transports=possible_transports)
541
560
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
564
583
note('%s is%s redirected to %s',
565
584
transport.base, e.permanently, target)
566
585
# Let's try with a new transport
567
qualified_target = e.get_target_url()[:-len(relpath)]
568
586
# FIXME: If 'transport' has a qualifier, this should
569
587
# be applied again to the new transport *iff* the
570
# schemes used are the same. It's a bit tricky to
571
# verify, so I'll punt for now
588
# schemes used are the same. Uncomment this code
589
# once the function (and tests) exist.
572
590
# -- vila20070212
591
#target = urlutils.copy_url_qualifiers(original, target)
573
592
return get_transport(target)
606
625
def open_containing_from_transport(a_transport):
607
"""Open an existing branch which contains a_transport.base
626
"""Open an existing branch which contains a_transport.base.
609
628
This probes for a branch at a_transport, and searches upwards from there.
658
677
def open_repository(self, _unsupported=False):
659
678
"""Open the repository object at this BzrDir if one is present.
661
This will not follow the Branch object pointer - its strictly a direct
680
This will not follow the Branch object pointer - it's strictly a direct
662
681
open facility. Most client code should use open_branch().repository to
663
682
get at a repository.
665
_unsupported is a private parameter, not part of the api.
684
:param _unsupported: a private parameter, not part of the api.
666
685
TODO: static convenience version of this?
668
687
raise NotImplementedError(self.open_repository)
741
760
"""Produce a metadir suitable for cloning or sprouting with.
743
762
These operations may produce workingtrees (yes, even though they're
744
"cloning" something that doesn't have a tree, so a viable workingtree
763
"cloning" something that doesn't have a tree), so a viable workingtree
745
764
format must be selected.
747
766
format, repository = self._cloning_metadir()
760
779
"""Create a copy of this bzrdir prepared for use as a new line of
763
If urls last component does not exist, it will be created.
782
If url's last component does not exist, it will be created.
765
784
Attributes related to the identity of the source branch like
766
785
branch nickname will be cleaned, a working tree is created
893
912
"""See BzrDir.create_branch."""
894
913
return self.open_branch()
915
def destroy_branch(self):
916
"""See BzrDir.destroy_branch."""
917
raise errors.UnsupportedOperation(self.destroy_branch, self)
896
919
def create_repository(self, shared=False):
897
920
"""See BzrDir.create_repository."""
1069
1092
"""See BzrDir.create_branch."""
1070
1093
return self._format.get_branch_format().initialize(self)
1095
def destroy_branch(self):
1096
"""See BzrDir.create_branch."""
1097
self.transport.delete_tree('branch')
1072
1099
def create_repository(self, shared=False):
1073
1100
"""See BzrDir.create_repository."""
1074
1101
return self._format.repository_format.initialize(self, shared)
1083
1110
wt = self.open_workingtree(recommend_upgrade=False)
1084
1111
repository = wt.branch.repository
1085
1112
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1086
wt.revert([], old_tree=empty)
1113
wt.revert(old_tree=empty)
1087
1114
self.destroy_workingtree_metadata()
1089
1116
def destroy_workingtree_metadata(self):
1432
1459
klass._default_format = format
1434
1461
def __str__(self):
1435
return self.get_format_string()[:-1]
1463
return self.get_format_string().rstrip()
1438
1466
def unregister_format(klass, format):
1804
1832
def _convert_working_inv(self):
1805
1833
inv = xml4.serializer_v4.read_inventory(
1806
1834
self.branch.control_files.get('inventory'))
1807
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1835
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1808
1836
# FIXME inventory is a working tree change.
1809
1837
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1889
1917
present_parents = [p for p in rev.parent_ids
1890
1918
if p not in self.absent_revisions]
1891
1919
self._convert_revision_contents(rev, inv, present_parents)
1892
self._store_new_weave(rev, inv, present_parents)
1920
self._store_new_inv(rev, inv, present_parents)
1893
1921
self.converted_revs.add(rev_id)
1895
def _store_new_weave(self, rev, inv, present_parents):
1923
def _store_new_inv(self, rev, inv, present_parents):
1896
1924
# the XML is now updated with text versions
1898
1926
entries = inv.iter_entries()
1903
1931
(file_id, rev.revision_id)
1904
1932
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1905
1933
new_inv_sha1 = sha_string(new_inv_xml)
1906
self.inv_weave.add_lines(rev.revision_id,
1934
self.inv_weave.add_lines(rev.revision_id,
1907
1935
present_parents,
1908
1936
new_inv_xml.splitlines(True))
1909
1937
rev.inventory_sha1 = new_inv_sha1
1934
1962
w = Weave(file_id)
1935
1963
self.text_weaves[file_id] = w
1936
1964
text_changed = False
1937
previous_entries = ie.find_previous_heads(parent_invs,
1941
for old_revision in previous_entries:
1942
# if this fails, its a ghost ?
1943
assert old_revision in self.converted_revs, \
1944
"Revision {%s} not in converted_revs" % old_revision
1965
parent_candiate_entries = ie.parent_candidates(parent_invs)
1966
for old_revision in parent_candiate_entries.keys():
1967
# if this fails, its a ghost ?
1968
assert old_revision in self.converted_revs, \
1969
"Revision {%s} not in converted_revs" % old_revision
1970
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
1971
# XXX: Note that this is unordered - and this is tolerable because
1972
# the previous code was also unordered.
1973
previous_entries = dict((head, parent_candiate_entries[head]) for head
1945
1975
self.snapshot_ie(previous_entries, ie, w, rev_id)
1947
1977
assert getattr(ie, 'revision', None) is not None
1979
def get_parents(self, revision_ids):
1980
for revision_id in revision_ids:
1981
yield self.revisions[revision_id].parent_ids
1949
1983
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1950
1984
# TODO: convert this logic, which is ~= snapshot to
1951
1985
# a call to:. This needs the path figured out. rather than a work_tree