1
# Copyright (C) 2005 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
19
At format 7 this was split out into Branch, Repository and Checkout control
23
from copy import deepcopy
24
from cStringIO import StringIO
25
from unittest import TestSuite
29
import bzrlib.errors as errors
30
from bzrlib.lockable_files import LockableFiles
31
from bzrlib.osutils import safe_unicode
32
from bzrlib.trace import mutter
33
from bzrlib.symbol_versioning import *
34
from bzrlib.transport import get_transport
35
from bzrlib.transport.local import LocalTransport
39
"""A .bzr control diretory.
41
BzrDir instances let you create or open any of the things that can be
42
found within .bzr - checkouts, branches and repositories.
45
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
47
a transport connected to the directory this bzr was opened from.
50
def _check_supported(self, format, allow_unsupported):
51
"""Check whether format is a supported format.
53
If allow_unsupported is True, this is a no-op.
55
if not allow_unsupported and not format.is_supported():
56
raise errors.UnsupportedFormatError(format)
58
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
59
"""Clone this bzrdir and its contents to url verbatim.
61
If urls last component does not exist, it will be created.
63
if revision_id is not None, then the clone operation may tune
64
itself to download less data.
65
:param force_new_repo: Do not use a shared repository for the target
66
even if one is available.
69
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
70
result = self._format.initialize(url)
72
local_repo = self.find_repository()
73
except errors.NoRepositoryPresent:
76
# may need to copy content in
78
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
81
result_repo = result.find_repository()
82
# fetch content this dir needs.
84
# XXX FIXME RBC 20060214 need tests for this when the basis
86
result_repo.fetch(basis_repo, revision_id=revision_id)
87
result_repo.fetch(local_repo, revision_id=revision_id)
88
except errors.NoRepositoryPresent:
89
# needed to make one anyway.
90
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
91
# 1 if there is a branch present
92
# make sure its content is available in the target repository
95
self.open_branch().clone(result, revision_id=revision_id)
96
except errors.NotBranchError:
99
self.open_workingtree().clone(result, basis=basis_tree)
100
except (errors.NoWorkingTree, errors.NotLocalUrl):
104
def _get_basis_components(self, basis):
105
"""Retrieve the basis components that are available at basis."""
107
return None, None, None
109
basis_tree = basis.open_workingtree()
110
basis_branch = basis_tree.branch
111
basis_repo = basis_branch.repository
112
except (errors.NoWorkingTree, errors.NotLocalUrl):
115
basis_branch = basis.open_branch()
116
basis_repo = basis_branch.repository
117
except errors.NotBranchError:
120
basis_repo = basis.open_repository()
121
except errors.NoRepositoryPresent:
123
return basis_repo, basis_branch, basis_tree
125
def _make_tail(self, url):
126
segments = url.split('/')
127
if segments and segments[-1] not in ('', '.'):
128
parent = '/'.join(segments[:-1])
129
t = bzrlib.transport.get_transport(parent)
131
t.mkdir(segments[-1])
132
except errors.FileExists:
137
"""Create a new BzrDir at the url 'base'.
139
This will call the current default formats initialize with base
140
as the only parameter.
142
If you need a specific format, consider creating an instance
143
of that and calling initialize().
145
segments = base.split('/')
146
if segments and segments[-1] not in ('', '.'):
147
parent = '/'.join(segments[:-1])
148
t = bzrlib.transport.get_transport(parent)
150
t.mkdir(segments[-1])
151
except errors.FileExists:
153
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
155
def create_branch(self):
156
"""Create a branch in this BzrDir.
158
The bzrdirs format will control what branch format is created.
159
For more control see BranchFormatXX.create(a_bzrdir).
161
raise NotImplementedError(self.create_branch)
164
def create_branch_and_repo(base, force_new_repo=False):
165
"""Create a new BzrDir, Branch and Repository at the url 'base'.
167
This will use the current default BzrDirFormat, and use whatever
168
repository format that that uses via bzrdir.create_branch and
169
create_repository. If a shared repository is available that is used
172
The created Branch object is returned.
174
:param base: The URL to create the branch at.
175
:param force_new_repo: If True a new repository is always created.
177
bzrdir = BzrDir.create(base)
179
bzrdir.create_repository()
181
repo = bzrdir.find_repository()
182
except errors.NoRepositoryPresent:
183
bzrdir.create_repository()
184
return bzrdir.create_branch()
187
def create_repository(base, shared=False):
188
"""Create a new BzrDir and Repository at the url 'base'.
190
This will use the current default BzrDirFormat, and use whatever
191
repository format that that uses for bzrdirformat.create_repository.
193
;param shared: Create a shared repository rather than a standalone
195
The Repository object is returned.
197
This must be overridden as an instance method in child classes, where
198
it should take no parameters and construct whatever repository format
199
that child class desires.
201
bzrdir = BzrDir.create(base)
202
return bzrdir.create_repository()
205
def create_standalone_workingtree(base):
206
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
208
'base' must be a local path or a file:// url.
210
This will use the current default BzrDirFormat, and use whatever
211
repository format that that uses for bzrdirformat.create_workingtree,
212
create_branch and create_repository.
214
The WorkingTree object is returned.
216
t = get_transport(safe_unicode(base))
217
if not isinstance(t, LocalTransport):
218
raise errors.NotLocalUrl(base)
219
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base)).bzrdir
220
return bzrdir.create_workingtree()
222
def create_workingtree(self, revision_id=None):
223
"""Create a working tree at this BzrDir.
225
revision_id: create it as of this revision id.
227
raise NotImplementedError(self.create_workingtree)
229
def find_repository(self):
230
"""Find the repository that should be used for a_bzrdir.
232
This does not require a branch as we use it to find the repo for
233
new branches as well as to hook existing branches up to their
237
return self.open_repository()
238
except errors.NoRepositoryPresent:
240
next_transport = self.root_transport.clone('..')
243
found_bzrdir = BzrDir.open_containing_transport(
245
except errors.NotBranchError:
246
raise errors.NoRepositoryPresent(self)
248
repository = found_bzrdir.open_repository()
249
except errors.NoRepositoryPresent:
250
next_transport = found_bzrdir.root_transport.clone('..')
252
if ((found_bzrdir.root_transport.base ==
253
self.root_transport.base) or repository.is_shared()):
256
raise errors.NoRepositoryPresent(self)
257
raise errors.NoRepositoryPresent(self)
259
def get_branch_transport(self, branch_format):
260
"""Get the transport for use by branch format in this BzrDir.
262
Note that bzr dirs that do not support format strings will raise
263
IncompatibleFormat if the branch format they are given has
264
a format string, and vice verca.
266
If branch_format is None, the transport is returned with no
267
checking. if it is not None, then the returned transport is
268
guaranteed to point to an existing directory ready for use.
270
raise NotImplementedError(self.get_branch_transport)
272
def get_repository_transport(self, repository_format):
273
"""Get the transport for use by repository format in this BzrDir.
275
Note that bzr dirs that do not support format strings will raise
276
IncompatibleFormat if the repository format they are given has
277
a format string, and vice verca.
279
If repository_format is None, the transport is returned with no
280
checking. if it is not None, then the returned transport is
281
guaranteed to point to an existing directory ready for use.
283
raise NotImplementedError(self.get_repository_transport)
285
def get_workingtree_transport(self, tree_format):
286
"""Get the transport for use by workingtree format in this BzrDir.
288
Note that bzr dirs that do not support format strings will raise
289
IncompatibleFormat if the workingtree format they are given has
290
a format string, and vice verca.
292
If workingtree_format is None, the transport is returned with no
293
checking. if it is not None, then the returned transport is
294
guaranteed to point to an existing directory ready for use.
296
raise NotImplementedError(self.get_workingtree_transport)
298
def __init__(self, _transport, _format):
299
"""Initialize a Bzr control dir object.
301
Only really common logic should reside here, concrete classes should be
302
made with varying behaviours.
304
:param _format: the format that is creating this BzrDir instance.
305
:param _transport: the transport this dir is based at.
307
self._format = _format
308
self.transport = _transport.clone('.bzr')
309
self.root_transport = _transport
312
def open_unsupported(base):
313
"""Open a branch which is not supported."""
314
return BzrDir.open(base, _unsupported=True)
317
def open(base, _unsupported=False):
318
"""Open an existing bzrdir, rooted at 'base' (url)
320
_unsupported is a private parameter to the BzrDir class.
322
t = get_transport(base)
323
mutter("trying to open %r with transport %r", base, t)
324
format = BzrDirFormat.find_format(t)
325
if not _unsupported and not format.is_supported():
326
# see open_downlevel to open legacy branches.
327
raise errors.UnsupportedFormatError(
328
'sorry, format %s not supported' % format,
329
['use a different bzr version',
330
'or remove the .bzr directory'
331
' and "bzr init" again'])
332
return format.open(t, _found=True)
334
def open_branch(self, unsupported=False):
335
"""Open the branch object at this BzrDir if one is present.
337
If unsupported is True, then no longer supported branch formats can
340
TODO: static convenience version of this?
342
raise NotImplementedError(self.open_branch)
345
def open_containing(url):
346
"""Open an existing branch which contains url.
348
:param url: url to search from.
349
See open_containing_transport for more detail.
351
return BzrDir.open_containing_transport(get_transport(url))
354
def open_containing_transport(a_transport):
355
"""Open an existing branch which contains a_transport.base
357
This probes for a branch at a_transport, and searches upwards from there.
359
Basically we keep looking up until we find the control directory or
360
run into the root. If there isn't one, raises NotBranchError.
361
If there is one and it is either an unrecognised format or an unsupported
362
format, UnknownFormatError or UnsupportedFormatError are raised.
363
If there is one, it is returned, along with the unused portion of url.
365
# this gets the normalised url back. I.e. '.' -> the full path.
366
url = a_transport.base
369
format = BzrDirFormat.find_format(a_transport)
370
return format.open(a_transport), a_transport.relpath(url)
371
except errors.NotBranchError, e:
372
mutter('not a branch in: %r %s', a_transport.base, e)
373
new_t = a_transport.clone('..')
374
if new_t.base == a_transport.base:
375
# reached the root, whatever that may be
376
raise errors.NotBranchError(path=url)
379
def open_repository(self, _unsupported=False):
380
"""Open the repository object at this BzrDir if one is present.
382
This will not follow the Branch object pointer - its strictly a direct
383
open facility. Most client code should use open_branch().repository to
386
_unsupported is a private parameter, not part of the api.
387
TODO: static convenience version of this?
389
raise NotImplementedError(self.open_repository)
391
def open_workingtree(self, _unsupported=False):
392
"""Open the workingtree object at this BzrDir if one is present.
394
TODO: static convenience version of this?
396
raise NotImplementedError(self.open_workingtree)
398
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
399
"""Create a copy of this bzrdir prepared for use as a new line of
402
If urls last component does not exist, it will be created.
404
Attributes related to the identity of the source branch like
405
branch nickname will be cleaned, a working tree is created
406
whether one existed before or not; and a local branch is always
409
if revision_id is not None, then the clone operation may tune
410
itself to download less data.
413
result = self._format.initialize(url)
414
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
416
source_branch = self.open_branch()
417
source_repository = source_branch.repository
418
except errors.NotBranchError:
421
source_repository = self.open_repository()
422
except errors.NoRepositoryPresent:
423
# copy the entire basis one if there is one
424
# but there is no repository.
425
source_repository = basis_repo
430
result_repo = result.find_repository()
431
except errors.NoRepositoryPresent:
433
if source_repository is None and result_repo is not None:
435
elif source_repository is None and result_repo is None:
436
# no repo available, make a new one
437
result.create_repository()
438
elif source_repository is not None and result_repo is None:
439
# have soure, and want to make a new target repo
440
source_repository.clone(result,
441
revision_id=revision_id,
444
# fetch needed content into target.
446
# XXX FIXME RBC 20060214 need tests for this when the basis
448
result_repo.fetch(basis_repo, revision_id=revision_id)
449
result_repo.fetch(source_repository, revision_id=revision_id)
450
if source_branch is not None:
451
source_branch.sprout(result, revision_id=revision_id)
453
result.create_branch()
455
self.open_workingtree().clone(result,
456
revision_id=revision_id,
458
except (errors.NoWorkingTree, errors.NotLocalUrl):
459
result.create_workingtree()
463
class BzrDirPreSplitOut(BzrDir):
464
"""A common class for the all-in-one formats."""
466
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
467
"""See BzrDir.clone()."""
468
from bzrlib.workingtree import WorkingTreeFormat2
470
result = self._format.initialize(url, _cloning=True)
471
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
472
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
473
self.open_branch().clone(result, revision_id=revision_id)
475
self.open_workingtree().clone(result, basis=basis_tree)
476
except errors.NotLocalUrl:
477
# make a new one, this format always has to have one.
478
WorkingTreeFormat2().initialize(result)
481
def create_branch(self):
482
"""See BzrDir.create_branch."""
483
return self.open_branch()
485
def create_repository(self, shared=False):
486
"""See BzrDir.create_repository."""
488
raise errors.IncompatibleFormat('shared repository', self._format)
489
return self.open_repository()
491
def create_workingtree(self, revision_id=None):
492
"""See BzrDir.create_workingtree."""
493
# this looks buggy but is not -really-
494
# clone and sprout will have set the revision_id
495
# and that will have set it for us, its only
496
# specific uses of create_workingtree in isolation
497
# that can do wonky stuff here, and that only
498
# happens for creating checkouts, which cannot be
499
# done on this format anyway. So - acceptable wart.
500
result = self.open_workingtree()
501
if revision_id is not None:
502
result.set_last_revision(revision_id)
505
def get_branch_transport(self, branch_format):
506
"""See BzrDir.get_branch_transport()."""
507
if branch_format is None:
508
return self.transport
510
branch_format.get_format_string()
511
except NotImplementedError:
512
return self.transport
513
raise errors.IncompatibleFormat(branch_format, self._format)
515
def get_repository_transport(self, repository_format):
516
"""See BzrDir.get_repository_transport()."""
517
if repository_format is None:
518
return self.transport
520
repository_format.get_format_string()
521
except NotImplementedError:
522
return self.transport
523
raise errors.IncompatibleFormat(repository_format, self._format)
525
def get_workingtree_transport(self, workingtree_format):
526
"""See BzrDir.get_workingtree_transport()."""
527
if workingtree_format is None:
528
return self.transport
530
workingtree_format.get_format_string()
531
except NotImplementedError:
532
return self.transport
533
raise errors.IncompatibleFormat(workingtree_format, self._format)
535
def open_branch(self, unsupported=False):
536
"""See BzrDir.open_branch."""
537
from bzrlib.branch import BzrBranchFormat4
538
format = BzrBranchFormat4()
539
self._check_supported(format, unsupported)
540
return format.open(self, _found=True)
542
def sprout(self, url, revision_id=None, basis=None):
543
"""See BzrDir.sprout()."""
544
from bzrlib.workingtree import WorkingTreeFormat2
546
result = self._format.initialize(url, _cloning=True)
547
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
549
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
550
except errors.NoRepositoryPresent:
553
self.open_branch().sprout(result, revision_id=revision_id)
554
except errors.NotBranchError:
557
self.open_workingtree().clone(result, basis=basis_tree)
558
except (errors.NotBranchError, errors.NotLocalUrl):
559
# we always want a working tree
560
WorkingTreeFormat2().initialize(result)
564
class BzrDir4(BzrDirPreSplitOut):
565
"""A .bzr version 4 control object.
567
This is a deprecated format and may be removed after sept 2006.
570
def create_repository(self, shared=False):
571
"""See BzrDir.create_repository."""
572
from bzrlib.repository import RepositoryFormat4
573
return RepositoryFormat4().initialize(self, shared)
575
def open_repository(self):
576
"""See BzrDir.open_repository."""
577
from bzrlib.repository import RepositoryFormat4
578
return RepositoryFormat4().open(self, _found=True)
581
class BzrDir5(BzrDirPreSplitOut):
582
"""A .bzr version 5 control object.
584
This is a deprecated format and may be removed after sept 2006.
587
def open_repository(self):
588
"""See BzrDir.open_repository."""
589
from bzrlib.repository import RepositoryFormat5
590
return RepositoryFormat5().open(self, _found=True)
592
def open_workingtree(self, _unsupported=False):
593
"""See BzrDir.create_workingtree."""
594
from bzrlib.workingtree import WorkingTreeFormat2
595
return WorkingTreeFormat2().open(self, _found=True)
598
class BzrDir6(BzrDirPreSplitOut):
599
"""A .bzr version 6 control object.
601
This is a deprecated format and may be removed after sept 2006.
604
def open_repository(self):
605
"""See BzrDir.open_repository."""
606
from bzrlib.repository import RepositoryFormat6
607
return RepositoryFormat6().open(self, _found=True)
609
def open_workingtree(self, _unsupported=False):
610
"""See BzrDir.create_workingtree."""
611
from bzrlib.workingtree import WorkingTreeFormat2
612
return WorkingTreeFormat2().open(self, _found=True)
615
class BzrDirMeta1(BzrDir):
616
"""A .bzr meta version 1 control object.
618
This is the first control object where the
619
individual formats are really split out.
622
def create_branch(self):
623
"""See BzrDir.create_branch."""
624
from bzrlib.branch import BranchFormat
625
return BranchFormat.get_default_format().initialize(self)
627
def create_repository(self, shared=False):
628
"""See BzrDir.create_repository."""
629
from bzrlib.repository import RepositoryFormat
630
return RepositoryFormat.get_default_format().initialize(self, shared)
632
def create_workingtree(self, revision_id=None):
633
"""See BzrDir.create_workingtree."""
634
from bzrlib.workingtree import WorkingTreeFormat
635
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
637
def get_branch_transport(self, branch_format):
638
"""See BzrDir.get_branch_transport()."""
639
if branch_format is None:
640
return self.transport.clone('branch')
642
branch_format.get_format_string()
643
except NotImplementedError:
644
raise errors.IncompatibleFormat(branch_format, self._format)
646
self.transport.mkdir('branch')
647
except errors.FileExists:
649
return self.transport.clone('branch')
651
def get_repository_transport(self, repository_format):
652
"""See BzrDir.get_repository_transport()."""
653
if repository_format is None:
654
return self.transport.clone('repository')
656
repository_format.get_format_string()
657
except NotImplementedError:
658
raise errors.IncompatibleFormat(repository_format, self._format)
660
self.transport.mkdir('repository')
661
except errors.FileExists:
663
return self.transport.clone('repository')
665
def get_workingtree_transport(self, workingtree_format):
666
"""See BzrDir.get_workingtree_transport()."""
667
if workingtree_format is None:
668
return self.transport.clone('checkout')
670
workingtree_format.get_format_string()
671
except NotImplementedError:
672
raise errors.IncompatibleFormat(workingtree_format, self._format)
674
self.transport.mkdir('checkout')
675
except errors.FileExists:
677
return self.transport.clone('checkout')
679
def open_branch(self, unsupported=False):
680
"""See BzrDir.open_branch."""
681
from bzrlib.branch import BranchFormat
682
format = BranchFormat.find_format(self)
683
self._check_supported(format, unsupported)
684
return format.open(self, _found=True)
686
def open_repository(self, unsupported=False):
687
"""See BzrDir.open_repository."""
688
from bzrlib.repository import RepositoryFormat
689
format = RepositoryFormat.find_format(self)
690
self._check_supported(format, unsupported)
691
return format.open(self, _found=True)
693
def open_workingtree(self, unsupported=False):
694
"""See BzrDir.open_workingtree."""
695
from bzrlib.workingtree import WorkingTreeFormat
696
format = WorkingTreeFormat.find_format(self)
697
self._check_supported(format, unsupported)
698
return format.open(self, _found=True)
701
class BzrDirFormat(object):
702
"""An encapsulation of the initialization and open routines for a format.
704
Formats provide three things:
705
* An initialization routine,
709
Formats are placed in an dict by their format string for reference
710
during bzrdir opening. These should be subclasses of BzrDirFormat
713
Once a format is deprecated, just deprecate the initialize and open
714
methods on the format class. Do not deprecate the object, as the
715
object will be created every system load.
718
_default_format = None
719
"""The default format used for new .bzr dirs."""
722
"""The known formats."""
725
def find_format(klass, transport):
726
"""Return the format registered for URL."""
728
format_string = transport.get(".bzr/branch-format").read()
729
return klass._formats[format_string]
730
except errors.NoSuchFile:
731
raise errors.NotBranchError(path=transport.base)
733
raise errors.UnknownFormatError(format_string)
736
def get_default_format(klass):
737
"""Return the current default format."""
738
return klass._default_format
740
def get_format_string(self):
741
"""Return the ASCII format string that identifies this format."""
742
raise NotImplementedError(self.get_format_string)
744
def initialize(self, url):
745
"""Create a bzr control dir at this url and return an opened copy."""
746
# Since we don't have a .bzr directory, inherit the
747
# mode from the root directory
748
t = get_transport(url)
749
temp_control = LockableFiles(t, '')
750
temp_control._transport.mkdir('.bzr',
751
# FIXME: RBC 20060121 dont peek under
753
mode=temp_control._dir_mode)
754
file_mode = temp_control._file_mode
756
mutter('created control directory in ' + t.base)
757
control = t.clone('.bzr')
758
lock_file = 'branch-lock'
759
utf8_files = [('README',
760
"This is a Bazaar-NG control directory.\n"
761
"Do not change any files in this directory.\n"),
762
('branch-format', self.get_format_string()),
764
# NB: no need to escape relative paths that are url safe.
765
control.put(lock_file, StringIO(), mode=file_mode)
766
control_files = LockableFiles(control, lock_file)
767
control_files.lock_write()
769
for file, content in utf8_files:
770
control_files.put_utf8(file, content)
772
control_files.unlock()
773
return self.open(t, _found=True)
775
def is_supported(self):
776
"""Is this format supported?
778
Supported formats must be initializable and openable.
779
Unsupported formats may not support initialization or committing or
780
some other features depending on the reason for not being supported.
784
def open(self, transport, _found=False):
785
"""Return an instance of this format for the dir transport points at.
787
_found is a private parameter, do not use it.
790
assert isinstance(BzrDirFormat.find_format(transport),
792
return self._open(transport)
794
def _open(self, transport):
795
"""Template method helper for opening BzrDirectories.
797
This performs the actual open and any additional logic or parameter
800
raise NotImplementedError(self._open)
803
def register_format(klass, format):
804
klass._formats[format.get_format_string()] = format
807
def set_default_format(klass, format):
808
klass._default_format = format
811
def unregister_format(klass, format):
812
assert klass._formats[format.get_format_string()] is format
813
del klass._formats[format.get_format_string()]
816
class BzrDirFormat4(BzrDirFormat):
819
This format is a combined format for working tree, branch and repository.
821
- Format 1 working trees [always]
822
- Format 4 branches [always]
823
- Format 4 repositories [always]
825
This format is deprecated: it indexes texts using a text it which is
826
removed in format 5; write support for this format has been removed.
829
def get_format_string(self):
830
"""See BzrDirFormat.get_format_string()."""
831
return "Bazaar-NG branch, format 0.0.4\n"
833
def initialize(self, url):
834
"""Format 4 branches cannot be created."""
835
raise errors.UninitializableFormat(self)
837
def is_supported(self):
838
"""Format 4 is not supported.
840
It is not supported because the model changed from 4 to 5 and the
841
conversion logic is expensive - so doing it on the fly was not
846
def _open(self, transport):
847
"""See BzrDirFormat._open."""
848
return BzrDir4(transport, self)
851
class BzrDirFormat5(BzrDirFormat):
852
"""Bzr control format 5.
854
This format is a combined format for working tree, branch and repository.
856
- Format 2 working trees [always]
857
- Format 4 branches [always]
858
- Format 5 repositories [always]
859
Unhashed stores in the repository.
862
def get_format_string(self):
863
"""See BzrDirFormat.get_format_string()."""
864
return "Bazaar-NG branch, format 5\n"
866
def initialize(self, url, _cloning=False):
867
"""Format 5 dirs always have working tree, branch and repository.
869
Except when they are being cloned.
871
from bzrlib.branch import BzrBranchFormat4
872
from bzrlib.repository import RepositoryFormat5
873
from bzrlib.workingtree import WorkingTreeFormat2
874
result = super(BzrDirFormat5, self).initialize(url)
875
RepositoryFormat5().initialize(result, _internal=True)
877
BzrBranchFormat4().initialize(result)
878
WorkingTreeFormat2().initialize(result)
881
def _open(self, transport):
882
"""See BzrDirFormat._open."""
883
return BzrDir5(transport, self)
886
class BzrDirFormat6(BzrDirFormat):
887
"""Bzr control format 6.
889
This format is a combined format for working tree, branch and repository.
891
- Format 2 working trees [always]
892
- Format 4 branches [always]
893
- Format 6 repositories [always]
896
def get_format_string(self):
897
"""See BzrDirFormat.get_format_string()."""
898
return "Bazaar-NG branch, format 6\n"
900
def initialize(self, url, _cloning=False):
901
"""Format 6 dirs always have working tree, branch and repository.
903
Except when they are being cloned.
905
from bzrlib.branch import BzrBranchFormat4
906
from bzrlib.repository import RepositoryFormat6
907
from bzrlib.workingtree import WorkingTreeFormat2
908
result = super(BzrDirFormat6, self).initialize(url)
909
RepositoryFormat6().initialize(result, _internal=True)
911
BzrBranchFormat4().initialize(result)
913
WorkingTreeFormat2().initialize(result)
914
except errors.NotLocalUrl:
915
# emulate pre-check behaviour for working tree and silently
920
def _open(self, transport):
921
"""See BzrDirFormat._open."""
922
return BzrDir6(transport, self)
925
class BzrDirMetaFormat1(BzrDirFormat):
926
"""Bzr meta control format 1
928
This is the first format with split out working tree, branch and repository
931
- Format 3 working trees [optional]
932
- Format 5 branches [optional]
933
- Format 7 repositories [optional]
936
def get_format_string(self):
937
"""See BzrDirFormat.get_format_string()."""
938
return "Bazaar-NG meta directory, format 1\n"
940
def _open(self, transport):
941
"""See BzrDirFormat._open."""
942
return BzrDirMeta1(transport, self)
945
BzrDirFormat.register_format(BzrDirFormat4())
946
BzrDirFormat.register_format(BzrDirFormat5())
947
BzrDirFormat.register_format(BzrDirMetaFormat1())
948
__default_format = BzrDirFormat6()
949
BzrDirFormat.register_format(__default_format)
950
BzrDirFormat.set_default_format(__default_format)
953
class BzrDirTestProviderAdapter(object):
954
"""A tool to generate a suite testing multiple bzrdir formats at once.
956
This is done by copying the test once for each transport and injecting
957
the transport_server, transport_readonly_server, and bzrdir_format
958
classes into each copy. Each copy is also given a new id() to make it
962
def __init__(self, transport_server, transport_readonly_server, formats):
963
self._transport_server = transport_server
964
self._transport_readonly_server = transport_readonly_server
965
self._formats = formats
967
def adapt(self, test):
969
for format in self._formats:
970
new_test = deepcopy(test)
971
new_test.transport_server = self._transport_server
972
new_test.transport_readonly_server = self._transport_readonly_server
973
new_test.bzrdir_format = format
974
def make_new_test_id():
975
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
976
return lambda: new_id
977
new_test.id = make_new_test_id()
978
result.addTest(new_test)
982
class ScratchDir(BzrDir6):
983
"""Special test class: a bzrdir that cleans up itself..
986
>>> base = d.transport.base
989
>>> b.transport.__del__()
994
def __init__(self, files=[], dirs=[], transport=None):
995
"""Make a test branch.
997
This creates a temporary directory and runs init-tree in it.
999
If any files are listed, they are created in the working copy.
1001
if transport is None:
1002
transport = bzrlib.transport.local.ScratchTransport()
1003
# local import for scope restriction
1004
BzrDirFormat6().initialize(transport.base)
1005
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1006
self.create_repository()
1007
self.create_branch()
1008
self.create_workingtree()
1010
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1012
# BzrBranch creates a clone to .bzr and then forgets about the
1013
# original transport. A ScratchTransport() deletes itself and
1014
# everything underneath it when it goes away, so we need to
1015
# grab a local copy to prevent that from happening
1016
self._transport = transport
1019
self._transport.mkdir(d)
1022
self._transport.put(f, 'content of %s' % f)
1026
>>> orig = ScratchDir(files=["file1", "file2"])
1027
>>> os.listdir(orig.base)
1028
[u'.bzr', u'file1', u'file2']
1029
>>> clone = orig.clone()
1030
>>> if os.name != 'nt':
1031
... os.path.samefile(orig.base, clone.base)
1033
... orig.base == clone.base
1036
>>> os.listdir(clone.base)
1037
[u'.bzr', u'file1', u'file2']
1039
from shutil import copytree
1040
from bzrlib.osutils import mkdtemp
1043
copytree(self.base, base, symlinks=True)
1045
transport=bzrlib.transport.local.ScratchTransport(base))