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):
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.
67
result = self._format.initialize(url)
68
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
70
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
71
except errors.NoRepositoryPresent:
74
self.open_branch().clone(result, revision_id=revision_id)
75
except errors.NotBranchError:
78
self.open_workingtree().clone(result, basis=basis_tree)
79
except (errors.NotBranchError, errors.NotLocalUrl):
83
def _get_basis_components(self, basis):
84
"""Retrieve the basis components that are available at basis."""
86
return None, None, None
88
basis_tree = basis.open_workingtree()
89
basis_branch = basis_tree.branch
90
basis_repo = basis_branch.repository
91
except (errors.NoWorkingTree, errors.NotLocalUrl):
94
basis_branch = basis.open_branch()
95
basis_repo = basis_branch.repository
96
except errors.NotBranchError:
99
basis_repo = basis.open_repository()
100
except errors.NoRepositoryPresent:
102
return basis_repo, basis_branch, basis_tree
104
def _make_tail(self, url):
105
segments = url.split('/')
106
if segments and segments[-1] not in ('', '.'):
107
parent = '/'.join(segments[:-1])
108
t = bzrlib.transport.get_transport(parent)
110
t.mkdir(segments[-1])
111
except errors.FileExists:
116
"""Create a new BzrDir at the url 'base'.
118
This will call the current default formats initialize with base
119
as the only parameter.
121
If you need a specific format, consider creating an instance
122
of that and calling initialize().
124
segments = base.split('/')
125
if segments and segments[-1] not in ('', '.'):
126
parent = '/'.join(segments[:-1])
127
t = bzrlib.transport.get_transport(parent)
129
t.mkdir(segments[-1])
130
except errors.FileExists:
132
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
134
def create_branch(self):
135
"""Create a branch in this BzrDir.
137
The bzrdirs format will control what branch format is created.
138
For more control see BranchFormatXX.create(a_bzrdir).
140
raise NotImplementedError(self.create_branch)
143
def create_branch_and_repo(base):
144
"""Create a new BzrDir, Branch and Repository at the url 'base'.
146
This will use the current default BzrDirFormat, and use whatever
147
repository format that that uses via bzrdir.create_branch and
150
The created Branch object is returned.
152
bzrdir = BzrDir.create(base)
153
bzrdir.create_repository()
154
return bzrdir.create_branch()
157
def create_repository(base, shared=False):
158
"""Create a new BzrDir and Repository at the url 'base'.
160
This will use the current default BzrDirFormat, and use whatever
161
repository format that that uses for bzrdirformat.create_repository.
163
;param shared: Create a shared repository rather than a standalone
165
The Repository object is returned.
167
This must be overridden as an instance method in child classes, where
168
it should take no parameters and construct whatever repository format
169
that child class desires.
171
bzrdir = BzrDir.create(base)
172
return bzrdir.create_repository()
175
def create_standalone_workingtree(base):
176
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
178
'base' must be a local path or a file:// url.
180
This will use the current default BzrDirFormat, and use whatever
181
repository format that that uses for bzrdirformat.create_workingtree,
182
create_branch and create_repository.
184
The WorkingTree object is returned.
186
t = get_transport(safe_unicode(base))
187
if not isinstance(t, LocalTransport):
188
raise errors.NotLocalUrl(base)
189
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base)).bzrdir
190
return bzrdir.create_workingtree()
192
def create_workingtree(self):
193
"""Create a working tree at this BzrDir"""
194
raise NotImplementedError(self.create_workingtree)
196
def get_branch_transport(self, branch_format):
197
"""Get the transport for use by branch format in this BzrDir.
199
Note that bzr dirs that do not support format strings will raise
200
IncompatibleFormat if the branch format they are given has
201
a format string, and vice verca.
203
If branch_format is None, the transport is returned with no
204
checking. if it is not None, then the returned transport is
205
guaranteed to point to an existing directory ready for use.
207
raise NotImplementedError(self.get_branch_transport)
209
def get_repository_transport(self, repository_format):
210
"""Get the transport for use by repository format in this BzrDir.
212
Note that bzr dirs that do not support format strings will raise
213
IncompatibleFormat if the repository format they are given has
214
a format string, and vice verca.
216
If repository_format is None, the transport is returned with no
217
checking. if it is not None, then the returned transport is
218
guaranteed to point to an existing directory ready for use.
220
raise NotImplementedError(self.get_repository_transport)
222
def get_workingtree_transport(self, tree_format):
223
"""Get the transport for use by workingtree format in this BzrDir.
225
Note that bzr dirs that do not support format strings will raise
226
IncompatibleFormat if the workingtree format they are given has
227
a format string, and vice verca.
229
If workingtree_format is None, the transport is returned with no
230
checking. if it is not None, then the returned transport is
231
guaranteed to point to an existing directory ready for use.
233
raise NotImplementedError(self.get_workingtree_transport)
235
def __init__(self, _transport, _format):
236
"""Initialize a Bzr control dir object.
238
Only really common logic should reside here, concrete classes should be
239
made with varying behaviours.
241
:param _format: the format that is creating this BzrDir instance.
242
:param _transport: the transport this dir is based at.
244
self._format = _format
245
self.transport = _transport.clone('.bzr')
246
self.root_transport = _transport
249
def open_unsupported(base):
250
"""Open a branch which is not supported."""
251
return BzrDir.open(base, _unsupported=True)
254
def open(base, _unsupported=False):
255
"""Open an existing bzrdir, rooted at 'base' (url)
257
_unsupported is a private parameter to the BzrDir class.
259
t = get_transport(base)
260
mutter("trying to open %r with transport %r", base, t)
261
format = BzrDirFormat.find_format(t)
262
if not _unsupported and not format.is_supported():
263
# see open_downlevel to open legacy branches.
264
raise errors.UnsupportedFormatError(
265
'sorry, format %s not supported' % format,
266
['use a different bzr version',
267
'or remove the .bzr directory'
268
' and "bzr init" again'])
269
return format.open(t, _found=True)
271
def open_branch(self, unsupported=False):
272
"""Open the branch object at this BzrDir if one is present.
274
If unsupported is True, then no longer supported branch formats can
277
TODO: static convenience version of this?
279
raise NotImplementedError(self.open_branch)
282
def open_containing(url):
283
"""Open an existing branch which contains url.
285
:param url: url to search from.
286
See open_containing_transport for more detail.
288
return BzrDir.open_containing_transport(get_transport(url))
291
def open_containing_transport(a_transport):
292
"""Open an existing branch which contains a_transport.base
294
This probes for a branch at a_transport, and searches upwards from there.
296
Basically we keep looking up until we find the control directory or
297
run into the root. If there isn't one, raises NotBranchError.
298
If there is one and it is either an unrecognised format or an unsupported
299
format, UnknownFormatError or UnsupportedFormatError are raised.
300
If there is one, it is returned, along with the unused portion of url.
302
# this gets the normalised url back. I.e. '.' -> the full path.
303
url = a_transport.base
306
format = BzrDirFormat.find_format(a_transport)
307
return format.open(a_transport), a_transport.relpath(url)
308
except errors.NotBranchError, e:
309
mutter('not a branch in: %r %s', a_transport.base, e)
310
new_t = a_transport.clone('..')
311
if new_t.base == a_transport.base:
312
# reached the root, whatever that may be
313
raise errors.NotBranchError(path=url)
316
def open_repository(self, _unsupported=False):
317
"""Open the repository object at this BzrDir if one is present.
319
This will not follow the Branch object pointer - its strictly a direct
320
open facility. Most client code should use open_branch().repository to
323
_unsupported is a private parameter, not part of the api.
324
TODO: static convenience version of this?
326
raise NotImplementedError(self.open_repository)
328
def open_workingtree(self, _unsupported=False):
329
"""Open the workingtree object at this BzrDir if one is present.
331
TODO: static convenience version of this?
333
raise NotImplementedError(self.open_workingtree)
335
def sprout(self, url, revision_id=None, basis=None):
336
"""Create a copy of this bzrdir prepared for use as a new line of
339
If urls last component does not exist, it will be created.
341
Attributes related to the identity of the source branch like
342
branch nickname will be cleaned, a working tree is created
343
whether one existed before or not; and a local branch is always
346
if revision_id is not None, then the clone operation may tune
347
itself to download less data.
350
result = self._format.initialize(url)
351
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
353
source_branch = self.open_branch()
354
source_repository = source_branch.repository
355
except errors.NotBranchError:
358
source_repository = self.open_repository()
359
except errors.NoRepositoryPresent:
360
# copy the basis one if there is one
361
source_repository = basis_repo
362
if source_repository is not None:
363
source_repository.clone(result,
364
revision_id=revision_id,
367
# no repo available, make a new one
368
result.create_repository()
369
if source_branch is not None:
370
source_branch.sprout(result, revision_id=revision_id)
372
result.create_branch()
374
self.open_workingtree().clone(result,
375
revision_id=revision_id,
377
except (errors.NotBranchError, errors.NotLocalUrl):
378
result.create_workingtree()
382
class BzrDirPreSplitOut(BzrDir):
383
"""A common class for the all-in-one formats."""
385
def clone(self, url, revision_id=None, basis=None):
386
"""See BzrDir.clone()."""
387
from bzrlib.workingtree import WorkingTreeFormat2
389
result = self._format.initialize(url, _cloning=True)
390
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
391
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
392
self.open_branch().clone(result, revision_id=revision_id)
394
self.open_workingtree().clone(result, basis=basis_tree)
395
except errors.NotLocalUrl:
396
# make a new one, this format always has to have one.
397
WorkingTreeFormat2().initialize(result)
400
def create_branch(self):
401
"""See BzrDir.create_branch."""
402
return self.open_branch()
404
def create_repository(self, shared=False):
405
"""See BzrDir.create_repository."""
407
raise errors.IncompatibleFormat('shared repository', self._format)
408
return self.open_repository()
410
def create_workingtree(self):
411
"""See BzrDir.create_workingtree."""
412
return self.open_workingtree()
414
def get_branch_transport(self, branch_format):
415
"""See BzrDir.get_branch_transport()."""
416
if branch_format is None:
417
return self.transport
419
branch_format.get_format_string()
420
except NotImplementedError:
421
return self.transport
422
raise errors.IncompatibleFormat(branch_format, self._format)
424
def get_repository_transport(self, repository_format):
425
"""See BzrDir.get_repository_transport()."""
426
if repository_format is None:
427
return self.transport
429
repository_format.get_format_string()
430
except NotImplementedError:
431
return self.transport
432
raise errors.IncompatibleFormat(repository_format, self._format)
434
def get_workingtree_transport(self, workingtree_format):
435
"""See BzrDir.get_workingtree_transport()."""
436
if workingtree_format is None:
437
return self.transport
439
workingtree_format.get_format_string()
440
except NotImplementedError:
441
return self.transport
442
raise errors.IncompatibleFormat(workingtree_format, self._format)
444
def open_branch(self, unsupported=False):
445
"""See BzrDir.open_branch."""
446
from bzrlib.branch import BzrBranchFormat4
447
format = BzrBranchFormat4()
448
self._check_supported(format, unsupported)
449
return format.open(self, _found=True)
451
def sprout(self, url, revision_id=None, basis=None):
452
"""See BzrDir.sprout()."""
453
from bzrlib.workingtree import WorkingTreeFormat2
455
result = self._format.initialize(url, _cloning=True)
456
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
458
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
459
except errors.NoRepositoryPresent:
462
self.open_branch().sprout(result, revision_id=revision_id)
463
except errors.NotBranchError:
466
self.open_workingtree().clone(result, basis=basis_tree)
467
except (errors.NotBranchError, errors.NotLocalUrl):
468
# we always want a working tree
469
WorkingTreeFormat2().initialize(result)
473
class BzrDir4(BzrDirPreSplitOut):
474
"""A .bzr version 4 control object."""
476
def create_repository(self, shared=False):
477
"""See BzrDir.create_repository."""
478
from bzrlib.repository import RepositoryFormat4
479
return RepositoryFormat4().initialize(self, shared)
481
def open_repository(self):
482
"""See BzrDir.open_repository."""
483
from bzrlib.repository import RepositoryFormat4
484
return RepositoryFormat4().open(self, _found=True)
487
class BzrDir5(BzrDirPreSplitOut):
488
"""A .bzr version 5 control object."""
490
def open_repository(self):
491
"""See BzrDir.open_repository."""
492
from bzrlib.repository import RepositoryFormat5
493
return RepositoryFormat5().open(self, _found=True)
495
def open_workingtree(self, _unsupported=False):
496
"""See BzrDir.create_workingtree."""
497
from bzrlib.workingtree import WorkingTreeFormat2
498
return WorkingTreeFormat2().open(self, _found=True)
501
class BzrDir6(BzrDirPreSplitOut):
502
"""A .bzr version 6 control object."""
504
def open_repository(self):
505
"""See BzrDir.open_repository."""
506
from bzrlib.repository import RepositoryFormat6
507
return RepositoryFormat6().open(self, _found=True)
509
def open_workingtree(self, _unsupported=False):
510
"""See BzrDir.create_workingtree."""
511
from bzrlib.workingtree import WorkingTreeFormat2
512
return WorkingTreeFormat2().open(self, _found=True)
515
class BzrDirMeta1(BzrDir):
516
"""A .bzr meta version 1 control object.
518
This is the first control object where the
519
individual formats are really split out.
522
def create_branch(self):
523
"""See BzrDir.create_branch."""
524
from bzrlib.branch import BranchFormat
525
return BranchFormat.get_default_format().initialize(self)
527
def create_repository(self, shared=False):
528
"""See BzrDir.create_repository."""
529
from bzrlib.repository import RepositoryFormat
530
return RepositoryFormat.get_default_format().initialize(self, shared)
532
def create_workingtree(self):
533
"""See BzrDir.create_workingtree."""
534
from bzrlib.workingtree import WorkingTreeFormat
535
return WorkingTreeFormat.get_default_format().initialize(self)
537
def get_branch_transport(self, branch_format):
538
"""See BzrDir.get_branch_transport()."""
539
if branch_format is None:
540
return self.transport.clone('branch')
542
branch_format.get_format_string()
543
except NotImplementedError:
544
raise errors.IncompatibleFormat(branch_format, self._format)
546
self.transport.mkdir('branch')
547
except errors.FileExists:
549
return self.transport.clone('branch')
551
def get_repository_transport(self, repository_format):
552
"""See BzrDir.get_repository_transport()."""
553
if repository_format is None:
554
return self.transport.clone('repository')
556
repository_format.get_format_string()
557
except NotImplementedError:
558
raise errors.IncompatibleFormat(repository_format, self._format)
560
self.transport.mkdir('repository')
561
except errors.FileExists:
563
return self.transport.clone('repository')
565
def get_workingtree_transport(self, workingtree_format):
566
"""See BzrDir.get_workingtree_transport()."""
567
if workingtree_format is None:
568
return self.transport.clone('checkout')
570
workingtree_format.get_format_string()
571
except NotImplementedError:
572
raise errors.IncompatibleFormat(workingtree_format, self._format)
574
self.transport.mkdir('checkout')
575
except errors.FileExists:
577
return self.transport.clone('checkout')
579
def open_branch(self, unsupported=False):
580
"""See BzrDir.open_branch."""
581
from bzrlib.branch import BranchFormat
582
format = BranchFormat.find_format(self)
583
self._check_supported(format, unsupported)
584
return format.open(self, _found=True)
586
def open_repository(self, unsupported=False):
587
"""See BzrDir.open_repository."""
588
from bzrlib.repository import RepositoryFormat
589
format = RepositoryFormat.find_format(self)
590
self._check_supported(format, unsupported)
591
return format.open(self, _found=True)
593
def open_workingtree(self, unsupported=False):
594
"""See BzrDir.create_workingtree."""
595
from bzrlib.workingtree import WorkingTreeFormat
596
format = WorkingTreeFormat.find_format(self)
597
self._check_supported(format, unsupported)
598
return format.open(self, _found=True)
601
class BzrDirFormat(object):
602
"""An encapsulation of the initialization and open routines for a format.
604
Formats provide three things:
605
* An initialization routine,
609
Formats are placed in an dict by their format string for reference
610
during bzrdir opening. These should be subclasses of BzrDirFormat
613
Once a format is deprecated, just deprecate the initialize and open
614
methods on the format class. Do not deprecate the object, as the
615
object will be created every system load.
618
_default_format = None
619
"""The default format used for new .bzr dirs."""
622
"""The known formats."""
625
def find_format(klass, transport):
626
"""Return the format registered for URL."""
628
format_string = transport.get(".bzr/branch-format").read()
629
return klass._formats[format_string]
630
except errors.NoSuchFile:
631
raise errors.NotBranchError(path=transport.base)
633
raise errors.UnknownFormatError(format_string)
636
def get_default_format(klass):
637
"""Return the current default format."""
638
return klass._default_format
640
def get_format_string(self):
641
"""Return the ASCII format string that identifies this format."""
642
raise NotImplementedError(self.get_format_string)
644
def initialize(self, url):
645
"""Create a bzr control dir at this url and return an opened copy."""
646
# Since we don't have a .bzr directory, inherit the
647
# mode from the root directory
648
t = get_transport(url)
649
temp_control = LockableFiles(t, '')
650
temp_control._transport.mkdir('.bzr',
651
# FIXME: RBC 20060121 dont peek under
653
mode=temp_control._dir_mode)
654
file_mode = temp_control._file_mode
656
mutter('created control directory in ' + t.base)
657
control = t.clone('.bzr')
658
lock_file = 'branch-lock'
659
utf8_files = [('README',
660
"This is a Bazaar-NG control directory.\n"
661
"Do not change any files in this directory.\n"),
662
('branch-format', self.get_format_string()),
664
# NB: no need to escape relative paths that are url safe.
665
control.put(lock_file, StringIO(), mode=file_mode)
666
control_files = LockableFiles(control, lock_file)
667
control_files.lock_write()
669
for file, content in utf8_files:
670
control_files.put_utf8(file, content)
672
control_files.unlock()
673
return self.open(t, _found=True)
675
def is_supported(self):
676
"""Is this format supported?
678
Supported formats must be initializable and openable.
679
Unsupported formats may not support initialization or committing or
680
some other features depending on the reason for not being supported.
684
def open(self, transport, _found=False):
685
"""Return an instance of this format for the dir transport points at.
687
_found is a private parameter, do not use it.
690
assert isinstance(BzrDirFormat.find_format(transport),
692
return self._open(transport)
694
def _open(self, transport):
695
"""Template method helper for opening BzrDirectories.
697
This performs the actual open and any additional logic or parameter
700
raise NotImplementedError(self._open)
703
def register_format(klass, format):
704
klass._formats[format.get_format_string()] = format
707
def set_default_format(klass, format):
708
klass._default_format = format
711
def unregister_format(klass, format):
712
assert klass._formats[format.get_format_string()] is format
713
del klass._formats[format.get_format_string()]
716
class BzrDirFormat4(BzrDirFormat):
719
This format is a combined format for working tree, branch and repository.
721
- Format 1 working trees [always]
722
- Format 4 branches [always]
723
- Format 4 repositories [always]
725
This format is deprecated: it indexes texts using a text it which is
726
removed in format 5; write support for this format has been removed.
729
def get_format_string(self):
730
"""See BzrDirFormat.get_format_string()."""
731
return "Bazaar-NG branch, format 0.0.4\n"
733
def initialize(self, url):
734
"""Format 4 branches cannot be created."""
735
raise errors.UninitializableFormat(self)
737
def is_supported(self):
738
"""Format 4 is not supported.
740
It is not supported because the model changed from 4 to 5 and the
741
conversion logic is expensive - so doing it on the fly was not
746
def _open(self, transport):
747
"""See BzrDirFormat._open."""
748
return BzrDir4(transport, self)
751
class BzrDirFormat5(BzrDirFormat):
752
"""Bzr control format 5.
754
This format is a combined format for working tree, branch and repository.
756
- Format 2 working trees [always]
757
- Format 4 branches [always]
758
- Format 5 repositories [always]
759
Unhashed stores in the repository.
762
def get_format_string(self):
763
"""See BzrDirFormat.get_format_string()."""
764
return "Bazaar-NG branch, format 5\n"
766
def initialize(self, url, _cloning=False):
767
"""Format 5 dirs always have working tree, branch and repository.
769
Except when they are being cloned.
771
from bzrlib.branch import BzrBranchFormat4
772
from bzrlib.repository import RepositoryFormat5
773
from bzrlib.workingtree import WorkingTreeFormat2
774
result = super(BzrDirFormat5, self).initialize(url)
775
RepositoryFormat5().initialize(result, _internal=True)
777
BzrBranchFormat4().initialize(result)
778
WorkingTreeFormat2().initialize(result)
781
def _open(self, transport):
782
"""See BzrDirFormat._open."""
783
return BzrDir5(transport, self)
786
class BzrDirFormat6(BzrDirFormat):
787
"""Bzr control format 6.
789
This format is a combined format for working tree, branch and repository.
791
- Format 2 working trees [always]
792
- Format 4 branches [always]
793
- Format 6 repositories [always]
796
def get_format_string(self):
797
"""See BzrDirFormat.get_format_string()."""
798
return "Bazaar-NG branch, format 6\n"
800
def initialize(self, url, _cloning=False):
801
"""Format 6 dirs always have working tree, branch and repository.
803
Except when they are being cloned.
805
from bzrlib.branch import BzrBranchFormat4
806
from bzrlib.repository import RepositoryFormat6
807
from bzrlib.workingtree import WorkingTreeFormat2
808
result = super(BzrDirFormat6, self).initialize(url)
809
RepositoryFormat6().initialize(result, _internal=True)
811
BzrBranchFormat4().initialize(result)
813
WorkingTreeFormat2().initialize(result)
814
except errors.NotLocalUrl:
815
# emulate pre-check behaviour for working tree and silently
820
def _open(self, transport):
821
"""See BzrDirFormat._open."""
822
return BzrDir6(transport, self)
825
class BzrDirMetaFormat1(BzrDirFormat):
826
"""Bzr meta control format 1
828
This is the first format with split out working tree, branch and repository
831
- Format 3 working trees [optional]
832
- Format 5 branches [optional]
833
- Format 7 repositories [optional]
836
def get_format_string(self):
837
"""See BzrDirFormat.get_format_string()."""
838
return "Bazaar-NG meta directory, format 1\n"
840
def _open(self, transport):
841
"""See BzrDirFormat._open."""
842
return BzrDirMeta1(transport, self)
845
BzrDirFormat.register_format(BzrDirFormat4())
846
BzrDirFormat.register_format(BzrDirFormat5())
847
BzrDirFormat.register_format(BzrDirMetaFormat1())
848
__default_format = BzrDirFormat6()
849
BzrDirFormat.register_format(__default_format)
850
BzrDirFormat.set_default_format(__default_format)
853
class BzrDirTestProviderAdapter(object):
854
"""A tool to generate a suite testing multiple bzrdir formats at once.
856
This is done by copying the test once for each transport and injecting
857
the transport_server, transport_readonly_server, and bzrdir_format
858
classes into each copy. Each copy is also given a new id() to make it
862
def __init__(self, transport_server, transport_readonly_server, formats):
863
self._transport_server = transport_server
864
self._transport_readonly_server = transport_readonly_server
865
self._formats = formats
867
def adapt(self, test):
869
for format in self._formats:
870
new_test = deepcopy(test)
871
new_test.transport_server = self._transport_server
872
new_test.transport_readonly_server = self._transport_readonly_server
873
new_test.bzrdir_format = format
874
def make_new_test_id():
875
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
876
return lambda: new_id
877
new_test.id = make_new_test_id()
878
result.addTest(new_test)
882
class ScratchDir(BzrDir6):
883
"""Special test class: a bzrdir that cleans up itself..
886
>>> base = d.transport.base
889
>>> b.transport.__del__()
894
def __init__(self, files=[], dirs=[], transport=None):
895
"""Make a test branch.
897
This creates a temporary directory and runs init-tree in it.
899
If any files are listed, they are created in the working copy.
901
if transport is None:
902
transport = bzrlib.transport.local.ScratchTransport()
903
# local import for scope restriction
904
BzrDirFormat6().initialize(transport.base)
905
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
906
self.create_repository()
908
self.create_workingtree()
910
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
912
# BzrBranch creates a clone to .bzr and then forgets about the
913
# original transport. A ScratchTransport() deletes itself and
914
# everything underneath it when it goes away, so we need to
915
# grab a local copy to prevent that from happening
916
self._transport = transport
919
self._transport.mkdir(d)
922
self._transport.put(f, 'content of %s' % f)
926
>>> orig = ScratchDir(files=["file1", "file2"])
927
>>> os.listdir(orig.base)
928
[u'.bzr', u'file1', u'file2']
929
>>> clone = orig.clone()
930
>>> if os.name != 'nt':
931
... os.path.samefile(orig.base, clone.base)
933
... orig.base == clone.base
936
>>> os.listdir(clone.base)
937
[u'.bzr', u'file1', u'file2']
939
from shutil import copytree
940
from bzrlib.osutils import mkdtemp
943
copytree(self.base, base, symlinks=True)
945
transport=bzrlib.transport.local.ScratchTransport(base))