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):
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
The Repository object is returned.
165
This must be overridden as an instance method in child classes, where
166
it should take no parameters and construct whatever repository format
167
that child class desires.
169
bzrdir = BzrDir.create(base)
170
return bzrdir.create_repository()
173
def create_standalone_workingtree(base):
174
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
176
'base' must be a local path or a file:// url.
178
This will use the current default BzrDirFormat, and use whatever
179
repository format that that uses for bzrdirformat.create_workingtree,
180
create_branch and create_repository.
182
The WorkingTree object is returned.
184
t = get_transport(safe_unicode(base))
185
if not isinstance(t, LocalTransport):
186
raise errors.NotLocalUrl(base)
187
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base)).bzrdir
188
return bzrdir.create_workingtree()
190
def create_workingtree(self):
191
"""Create a working tree at this BzrDir"""
192
raise NotImplementedError(self.create_workingtree)
194
def get_branch_transport(self, branch_format):
195
"""Get the transport for use by branch format in this BzrDir.
197
Note that bzr dirs that do not support format strings will raise
198
IncompatibleFormat if the branch format they are given has
199
a format string, and vice verca.
201
If branch_format is None, the transport is returned with no
202
checking. if it is not None, then the returned transport is
203
guaranteed to point to an existing directory ready for use.
205
raise NotImplementedError(self.get_branch_transport)
207
def get_repository_transport(self, repository_format):
208
"""Get the transport for use by repository format in this BzrDir.
210
Note that bzr dirs that do not support format strings will raise
211
IncompatibleFormat if the repository format they are given has
212
a format string, and vice verca.
214
If repository_format is None, the transport is returned with no
215
checking. if it is not None, then the returned transport is
216
guaranteed to point to an existing directory ready for use.
218
raise NotImplementedError(self.get_repository_transport)
220
def get_workingtree_transport(self, branch_format):
221
"""Get the transport for use by workingtree format in this BzrDir.
223
Note that bzr dirs that do not support format strings will raise
224
IncompatibleFormat if the workingtree format they are given has
225
a format string, and vice verca.
227
If workingtree_format is None, the transport is returned with no
228
checking. if it is not None, then the returned transport is
229
guaranteed to point to an existing directory ready for use.
231
raise NotImplementedError(self.get_workingtree_transport)
233
def __init__(self, _transport, _format):
234
"""Initialize a Bzr control dir object.
236
Only really common logic should reside here, concrete classes should be
237
made with varying behaviours.
239
_format: the format that is creating this BzrDir instance.
240
_transport: the transport this dir is based at.
242
self._format = _format
243
self.transport = _transport.clone('.bzr')
244
self.root_transport = _transport
247
def open_unsupported(base):
248
"""Open a branch which is not supported."""
249
return BzrDir.open(base, _unsupported=True)
252
def open(base, _unsupported=False):
253
"""Open an existing branch, rooted at 'base' (url)
255
_unsupported is a private parameter to the BzrDir class.
257
t = get_transport(base)
258
mutter("trying to open %r with transport %r", base, t)
259
format = BzrDirFormat.find_format(t)
260
if not _unsupported and not format.is_supported():
261
# see open_downlevel to open legacy branches.
262
raise errors.UnsupportedFormatError(
263
'sorry, format %s not supported' % format,
264
['use a different bzr version',
265
'or remove the .bzr directory'
266
' and "bzr init" again'])
267
return format.open(t, _found=True)
269
def open_branch(self, unsupported=False):
270
"""Open the branch object at this BzrDir if one is present.
272
If unsupported is True, then no longer supported branch formats can
275
TODO: static convenience version of this?
277
raise NotImplementedError(self.open_branch)
280
def open_containing(url):
281
"""Open an existing branch which contains url.
283
This probes for a branch at url, and searches upwards from there.
285
Basically we keep looking up until we find the control directory or
286
run into the root. If there isn't one, raises NotBranchError.
287
If there is one and it is either an unrecognised format or an unsupported
288
format, UnknownFormatError or UnsupportedFormatError are raised.
289
If there is one, it is returned, along with the unused portion of url.
291
t = get_transport(url)
292
# this gets the normalised url back. I.e. '.' -> the full path.
296
format = BzrDirFormat.find_format(t)
297
return format.open(t), t.relpath(url)
298
except errors.NotBranchError, e:
299
mutter('not a branch in: %r %s', t.base, e)
300
new_t = t.clone('..')
301
if new_t.base == t.base:
302
# reached the root, whatever that may be
303
raise errors.NotBranchError(path=url)
306
def open_repository(self, _unsupported=False):
307
"""Open the repository object at this BzrDir if one is present.
309
This will not follow the Branch object pointer - its strictly a direct
310
open facility. Most client code should use open_branch().repository to
313
_unsupported is a private parameter, not part of the api.
314
TODO: static convenience version of this?
316
raise NotImplementedError(self.open_repository)
318
def open_workingtree(self, _unsupported=False):
319
"""Open the workingtree object at this BzrDir if one is present.
321
TODO: static convenience version of this?
323
raise NotImplementedError(self.open_workingtree)
325
def sprout(self, url, revision_id=None, basis=None):
326
"""Create a copy of this bzrdir prepared for use as a new line of
329
If urls last component does not exist, it will be created.
331
Attributes related to the identity of the source branch like
332
branch nickname will be cleaned, a working tree is created
333
whether one existed before or not; and a local branch is always
336
if revision_id is not None, then the clone operation may tune
337
itself to download less data.
340
result = self._format.initialize(url)
341
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
343
source_branch = self.open_branch()
344
source_repository = source_branch.repository
345
except errors.NotBranchError:
348
source_repository = self.open_repository()
349
except errors.NoRepositoryPresent:
350
# copy the basis one if there is one
351
source_repository = basis_repo
352
if source_repository is not None:
353
source_repository.clone(result,
354
revision_id=revision_id,
357
# no repo available, make a new one
358
result.create_repository()
359
if source_branch is not None:
360
source_branch.sprout(result, revision_id=revision_id)
362
result.create_branch()
364
self.open_workingtree().clone(result,
365
revision_id=revision_id,
367
except (errors.NotBranchError, errors.NotLocalUrl):
368
result.create_workingtree()
372
class BzrDirPreSplitOut(BzrDir):
373
"""A common class for the all-in-one formats."""
375
def clone(self, url, revision_id=None, basis=None):
376
"""See BzrDir.clone()."""
377
from bzrlib.workingtree import WorkingTreeFormat2
379
result = self._format.initialize(url, _cloning=True)
380
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
381
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
382
self.open_branch().clone(result, revision_id=revision_id)
384
self.open_workingtree().clone(result, basis=basis_tree)
385
except errors.NotLocalUrl:
386
# make a new one, this format always has to have one.
387
WorkingTreeFormat2().initialize(result)
390
def create_branch(self):
391
"""See BzrDir.create_branch."""
392
return self.open_branch()
394
def create_repository(self):
395
"""See BzrDir.create_repository."""
396
return self.open_repository()
398
def create_workingtree(self):
399
"""See BzrDir.create_workingtree."""
400
return self.open_workingtree()
402
def get_branch_transport(self, branch_format):
403
"""See BzrDir.get_branch_transport()."""
404
if branch_format is None:
405
return self.transport
407
branch_format.get_format_string()
408
except NotImplementedError:
409
return self.transport
410
raise errors.IncompatibleFormat(branch_format, self._format)
412
def get_repository_transport(self, repository_format):
413
"""See BzrDir.get_repository_transport()."""
414
if repository_format is None:
415
return self.transport
417
repository_format.get_format_string()
418
except NotImplementedError:
419
return self.transport
420
raise errors.IncompatibleFormat(repository_format, self._format)
422
def get_workingtree_transport(self, workingtree_format):
423
"""See BzrDir.get_workingtree_transport()."""
424
if workingtree_format is None:
425
return self.transport
427
workingtree_format.get_format_string()
428
except NotImplementedError:
429
return self.transport
430
raise errors.IncompatibleFormat(workingtree_format, self._format)
432
def open_branch(self, unsupported=False):
433
"""See BzrDir.open_branch."""
434
from bzrlib.branch import BzrBranchFormat4
435
format = BzrBranchFormat4()
436
self._check_supported(format, unsupported)
437
return format.open(self, _found=True)
439
def sprout(self, url, revision_id=None, basis=None):
440
"""See BzrDir.sprout()."""
441
from bzrlib.workingtree import WorkingTreeFormat2
443
result = self._format.initialize(url, _cloning=True)
444
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
446
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
447
except errors.NoRepositoryPresent:
450
self.open_branch().sprout(result, revision_id=revision_id)
451
except errors.NotBranchError:
454
self.open_workingtree().clone(result, basis=basis_tree)
455
except (errors.NotBranchError, errors.NotLocalUrl):
456
# we always want a working tree
457
WorkingTreeFormat2().initialize(result)
461
class BzrDir4(BzrDirPreSplitOut):
462
"""A .bzr version 4 control object."""
464
def create_repository(self):
465
"""See BzrDir.create_repository."""
466
from bzrlib.repository import RepositoryFormat4
467
return RepositoryFormat4().initialize(self)
469
def open_repository(self):
470
"""See BzrDir.open_repository."""
471
from bzrlib.repository import RepositoryFormat4
472
return RepositoryFormat4().open(self, _found=True)
475
class BzrDir5(BzrDirPreSplitOut):
476
"""A .bzr version 5 control object."""
478
def open_repository(self):
479
"""See BzrDir.open_repository."""
480
from bzrlib.repository import RepositoryFormat5
481
return RepositoryFormat5().open(self, _found=True)
483
def open_workingtree(self, _unsupported=False):
484
"""See BzrDir.create_workingtree."""
485
from bzrlib.workingtree import WorkingTreeFormat2
486
return WorkingTreeFormat2().open(self, _found=True)
489
class BzrDir6(BzrDirPreSplitOut):
490
"""A .bzr version 6 control object."""
492
def open_repository(self):
493
"""See BzrDir.open_repository."""
494
from bzrlib.repository import RepositoryFormat6
495
return RepositoryFormat6().open(self, _found=True)
497
def open_workingtree(self, _unsupported=False):
498
"""See BzrDir.create_workingtree."""
499
from bzrlib.workingtree import WorkingTreeFormat2
500
return WorkingTreeFormat2().open(self, _found=True)
503
class BzrDirMeta1(BzrDir):
504
"""A .bzr meta version 1 control object.
506
This is the first control object where the
507
individual formats are really split out.
510
def create_branch(self):
511
"""See BzrDir.create_branch."""
512
from bzrlib.branch import BranchFormat
513
return BranchFormat.get_default_format().initialize(self)
515
def create_repository(self):
516
"""See BzrDir.create_repository."""
517
from bzrlib.repository import RepositoryFormat
518
return RepositoryFormat.get_default_format().initialize(self)
520
def create_workingtree(self):
521
"""See BzrDir.create_workingtree."""
522
from bzrlib.workingtree import WorkingTreeFormat
523
return WorkingTreeFormat.get_default_format().initialize(self)
525
def get_branch_transport(self, branch_format):
526
"""See BzrDir.get_branch_transport()."""
527
if branch_format is None:
528
return self.transport.clone('branch')
530
branch_format.get_format_string()
531
except NotImplementedError:
532
raise errors.IncompatibleFormat(branch_format, self._format)
534
self.transport.mkdir('branch')
535
except errors.FileExists:
537
return self.transport.clone('branch')
539
def get_repository_transport(self, repository_format):
540
"""See BzrDir.get_repository_transport()."""
541
if repository_format is None:
542
return self.transport.clone('repository')
544
repository_format.get_format_string()
545
except NotImplementedError:
546
raise errors.IncompatibleFormat(repository_format, self._format)
548
self.transport.mkdir('repository')
549
except errors.FileExists:
551
return self.transport.clone('repository')
553
def get_workingtree_transport(self, workingtree_format):
554
"""See BzrDir.get_workingtree_transport()."""
555
if workingtree_format is None:
556
return self.transport.clone('checkout')
558
workingtree_format.get_format_string()
559
except NotImplementedError:
560
raise errors.IncompatibleFormat(workingtree_format, self._format)
562
self.transport.mkdir('checkout')
563
except errors.FileExists:
565
return self.transport.clone('checkout')
567
def open_branch(self, unsupported=False):
568
"""See BzrDir.open_branch."""
569
from bzrlib.branch import BranchFormat
570
format = BranchFormat.find_format(self)
571
self._check_supported(format, unsupported)
572
return format.open(self, _found=True)
574
def open_repository(self, unsupported=False):
575
"""See BzrDir.open_repository."""
576
from bzrlib.repository import RepositoryFormat
577
format = RepositoryFormat.find_format(self)
578
self._check_supported(format, unsupported)
579
return format.open(self, _found=True)
581
def open_workingtree(self, unsupported=False):
582
"""See BzrDir.create_workingtree."""
583
from bzrlib.workingtree import WorkingTreeFormat
584
format = WorkingTreeFormat.find_format(self)
585
self._check_supported(format, unsupported)
586
return format.open(self, _found=True)
589
class BzrDirFormat(object):
590
"""An encapsulation of the initialization and open routines for a format.
592
Formats provide three things:
593
* An initialization routine,
597
Formats are placed in an dict by their format string for reference
598
during bzrdir opening. These should be subclasses of BzrDirFormat
601
Once a format is deprecated, just deprecate the initialize and open
602
methods on the format class. Do not deprecate the object, as the
603
object will be created every system load.
606
_default_format = None
607
"""The default format used for new .bzr dirs."""
610
"""The known formats."""
613
def find_format(klass, transport):
614
"""Return the format registered for URL."""
616
format_string = transport.get(".bzr/branch-format").read()
617
return klass._formats[format_string]
618
except errors.NoSuchFile:
619
raise errors.NotBranchError(path=transport.base)
621
raise errors.UnknownFormatError(format_string)
624
def get_default_format(klass):
625
"""Return the current default format."""
626
return klass._default_format
628
def get_format_string(self):
629
"""Return the ASCII format string that identifies this format."""
630
raise NotImplementedError(self.get_format_string)
632
def initialize(self, url):
633
"""Create a bzr control dir at this url and return an opened copy."""
634
# Since we don't have a .bzr directory, inherit the
635
# mode from the root directory
636
t = get_transport(url)
637
temp_control = LockableFiles(t, '')
638
temp_control._transport.mkdir('.bzr',
639
# FIXME: RBC 20060121 dont peek under
641
mode=temp_control._dir_mode)
642
file_mode = temp_control._file_mode
644
mutter('created control directory in ' + t.base)
645
control = t.clone('.bzr')
646
lock_file = 'branch-lock'
647
utf8_files = [('README',
648
"This is a Bazaar-NG control directory.\n"
649
"Do not change any files in this directory.\n"),
650
('branch-format', self.get_format_string()),
652
# NB: no need to escape relative paths that are url safe.
653
control.put(lock_file, StringIO(), mode=file_mode)
654
control_files = LockableFiles(control, lock_file)
655
control_files.lock_write()
657
for file, content in utf8_files:
658
control_files.put_utf8(file, content)
660
control_files.unlock()
661
return self.open(t, _found=True)
663
def is_supported(self):
664
"""Is this format supported?
666
Supported formats must be initializable and openable.
667
Unsupported formats may not support initialization or committing or
668
some other features depending on the reason for not being supported.
672
def open(self, transport, _found=False):
673
"""Return an instance of this format for the dir transport points at.
675
_found is a private parameter, do not use it.
678
assert isinstance(BzrDirFormat.find_format(transport),
680
return self._open(transport)
682
def _open(self, transport):
683
"""Template method helper for opening BzrDirectories.
685
This performs the actual open and any additional logic or parameter
688
raise NotImplementedError(self._open)
691
def register_format(klass, format):
692
klass._formats[format.get_format_string()] = format
695
def set_default_format(klass, format):
696
klass._default_format = format
699
def unregister_format(klass, format):
700
assert klass._formats[format.get_format_string()] is format
701
del klass._formats[format.get_format_string()]
704
class BzrDirFormat4(BzrDirFormat):
707
This format is a combined format for working tree, branch and repository.
709
- Format 1 working trees [always]
710
- Format 4 branches [always]
711
- Format 4 repositories [always]
713
This format is deprecated: it indexes texts using a text it which is
714
removed in format 5; write support for this format has been removed.
717
def get_format_string(self):
718
"""See BzrDirFormat.get_format_string()."""
719
return "Bazaar-NG branch, format 0.0.4\n"
721
def initialize(self, url):
722
"""Format 4 branches cannot be created."""
723
raise errors.UninitializableFormat(self)
725
def is_supported(self):
726
"""Format 4 is not supported.
728
It is not supported because the model changed from 4 to 5 and the
729
conversion logic is expensive - so doing it on the fly was not
734
def _open(self, transport):
735
"""See BzrDirFormat._open."""
736
return BzrDir4(transport, self)
739
class BzrDirFormat5(BzrDirFormat):
740
"""Bzr control format 5.
742
This format is a combined format for working tree, branch and repository.
744
- Format 2 working trees [always]
745
- Format 4 branches [always]
746
- Format 6 repositories [always]
747
Unhashed stores in the repository.
750
def get_format_string(self):
751
"""See BzrDirFormat.get_format_string()."""
752
return "Bazaar-NG branch, format 5\n"
754
def initialize(self, url, _cloning=False):
755
"""Format 5 dirs always have working tree, branch and repository.
757
Except when they are being cloned.
759
from bzrlib.branch import BzrBranchFormat4
760
from bzrlib.repository import RepositoryFormat5
761
from bzrlib.workingtree import WorkingTreeFormat2
762
result = super(BzrDirFormat5, self).initialize(url)
763
RepositoryFormat5().initialize(result, _internal=True)
765
BzrBranchFormat4().initialize(result)
766
WorkingTreeFormat2().initialize(result)
769
def _open(self, transport):
770
"""See BzrDirFormat._open."""
771
return BzrDir5(transport, self)
774
class BzrDirFormat6(BzrDirFormat):
775
"""Bzr control format 6.
777
This format is a combined format for working tree, branch and repository.
779
- Format 2 working trees [always]
780
- Format 4 branches [always]
781
- Format 6 repositories [always]
784
def get_format_string(self):
785
"""See BzrDirFormat.get_format_string()."""
786
return "Bazaar-NG branch, format 6\n"
788
def initialize(self, url, _cloning=False):
789
"""Format 6 dirs always have working tree, branch and repository.
791
Except when they are being cloned.
793
from bzrlib.branch import BzrBranchFormat4
794
from bzrlib.repository import RepositoryFormat6
795
from bzrlib.workingtree import WorkingTreeFormat2
796
result = super(BzrDirFormat6, self).initialize(url)
797
RepositoryFormat6().initialize(result, _internal=True)
799
BzrBranchFormat4().initialize(result)
801
WorkingTreeFormat2().initialize(result)
802
except errors.NotLocalUrl:
803
# emulate pre-check behaviour for working tree and silently
808
def _open(self, transport):
809
"""See BzrDirFormat._open."""
810
return BzrDir6(transport, self)
813
class BzrDirMetaFormat1(BzrDirFormat):
814
"""Bzr meta control format 1
816
This is the first format with split out working tree, branch and repository
819
- Format 3 working trees [optional]
820
- Format 5 branches [optional]
821
- Format 7 repositories [optional]
824
def get_format_string(self):
825
"""See BzrDirFormat.get_format_string()."""
826
return "Bazaar-NG meta directory, format 1\n"
828
def _open(self, transport):
829
"""See BzrDirFormat._open."""
830
return BzrDirMeta1(transport, self)
833
BzrDirFormat.register_format(BzrDirFormat4())
834
BzrDirFormat.register_format(BzrDirFormat5())
835
BzrDirFormat.register_format(BzrDirMetaFormat1())
836
__default_format = BzrDirFormat6()
837
BzrDirFormat.register_format(__default_format)
838
BzrDirFormat.set_default_format(__default_format)
841
class BzrDirTestProviderAdapter(object):
842
"""A tool to generate a suite testing multiple bzrdir formats at once.
844
This is done by copying the test once for each transport and injecting
845
the transport_server, transport_readonly_server, and bzrdir_format
846
classes into each copy. Each copy is also given a new id() to make it
850
def __init__(self, transport_server, transport_readonly_server, formats):
851
self._transport_server = transport_server
852
self._transport_readonly_server = transport_readonly_server
853
self._formats = formats
855
def adapt(self, test):
857
for format in self._formats:
858
new_test = deepcopy(test)
859
new_test.transport_server = self._transport_server
860
new_test.transport_readonly_server = self._transport_readonly_server
861
new_test.bzrdir_format = format
862
def make_new_test_id():
863
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
864
return lambda: new_id
865
new_test.id = make_new_test_id()
866
result.addTest(new_test)
870
class ScratchDir(BzrDir6):
871
"""Special test class: a bzrdir that cleans up itself..
874
>>> base = d.transport.base
877
>>> b.transport.__del__()
882
def __init__(self, files=[], dirs=[], transport=None):
883
"""Make a test branch.
885
This creates a temporary directory and runs init-tree in it.
887
If any files are listed, they are created in the working copy.
889
if transport is None:
890
transport = bzrlib.transport.local.ScratchTransport()
891
# local import for scope restriction
892
BzrDirFormat6().initialize(transport.base)
893
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
894
self.create_repository()
896
self.create_workingtree()
898
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
900
# BzrBranch creates a clone to .bzr and then forgets about the
901
# original transport. A ScratchTransport() deletes itself and
902
# everything underneath it when it goes away, so we need to
903
# grab a local copy to prevent that from happening
904
self._transport = transport
907
self._transport.mkdir(d)
910
self._transport.put(f, 'content of %s' % f)
914
>>> orig = ScratchDir(files=["file1", "file2"])
915
>>> os.listdir(orig.base)
916
[u'.bzr', u'file1', u'file2']
917
>>> clone = orig.clone()
918
>>> if os.name != 'nt':
919
... os.path.samefile(orig.base, clone.base)
921
... orig.base == clone.base
924
>>> os.listdir(clone.base)
925
[u'.bzr', u'file1', u'file2']
927
from shutil import copytree
928
from bzrlib.osutils import mkdtemp
931
copytree(self.base, base, symlinks=True)
933
transport=bzrlib.transport.local.ScratchTransport(base))