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
def create_repository(self):
478
"""See BzrDir.create_repository."""
479
from bzrlib.repository import RepositoryFormat4
480
return RepositoryFormat4().initialize(self)
482
def open_repository(self):
483
"""See BzrDir.open_repository."""
484
from bzrlib.repository import RepositoryFormat4
485
return RepositoryFormat4().open(self, _found=True)
488
class BzrDir5(BzrDirPreSplitOut):
489
"""A .bzr version 5 control object."""
491
def open_repository(self):
492
"""See BzrDir.open_repository."""
493
from bzrlib.repository import RepositoryFormat5
494
return RepositoryFormat5().open(self, _found=True)
496
def open_workingtree(self, _unsupported=False):
497
"""See BzrDir.create_workingtree."""
498
from bzrlib.workingtree import WorkingTreeFormat2
499
return WorkingTreeFormat2().open(self, _found=True)
502
class BzrDir6(BzrDirPreSplitOut):
503
"""A .bzr version 6 control object."""
505
def open_repository(self):
506
"""See BzrDir.open_repository."""
507
from bzrlib.repository import RepositoryFormat6
508
return RepositoryFormat6().open(self, _found=True)
510
def open_workingtree(self, _unsupported=False):
511
"""See BzrDir.create_workingtree."""
512
from bzrlib.workingtree import WorkingTreeFormat2
513
return WorkingTreeFormat2().open(self, _found=True)
516
class BzrDirMeta1(BzrDir):
517
"""A .bzr meta version 1 control object.
519
This is the first control object where the
520
individual formats are really split out.
523
def create_branch(self):
524
"""See BzrDir.create_branch."""
525
from bzrlib.branch import BranchFormat
526
return BranchFormat.get_default_format().initialize(self)
528
def create_repository(self):
529
"""See BzrDir.create_repository."""
530
from bzrlib.repository import RepositoryFormat
531
return RepositoryFormat.get_default_format().initialize(self)
533
def create_workingtree(self, revision_id=None):
534
"""See BzrDir.create_workingtree."""
535
from bzrlib.workingtree import WorkingTreeFormat
536
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
538
def get_branch_transport(self, branch_format):
539
"""See BzrDir.get_branch_transport()."""
540
if branch_format is None:
541
return self.transport.clone('branch')
543
branch_format.get_format_string()
544
except NotImplementedError:
545
raise errors.IncompatibleFormat(branch_format, self._format)
547
self.transport.mkdir('branch')
548
except errors.FileExists:
550
return self.transport.clone('branch')
552
def get_repository_transport(self, repository_format):
553
"""See BzrDir.get_repository_transport()."""
554
if repository_format is None:
555
return self.transport.clone('repository')
557
repository_format.get_format_string()
558
except NotImplementedError:
559
raise errors.IncompatibleFormat(repository_format, self._format)
561
self.transport.mkdir('repository')
562
except errors.FileExists:
564
return self.transport.clone('repository')
566
def get_workingtree_transport(self, workingtree_format):
567
"""See BzrDir.get_workingtree_transport()."""
568
if workingtree_format is None:
569
return self.transport.clone('checkout')
571
workingtree_format.get_format_string()
572
except NotImplementedError:
573
raise errors.IncompatibleFormat(workingtree_format, self._format)
575
self.transport.mkdir('checkout')
576
except errors.FileExists:
578
return self.transport.clone('checkout')
580
def open_branch(self, unsupported=False):
581
"""See BzrDir.open_branch."""
582
from bzrlib.branch import BranchFormat
583
format = BranchFormat.find_format(self)
584
self._check_supported(format, unsupported)
585
return format.open(self, _found=True)
587
def open_repository(self, unsupported=False):
588
"""See BzrDir.open_repository."""
589
from bzrlib.repository import RepositoryFormat
590
format = RepositoryFormat.find_format(self)
591
self._check_supported(format, unsupported)
592
return format.open(self, _found=True)
594
def open_workingtree(self, unsupported=False):
595
"""See BzrDir.open_workingtree."""
596
from bzrlib.workingtree import WorkingTreeFormat
597
format = WorkingTreeFormat.find_format(self)
598
self._check_supported(format, unsupported)
599
return format.open(self, _found=True)
602
class BzrDirFormat(object):
603
"""An encapsulation of the initialization and open routines for a format.
605
Formats provide three things:
606
* An initialization routine,
610
Formats are placed in an dict by their format string for reference
611
during bzrdir opening. These should be subclasses of BzrDirFormat
614
Once a format is deprecated, just deprecate the initialize and open
615
methods on the format class. Do not deprecate the object, as the
616
object will be created every system load.
619
_default_format = None
620
"""The default format used for new .bzr dirs."""
623
"""The known formats."""
626
def find_format(klass, transport):
627
"""Return the format registered for URL."""
629
format_string = transport.get(".bzr/branch-format").read()
630
return klass._formats[format_string]
631
except errors.NoSuchFile:
632
raise errors.NotBranchError(path=transport.base)
634
raise errors.UnknownFormatError(format_string)
637
def get_default_format(klass):
638
"""Return the current default format."""
639
return klass._default_format
641
def get_format_string(self):
642
"""Return the ASCII format string that identifies this format."""
643
raise NotImplementedError(self.get_format_string)
645
def initialize(self, url):
646
"""Create a bzr control dir at this url and return an opened copy."""
647
# Since we don't have a .bzr directory, inherit the
648
# mode from the root directory
649
t = get_transport(url)
650
temp_control = LockableFiles(t, '')
651
temp_control._transport.mkdir('.bzr',
652
# FIXME: RBC 20060121 dont peek under
654
mode=temp_control._dir_mode)
655
file_mode = temp_control._file_mode
657
mutter('created control directory in ' + t.base)
658
control = t.clone('.bzr')
659
lock_file = 'branch-lock'
660
utf8_files = [('README',
661
"This is a Bazaar-NG control directory.\n"
662
"Do not change any files in this directory.\n"),
663
('branch-format', self.get_format_string()),
665
# NB: no need to escape relative paths that are url safe.
666
control.put(lock_file, StringIO(), mode=file_mode)
667
control_files = LockableFiles(control, lock_file)
668
control_files.lock_write()
670
for file, content in utf8_files:
671
control_files.put_utf8(file, content)
673
control_files.unlock()
674
return self.open(t, _found=True)
676
def is_supported(self):
677
"""Is this format supported?
679
Supported formats must be initializable and openable.
680
Unsupported formats may not support initialization or committing or
681
some other features depending on the reason for not being supported.
685
def open(self, transport, _found=False):
686
"""Return an instance of this format for the dir transport points at.
688
_found is a private parameter, do not use it.
691
assert isinstance(BzrDirFormat.find_format(transport),
693
return self._open(transport)
695
def _open(self, transport):
696
"""Template method helper for opening BzrDirectories.
698
This performs the actual open and any additional logic or parameter
701
raise NotImplementedError(self._open)
704
def register_format(klass, format):
705
klass._formats[format.get_format_string()] = format
708
def set_default_format(klass, format):
709
klass._default_format = format
712
def unregister_format(klass, format):
713
assert klass._formats[format.get_format_string()] is format
714
del klass._formats[format.get_format_string()]
717
class BzrDirFormat4(BzrDirFormat):
720
This format is a combined format for working tree, branch and repository.
722
- Format 1 working trees [always]
723
- Format 4 branches [always]
724
- Format 4 repositories [always]
726
This format is deprecated: it indexes texts using a text it which is
727
removed in format 5; write support for this format has been removed.
730
def get_format_string(self):
731
"""See BzrDirFormat.get_format_string()."""
732
return "Bazaar-NG branch, format 0.0.4\n"
734
def initialize(self, url):
735
"""Format 4 branches cannot be created."""
736
raise errors.UninitializableFormat(self)
738
def is_supported(self):
739
"""Format 4 is not supported.
741
It is not supported because the model changed from 4 to 5 and the
742
conversion logic is expensive - so doing it on the fly was not
747
def _open(self, transport):
748
"""See BzrDirFormat._open."""
749
return BzrDir4(transport, self)
752
class BzrDirFormat5(BzrDirFormat):
753
"""Bzr control format 5.
755
This format is a combined format for working tree, branch and repository.
757
- Format 2 working trees [always]
758
- Format 4 branches [always]
759
- Format 6 repositories [always]
760
Unhashed stores in the repository.
763
def get_format_string(self):
764
"""See BzrDirFormat.get_format_string()."""
765
return "Bazaar-NG branch, format 5\n"
767
def initialize(self, url, _cloning=False):
768
"""Format 5 dirs always have working tree, branch and repository.
770
Except when they are being cloned.
772
from bzrlib.branch import BzrBranchFormat4
773
from bzrlib.repository import RepositoryFormat5
774
from bzrlib.workingtree import WorkingTreeFormat2
775
result = super(BzrDirFormat5, self).initialize(url)
776
RepositoryFormat5().initialize(result, _internal=True)
778
BzrBranchFormat4().initialize(result)
779
WorkingTreeFormat2().initialize(result)
782
def _open(self, transport):
783
"""See BzrDirFormat._open."""
784
return BzrDir5(transport, self)
787
class BzrDirFormat6(BzrDirFormat):
788
"""Bzr control format 6.
790
This format is a combined format for working tree, branch and repository.
792
- Format 2 working trees [always]
793
- Format 4 branches [always]
794
- Format 6 repositories [always]
797
def get_format_string(self):
798
"""See BzrDirFormat.get_format_string()."""
799
return "Bazaar-NG branch, format 6\n"
801
def initialize(self, url, _cloning=False):
802
"""Format 6 dirs always have working tree, branch and repository.
804
Except when they are being cloned.
806
from bzrlib.branch import BzrBranchFormat4
807
from bzrlib.repository import RepositoryFormat6
808
from bzrlib.workingtree import WorkingTreeFormat2
809
result = super(BzrDirFormat6, self).initialize(url)
810
RepositoryFormat6().initialize(result, _internal=True)
812
BzrBranchFormat4().initialize(result)
814
WorkingTreeFormat2().initialize(result)
815
except errors.NotLocalUrl:
816
# emulate pre-check behaviour for working tree and silently
821
def _open(self, transport):
822
"""See BzrDirFormat._open."""
823
return BzrDir6(transport, self)
826
class BzrDirMetaFormat1(BzrDirFormat):
827
"""Bzr meta control format 1
829
This is the first format with split out working tree, branch and repository
832
- Format 3 working trees [optional]
833
- Format 5 branches [optional]
834
- Format 7 repositories [optional]
837
def get_format_string(self):
838
"""See BzrDirFormat.get_format_string()."""
839
return "Bazaar-NG meta directory, format 1\n"
841
def _open(self, transport):
842
"""See BzrDirFormat._open."""
843
return BzrDirMeta1(transport, self)
846
BzrDirFormat.register_format(BzrDirFormat4())
847
BzrDirFormat.register_format(BzrDirFormat5())
848
BzrDirFormat.register_format(BzrDirMetaFormat1())
849
__default_format = BzrDirFormat6()
850
BzrDirFormat.register_format(__default_format)
851
BzrDirFormat.set_default_format(__default_format)
854
class BzrDirTestProviderAdapter(object):
855
"""A tool to generate a suite testing multiple bzrdir formats at once.
857
This is done by copying the test once for each transport and injecting
858
the transport_server, transport_readonly_server, and bzrdir_format
859
classes into each copy. Each copy is also given a new id() to make it
863
def __init__(self, transport_server, transport_readonly_server, formats):
864
self._transport_server = transport_server
865
self._transport_readonly_server = transport_readonly_server
866
self._formats = formats
868
def adapt(self, test):
870
for format in self._formats:
871
new_test = deepcopy(test)
872
new_test.transport_server = self._transport_server
873
new_test.transport_readonly_server = self._transport_readonly_server
874
new_test.bzrdir_format = format
875
def make_new_test_id():
876
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
877
return lambda: new_id
878
new_test.id = make_new_test_id()
879
result.addTest(new_test)
883
class ScratchDir(BzrDir6):
884
"""Special test class: a bzrdir that cleans up itself..
887
>>> base = d.transport.base
890
>>> b.transport.__del__()
895
def __init__(self, files=[], dirs=[], transport=None):
896
"""Make a test branch.
898
This creates a temporary directory and runs init-tree in it.
900
If any files are listed, they are created in the working copy.
902
if transport is None:
903
transport = bzrlib.transport.local.ScratchTransport()
904
# local import for scope restriction
905
BzrDirFormat6().initialize(transport.base)
906
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
907
self.create_repository()
909
self.create_workingtree()
911
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
913
# BzrBranch creates a clone to .bzr and then forgets about the
914
# original transport. A ScratchTransport() deletes itself and
915
# everything underneath it when it goes away, so we need to
916
# grab a local copy to prevent that from happening
917
self._transport = transport
920
self._transport.mkdir(d)
923
self._transport.put(f, 'content of %s' % f)
927
>>> orig = ScratchDir(files=["file1", "file2"])
928
>>> os.listdir(orig.base)
929
[u'.bzr', u'file1', u'file2']
930
>>> clone = orig.clone()
931
>>> if os.name != 'nt':
932
... os.path.samefile(orig.base, clone.base)
934
... orig.base == clone.base
937
>>> os.listdir(clone.base)
938
[u'.bzr', u'file1', u'file2']
940
from shutil import copytree
941
from bzrlib.osutils import mkdtemp
944
copytree(self.base, base, symlinks=True)
946
transport=bzrlib.transport.local.ScratchTransport(base))