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.NoWorkingTree, 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, revision_id=None):
191
"""Create a working tree at this BzrDir.
193
revision_id: create it as of this revision id.
195
raise NotImplementedError(self.create_workingtree)
197
def get_branch_transport(self, branch_format):
198
"""Get the transport for use by branch format in this BzrDir.
200
Note that bzr dirs that do not support format strings will raise
201
IncompatibleFormat if the branch format they are given has
202
a format string, and vice verca.
204
If branch_format is None, the transport is returned with no
205
checking. if it is not None, then the returned transport is
206
guaranteed to point to an existing directory ready for use.
208
raise NotImplementedError(self.get_branch_transport)
210
def get_repository_transport(self, repository_format):
211
"""Get the transport for use by repository format in this BzrDir.
213
Note that bzr dirs that do not support format strings will raise
214
IncompatibleFormat if the repository format they are given has
215
a format string, and vice verca.
217
If repository_format is None, the transport is returned with no
218
checking. if it is not None, then the returned transport is
219
guaranteed to point to an existing directory ready for use.
221
raise NotImplementedError(self.get_repository_transport)
223
def get_workingtree_transport(self, branch_format):
224
"""Get the transport for use by workingtree format in this BzrDir.
226
Note that bzr dirs that do not support format strings will raise
227
IncompatibleFormat if the workingtree format they are given has
228
a format string, and vice verca.
230
If workingtree_format is None, the transport is returned with no
231
checking. if it is not None, then the returned transport is
232
guaranteed to point to an existing directory ready for use.
234
raise NotImplementedError(self.get_workingtree_transport)
236
def __init__(self, _transport, _format):
237
"""Initialize a Bzr control dir object.
239
Only really common logic should reside here, concrete classes should be
240
made with varying behaviours.
242
_format: the format that is creating this BzrDir instance.
243
_transport: the transport this dir is based at.
245
self._format = _format
246
self.transport = _transport.clone('.bzr')
247
self.root_transport = _transport
250
def open_unsupported(base):
251
"""Open a branch which is not supported."""
252
return BzrDir.open(base, _unsupported=True)
255
def open(base, _unsupported=False):
256
"""Open an existing branch, rooted at 'base' (url)
258
_unsupported is a private parameter to the BzrDir class.
260
t = get_transport(base)
261
mutter("trying to open %r with transport %r", base, t)
262
format = BzrDirFormat.find_format(t)
263
if not _unsupported and not format.is_supported():
264
# see open_downlevel to open legacy branches.
265
raise errors.UnsupportedFormatError(
266
'sorry, format %s not supported' % format,
267
['use a different bzr version',
268
'or remove the .bzr directory'
269
' and "bzr init" again'])
270
return format.open(t, _found=True)
272
def open_branch(self, unsupported=False):
273
"""Open the branch object at this BzrDir if one is present.
275
If unsupported is True, then no longer supported branch formats can
278
TODO: static convenience version of this?
280
raise NotImplementedError(self.open_branch)
283
def open_containing(url):
284
"""Open an existing branch which contains url.
286
This probes for a branch at url, and searches upwards from there.
288
Basically we keep looking up until we find the control directory or
289
run into the root. If there isn't one, raises NotBranchError.
290
If there is one and it is either an unrecognised format or an unsupported
291
format, UnknownFormatError or UnsupportedFormatError are raised.
292
If there is one, it is returned, along with the unused portion of url.
294
t = get_transport(url)
295
# this gets the normalised url back. I.e. '.' -> the full path.
299
format = BzrDirFormat.find_format(t)
300
return format.open(t), t.relpath(url)
301
except errors.NotBranchError, e:
302
mutter('not a branch in: %r %s', t.base, e)
303
new_t = t.clone('..')
304
if new_t.base == t.base:
305
# reached the root, whatever that may be
306
raise errors.NotBranchError(path=url)
309
def open_repository(self, _unsupported=False):
310
"""Open the repository object at this BzrDir if one is present.
312
This will not follow the Branch object pointer - its strictly a direct
313
open facility. Most client code should use open_branch().repository to
316
_unsupported is a private parameter, not part of the api.
317
TODO: static convenience version of this?
319
raise NotImplementedError(self.open_repository)
321
def open_workingtree(self, _unsupported=False):
322
"""Open the workingtree object at this BzrDir if one is present.
324
TODO: static convenience version of this?
326
raise NotImplementedError(self.open_workingtree)
328
def sprout(self, url, revision_id=None, basis=None):
329
"""Create a copy of this bzrdir prepared for use as a new line of
332
If urls last component does not exist, it will be created.
334
Attributes related to the identity of the source branch like
335
branch nickname will be cleaned, a working tree is created
336
whether one existed before or not; and a local branch is always
339
if revision_id is not None, then the clone operation may tune
340
itself to download less data.
343
result = self._format.initialize(url)
344
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
346
source_branch = self.open_branch()
347
source_repository = source_branch.repository
348
except errors.NotBranchError:
351
source_repository = self.open_repository()
352
except errors.NoRepositoryPresent:
353
# copy the basis one if there is one
354
source_repository = basis_repo
355
if source_repository is not None:
356
source_repository.clone(result,
357
revision_id=revision_id,
360
# no repo available, make a new one
361
result.create_repository()
362
if source_branch is not None:
363
source_branch.sprout(result, revision_id=revision_id)
365
result.create_branch()
367
self.open_workingtree().clone(result,
368
revision_id=revision_id,
370
except (errors.NoWorkingTree, errors.NotLocalUrl):
371
result.create_workingtree()
375
class BzrDirPreSplitOut(BzrDir):
376
"""A common class for the all-in-one formats."""
378
def clone(self, url, revision_id=None, basis=None):
379
"""See BzrDir.clone()."""
380
from bzrlib.workingtree import WorkingTreeFormat2
382
result = self._format.initialize(url, _cloning=True)
383
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
384
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
385
self.open_branch().clone(result, revision_id=revision_id)
387
self.open_workingtree().clone(result, basis=basis_tree)
388
except errors.NotLocalUrl:
389
# make a new one, this format always has to have one.
390
WorkingTreeFormat2().initialize(result)
393
def create_branch(self):
394
"""See BzrDir.create_branch."""
395
return self.open_branch()
397
def create_repository(self):
398
"""See BzrDir.create_repository."""
399
return self.open_repository()
401
def create_workingtree(self, revision_id=None):
402
"""See BzrDir.create_workingtree."""
403
# this looks buggy but is not -really-
404
# clone and sprout will have set the revision_id
405
# and that will have set it for us, its only
406
# specific uses of create_workingtree in isolation
407
# that can do wonky stuff here, and that only
408
# happens for creating checkouts, which cannot be
409
# done on this format anyway. So - acceptable wart.
410
result = self.open_workingtree()
411
if revision_id is not None:
412
result.set_last_revision(revision_id)
415
def get_branch_transport(self, branch_format):
416
"""See BzrDir.get_branch_transport()."""
417
if branch_format is None:
418
return self.transport
420
branch_format.get_format_string()
421
except NotImplementedError:
422
return self.transport
423
raise errors.IncompatibleFormat(branch_format, self._format)
425
def get_repository_transport(self, repository_format):
426
"""See BzrDir.get_repository_transport()."""
427
if repository_format is None:
428
return self.transport
430
repository_format.get_format_string()
431
except NotImplementedError:
432
return self.transport
433
raise errors.IncompatibleFormat(repository_format, self._format)
435
def get_workingtree_transport(self, workingtree_format):
436
"""See BzrDir.get_workingtree_transport()."""
437
if workingtree_format is None:
438
return self.transport
440
workingtree_format.get_format_string()
441
except NotImplementedError:
442
return self.transport
443
raise errors.IncompatibleFormat(workingtree_format, self._format)
445
def open_branch(self, unsupported=False):
446
"""See BzrDir.open_branch."""
447
from bzrlib.branch import BzrBranchFormat4
448
format = BzrBranchFormat4()
449
self._check_supported(format, unsupported)
450
return format.open(self, _found=True)
452
def sprout(self, url, revision_id=None, basis=None):
453
"""See BzrDir.sprout()."""
454
from bzrlib.workingtree import WorkingTreeFormat2
456
result = self._format.initialize(url, _cloning=True)
457
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
459
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
460
except errors.NoRepositoryPresent:
463
self.open_branch().sprout(result, revision_id=revision_id)
464
except errors.NotBranchError:
467
self.open_workingtree().clone(result, basis=basis_tree)
468
except (errors.NotBranchError, errors.NotLocalUrl):
469
# we always want a working tree
470
WorkingTreeFormat2().initialize(result)
474
class BzrDir4(BzrDirPreSplitOut):
475
"""A .bzr version 4 control object.
477
This is a deprecated format and may be removed after sept 2006.
480
def create_repository(self):
481
"""See BzrDir.create_repository."""
482
from bzrlib.repository import RepositoryFormat4
483
return RepositoryFormat4().initialize(self)
485
def open_repository(self):
486
"""See BzrDir.open_repository."""
487
from bzrlib.repository import RepositoryFormat4
488
return RepositoryFormat4().open(self, _found=True)
491
class BzrDir5(BzrDirPreSplitOut):
492
"""A .bzr version 5 control object.
494
This is a deprecated format and may be removed after sept 2006.
497
def open_repository(self):
498
"""See BzrDir.open_repository."""
499
from bzrlib.repository import RepositoryFormat5
500
return RepositoryFormat5().open(self, _found=True)
502
def open_workingtree(self, _unsupported=False):
503
"""See BzrDir.create_workingtree."""
504
from bzrlib.workingtree import WorkingTreeFormat2
505
return WorkingTreeFormat2().open(self, _found=True)
508
class BzrDir6(BzrDirPreSplitOut):
509
"""A .bzr version 6 control object.
511
This is a deprecated format and may be removed after sept 2006.
514
def open_repository(self):
515
"""See BzrDir.open_repository."""
516
from bzrlib.repository import RepositoryFormat6
517
return RepositoryFormat6().open(self, _found=True)
519
def open_workingtree(self, _unsupported=False):
520
"""See BzrDir.create_workingtree."""
521
from bzrlib.workingtree import WorkingTreeFormat2
522
return WorkingTreeFormat2().open(self, _found=True)
525
class BzrDirMeta1(BzrDir):
526
"""A .bzr meta version 1 control object.
528
This is the first control object where the
529
individual formats are really split out.
532
def create_branch(self):
533
"""See BzrDir.create_branch."""
534
from bzrlib.branch import BranchFormat
535
return BranchFormat.get_default_format().initialize(self)
537
def create_repository(self):
538
"""See BzrDir.create_repository."""
539
from bzrlib.repository import RepositoryFormat
540
return RepositoryFormat.get_default_format().initialize(self)
542
def create_workingtree(self, revision_id=None):
543
"""See BzrDir.create_workingtree."""
544
from bzrlib.workingtree import WorkingTreeFormat
545
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
547
def get_branch_transport(self, branch_format):
548
"""See BzrDir.get_branch_transport()."""
549
if branch_format is None:
550
return self.transport.clone('branch')
552
branch_format.get_format_string()
553
except NotImplementedError:
554
raise errors.IncompatibleFormat(branch_format, self._format)
556
self.transport.mkdir('branch')
557
except errors.FileExists:
559
return self.transport.clone('branch')
561
def get_repository_transport(self, repository_format):
562
"""See BzrDir.get_repository_transport()."""
563
if repository_format is None:
564
return self.transport.clone('repository')
566
repository_format.get_format_string()
567
except NotImplementedError:
568
raise errors.IncompatibleFormat(repository_format, self._format)
570
self.transport.mkdir('repository')
571
except errors.FileExists:
573
return self.transport.clone('repository')
575
def get_workingtree_transport(self, workingtree_format):
576
"""See BzrDir.get_workingtree_transport()."""
577
if workingtree_format is None:
578
return self.transport.clone('checkout')
580
workingtree_format.get_format_string()
581
except NotImplementedError:
582
raise errors.IncompatibleFormat(workingtree_format, self._format)
584
self.transport.mkdir('checkout')
585
except errors.FileExists:
587
return self.transport.clone('checkout')
589
def open_branch(self, unsupported=False):
590
"""See BzrDir.open_branch."""
591
from bzrlib.branch import BranchFormat
592
format = BranchFormat.find_format(self)
593
self._check_supported(format, unsupported)
594
return format.open(self, _found=True)
596
def open_repository(self, unsupported=False):
597
"""See BzrDir.open_repository."""
598
from bzrlib.repository import RepositoryFormat
599
format = RepositoryFormat.find_format(self)
600
self._check_supported(format, unsupported)
601
return format.open(self, _found=True)
603
def open_workingtree(self, unsupported=False):
604
"""See BzrDir.open_workingtree."""
605
from bzrlib.workingtree import WorkingTreeFormat
606
format = WorkingTreeFormat.find_format(self)
607
self._check_supported(format, unsupported)
608
return format.open(self, _found=True)
611
class BzrDirFormat(object):
612
"""An encapsulation of the initialization and open routines for a format.
614
Formats provide three things:
615
* An initialization routine,
619
Formats are placed in an dict by their format string for reference
620
during bzrdir opening. These should be subclasses of BzrDirFormat
623
Once a format is deprecated, just deprecate the initialize and open
624
methods on the format class. Do not deprecate the object, as the
625
object will be created every system load.
628
_default_format = None
629
"""The default format used for new .bzr dirs."""
632
"""The known formats."""
635
def find_format(klass, transport):
636
"""Return the format registered for URL."""
638
format_string = transport.get(".bzr/branch-format").read()
639
return klass._formats[format_string]
640
except errors.NoSuchFile:
641
raise errors.NotBranchError(path=transport.base)
643
raise errors.UnknownFormatError(format_string)
646
def get_default_format(klass):
647
"""Return the current default format."""
648
return klass._default_format
650
def get_format_string(self):
651
"""Return the ASCII format string that identifies this format."""
652
raise NotImplementedError(self.get_format_string)
654
def initialize(self, url):
655
"""Create a bzr control dir at this url and return an opened copy."""
656
# Since we don't have a .bzr directory, inherit the
657
# mode from the root directory
658
t = get_transport(url)
659
temp_control = LockableFiles(t, '')
660
temp_control._transport.mkdir('.bzr',
661
# FIXME: RBC 20060121 dont peek under
663
mode=temp_control._dir_mode)
664
file_mode = temp_control._file_mode
666
mutter('created control directory in ' + t.base)
667
control = t.clone('.bzr')
668
lock_file = 'branch-lock'
669
utf8_files = [('README',
670
"This is a Bazaar-NG control directory.\n"
671
"Do not change any files in this directory.\n"),
672
('branch-format', self.get_format_string()),
674
# NB: no need to escape relative paths that are url safe.
675
control.put(lock_file, StringIO(), mode=file_mode)
676
control_files = LockableFiles(control, lock_file)
677
control_files.lock_write()
679
for file, content in utf8_files:
680
control_files.put_utf8(file, content)
682
control_files.unlock()
683
return self.open(t, _found=True)
685
def is_supported(self):
686
"""Is this format supported?
688
Supported formats must be initializable and openable.
689
Unsupported formats may not support initialization or committing or
690
some other features depending on the reason for not being supported.
694
def open(self, transport, _found=False):
695
"""Return an instance of this format for the dir transport points at.
697
_found is a private parameter, do not use it.
700
assert isinstance(BzrDirFormat.find_format(transport),
702
return self._open(transport)
704
def _open(self, transport):
705
"""Template method helper for opening BzrDirectories.
707
This performs the actual open and any additional logic or parameter
710
raise NotImplementedError(self._open)
713
def register_format(klass, format):
714
klass._formats[format.get_format_string()] = format
717
def set_default_format(klass, format):
718
klass._default_format = format
721
def unregister_format(klass, format):
722
assert klass._formats[format.get_format_string()] is format
723
del klass._formats[format.get_format_string()]
726
class BzrDirFormat4(BzrDirFormat):
729
This format is a combined format for working tree, branch and repository.
731
- Format 1 working trees [always]
732
- Format 4 branches [always]
733
- Format 4 repositories [always]
735
This format is deprecated: it indexes texts using a text it which is
736
removed in format 5; write support for this format has been removed.
739
def get_format_string(self):
740
"""See BzrDirFormat.get_format_string()."""
741
return "Bazaar-NG branch, format 0.0.4\n"
743
def initialize(self, url):
744
"""Format 4 branches cannot be created."""
745
raise errors.UninitializableFormat(self)
747
def is_supported(self):
748
"""Format 4 is not supported.
750
It is not supported because the model changed from 4 to 5 and the
751
conversion logic is expensive - so doing it on the fly was not
756
def _open(self, transport):
757
"""See BzrDirFormat._open."""
758
return BzrDir4(transport, self)
761
class BzrDirFormat5(BzrDirFormat):
762
"""Bzr control format 5.
764
This format is a combined format for working tree, branch and repository.
766
- Format 2 working trees [always]
767
- Format 4 branches [always]
768
- Format 6 repositories [always]
769
Unhashed stores in the repository.
772
def get_format_string(self):
773
"""See BzrDirFormat.get_format_string()."""
774
return "Bazaar-NG branch, format 5\n"
776
def initialize(self, url, _cloning=False):
777
"""Format 5 dirs always have working tree, branch and repository.
779
Except when they are being cloned.
781
from bzrlib.branch import BzrBranchFormat4
782
from bzrlib.repository import RepositoryFormat5
783
from bzrlib.workingtree import WorkingTreeFormat2
784
result = super(BzrDirFormat5, self).initialize(url)
785
RepositoryFormat5().initialize(result, _internal=True)
787
BzrBranchFormat4().initialize(result)
788
WorkingTreeFormat2().initialize(result)
791
def _open(self, transport):
792
"""See BzrDirFormat._open."""
793
return BzrDir5(transport, self)
796
class BzrDirFormat6(BzrDirFormat):
797
"""Bzr control format 6.
799
This format is a combined format for working tree, branch and repository.
801
- Format 2 working trees [always]
802
- Format 4 branches [always]
803
- Format 6 repositories [always]
806
def get_format_string(self):
807
"""See BzrDirFormat.get_format_string()."""
808
return "Bazaar-NG branch, format 6\n"
810
def initialize(self, url, _cloning=False):
811
"""Format 6 dirs always have working tree, branch and repository.
813
Except when they are being cloned.
815
from bzrlib.branch import BzrBranchFormat4
816
from bzrlib.repository import RepositoryFormat6
817
from bzrlib.workingtree import WorkingTreeFormat2
818
result = super(BzrDirFormat6, self).initialize(url)
819
RepositoryFormat6().initialize(result, _internal=True)
821
BzrBranchFormat4().initialize(result)
823
WorkingTreeFormat2().initialize(result)
824
except errors.NotLocalUrl:
825
# emulate pre-check behaviour for working tree and silently
830
def _open(self, transport):
831
"""See BzrDirFormat._open."""
832
return BzrDir6(transport, self)
835
class BzrDirMetaFormat1(BzrDirFormat):
836
"""Bzr meta control format 1
838
This is the first format with split out working tree, branch and repository
841
- Format 3 working trees [optional]
842
- Format 5 branches [optional]
843
- Format 7 repositories [optional]
846
def get_format_string(self):
847
"""See BzrDirFormat.get_format_string()."""
848
return "Bazaar-NG meta directory, format 1\n"
850
def _open(self, transport):
851
"""See BzrDirFormat._open."""
852
return BzrDirMeta1(transport, self)
855
BzrDirFormat.register_format(BzrDirFormat4())
856
BzrDirFormat.register_format(BzrDirFormat5())
857
BzrDirFormat.register_format(BzrDirMetaFormat1())
858
__default_format = BzrDirFormat6()
859
BzrDirFormat.register_format(__default_format)
860
BzrDirFormat.set_default_format(__default_format)
863
class BzrDirTestProviderAdapter(object):
864
"""A tool to generate a suite testing multiple bzrdir formats at once.
866
This is done by copying the test once for each transport and injecting
867
the transport_server, transport_readonly_server, and bzrdir_format
868
classes into each copy. Each copy is also given a new id() to make it
872
def __init__(self, transport_server, transport_readonly_server, formats):
873
self._transport_server = transport_server
874
self._transport_readonly_server = transport_readonly_server
875
self._formats = formats
877
def adapt(self, test):
879
for format in self._formats:
880
new_test = deepcopy(test)
881
new_test.transport_server = self._transport_server
882
new_test.transport_readonly_server = self._transport_readonly_server
883
new_test.bzrdir_format = format
884
def make_new_test_id():
885
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
886
return lambda: new_id
887
new_test.id = make_new_test_id()
888
result.addTest(new_test)
892
class ScratchDir(BzrDir6):
893
"""Special test class: a bzrdir that cleans up itself..
896
>>> base = d.transport.base
899
>>> b.transport.__del__()
904
def __init__(self, files=[], dirs=[], transport=None):
905
"""Make a test branch.
907
This creates a temporary directory and runs init-tree in it.
909
If any files are listed, they are created in the working copy.
911
if transport is None:
912
transport = bzrlib.transport.local.ScratchTransport()
913
# local import for scope restriction
914
BzrDirFormat6().initialize(transport.base)
915
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
916
self.create_repository()
918
self.create_workingtree()
920
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
922
# BzrBranch creates a clone to .bzr and then forgets about the
923
# original transport. A ScratchTransport() deletes itself and
924
# everything underneath it when it goes away, so we need to
925
# grab a local copy to prevent that from happening
926
self._transport = transport
929
self._transport.mkdir(d)
932
self._transport.put(f, 'content of %s' % f)
936
>>> orig = ScratchDir(files=["file1", "file2"])
937
>>> os.listdir(orig.base)
938
[u'.bzr', u'file1', u'file2']
939
>>> clone = orig.clone()
940
>>> if os.name != 'nt':
941
... os.path.samefile(orig.base, clone.base)
943
... orig.base == clone.base
946
>>> os.listdir(clone.base)
947
[u'.bzr', u'file1', u'file2']
949
from shutil import copytree
950
from bzrlib.osutils import mkdtemp
953
copytree(self.base, base, symlinks=True)
955
transport=bzrlib.transport.local.ScratchTransport(base))