1
# Copyright (C) 2005 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
19
At format 7 this was split out into Branch, Repository and Checkout control
23
from copy import deepcopy
24
from cStringIO import StringIO
25
from unittest import TestSuite
29
import bzrlib.errors as errors
30
from bzrlib.lockable_files import LockableFiles
31
from bzrlib.osutils import safe_unicode
32
from bzrlib.trace import mutter
33
from bzrlib.symbol_versioning import *
34
from bzrlib.transport import get_transport
35
from bzrlib.transport.local import LocalTransport
39
"""A .bzr control diretory.
41
BzrDir instances let you create or open any of the things that can be
42
found within .bzr - checkouts, branches and repositories.
45
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
47
a transport connected to the directory this bzr was opened from.
50
def _check_supported(self, format, allow_unsupported):
51
"""Check whether format is a supported format.
53
If allow_unsupported is True, this is a no-op.
55
if not allow_unsupported and not format.is_supported():
56
raise errors.UnsupportedFormatError(format)
58
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
59
"""Clone this bzrdir and its contents to url verbatim.
61
If urls last component does not exist, it will be created.
63
if revision_id is not None, then the clone operation may tune
64
itself to download less data.
65
:param force_new_repo: Do not use a shared repository for the target
66
even if one is available.
69
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
70
result = self._format.initialize(url)
72
local_repo = self.find_repository()
73
except errors.NoRepositoryPresent:
76
# may need to copy content in
78
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
81
result_repo = result.find_repository()
82
# fetch content this dir needs.
84
# XXX FIXME RBC 20060214 need tests for this when the basis
86
result_repo.fetch(basis_repo, revision_id=revision_id)
87
result_repo.fetch(local_repo, revision_id=revision_id)
88
except errors.NoRepositoryPresent:
89
# needed to make one anyway.
90
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
91
# 1 if there is a branch present
92
# make sure its content is available in the target repository
95
self.open_branch().clone(result, revision_id=revision_id)
96
except errors.NotBranchError:
99
self.open_workingtree().clone(result, basis=basis_tree)
100
except (errors.NotBranchError, errors.NotLocalUrl):
104
def _get_basis_components(self, basis):
105
"""Retrieve the basis components that are available at basis."""
107
return None, None, None
109
basis_tree = basis.open_workingtree()
110
basis_branch = basis_tree.branch
111
basis_repo = basis_branch.repository
112
except (errors.NoWorkingTree, errors.NotLocalUrl):
115
basis_branch = basis.open_branch()
116
basis_repo = basis_branch.repository
117
except errors.NotBranchError:
120
basis_repo = basis.open_repository()
121
except errors.NoRepositoryPresent:
123
return basis_repo, basis_branch, basis_tree
125
def _make_tail(self, url):
126
segments = url.split('/')
127
if segments and segments[-1] not in ('', '.'):
128
parent = '/'.join(segments[:-1])
129
t = bzrlib.transport.get_transport(parent)
131
t.mkdir(segments[-1])
132
except errors.FileExists:
137
"""Create a new BzrDir at the url 'base'.
139
This will call the current default formats initialize with base
140
as the only parameter.
142
If you need a specific format, consider creating an instance
143
of that and calling initialize().
145
segments = base.split('/')
146
if segments and segments[-1] not in ('', '.'):
147
parent = '/'.join(segments[:-1])
148
t = bzrlib.transport.get_transport(parent)
150
t.mkdir(segments[-1])
151
except errors.FileExists:
153
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
155
def create_branch(self):
156
"""Create a branch in this BzrDir.
158
The bzrdirs format will control what branch format is created.
159
For more control see BranchFormatXX.create(a_bzrdir).
161
raise NotImplementedError(self.create_branch)
164
def create_branch_and_repo(base, force_new_repo=False):
165
"""Create a new BzrDir, Branch and Repository at the url 'base'.
167
This will use the current default BzrDirFormat, and use whatever
168
repository format that that uses via bzrdir.create_branch and
169
create_repository. If a shared repository is available that is used
172
The created Branch object is returned.
174
:param base: The URL to create the branch at.
175
:param force_new_repo: If True a new repository is always created.
177
bzrdir = BzrDir.create(base)
179
bzrdir.create_repository()
181
repo = bzrdir.find_repository()
182
except errors.NoRepositoryPresent:
183
bzrdir.create_repository()
184
return bzrdir.create_branch()
187
def create_repository(base, shared=False):
188
"""Create a new BzrDir and Repository at the url 'base'.
190
This will use the current default BzrDirFormat, and use whatever
191
repository format that that uses for bzrdirformat.create_repository.
193
;param shared: Create a shared repository rather than a standalone
195
The Repository object is returned.
197
This must be overridden as an instance method in child classes, where
198
it should take no parameters and construct whatever repository format
199
that child class desires.
201
bzrdir = BzrDir.create(base)
202
return bzrdir.create_repository()
205
def create_standalone_workingtree(base):
206
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
208
'base' must be a local path or a file:// url.
210
This will use the current default BzrDirFormat, and use whatever
211
repository format that that uses for bzrdirformat.create_workingtree,
212
create_branch and create_repository.
214
The WorkingTree object is returned.
216
t = get_transport(safe_unicode(base))
217
if not isinstance(t, LocalTransport):
218
raise errors.NotLocalUrl(base)
219
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base)).bzrdir
220
return bzrdir.create_workingtree()
222
def create_workingtree(self):
223
"""Create a working tree at this BzrDir"""
224
raise NotImplementedError(self.create_workingtree)
226
def find_repository(self):
227
"""Find the repository that should be used for a_bzrdir.
229
This does not require a branch as we use it to find the repo for
230
new branches as well as to hook existing branches up to their
234
return self.open_repository()
235
except errors.NoRepositoryPresent:
237
next_transport = self.root_transport.clone('..')
240
found_bzrdir = BzrDir.open_containing_transport(
242
except errors.NotBranchError:
243
raise errors.NoRepositoryPresent(self)
245
repository = found_bzrdir.open_repository()
246
except errors.NoRepositoryPresent:
247
next_transport = found_bzrdir.root_transport.clone('..')
249
if ((found_bzrdir.root_transport.base ==
250
self.root_transport.base) or repository.is_shared()):
253
raise errors.NoRepositoryPresent(self)
254
raise errors.NoRepositoryPresent(self)
256
def get_branch_transport(self, branch_format):
257
"""Get the transport for use by branch format in this BzrDir.
259
Note that bzr dirs that do not support format strings will raise
260
IncompatibleFormat if the branch format they are given has
261
a format string, and vice verca.
263
If branch_format is None, the transport is returned with no
264
checking. if it is not None, then the returned transport is
265
guaranteed to point to an existing directory ready for use.
267
raise NotImplementedError(self.get_branch_transport)
269
def get_repository_transport(self, repository_format):
270
"""Get the transport for use by repository format in this BzrDir.
272
Note that bzr dirs that do not support format strings will raise
273
IncompatibleFormat if the repository format they are given has
274
a format string, and vice verca.
276
If repository_format is None, the transport is returned with no
277
checking. if it is not None, then the returned transport is
278
guaranteed to point to an existing directory ready for use.
280
raise NotImplementedError(self.get_repository_transport)
282
def get_workingtree_transport(self, tree_format):
283
"""Get the transport for use by workingtree format in this BzrDir.
285
Note that bzr dirs that do not support format strings will raise
286
IncompatibleFormat if the workingtree format they are given has
287
a format string, and vice verca.
289
If workingtree_format is None, the transport is returned with no
290
checking. if it is not None, then the returned transport is
291
guaranteed to point to an existing directory ready for use.
293
raise NotImplementedError(self.get_workingtree_transport)
295
def __init__(self, _transport, _format):
296
"""Initialize a Bzr control dir object.
298
Only really common logic should reside here, concrete classes should be
299
made with varying behaviours.
301
:param _format: the format that is creating this BzrDir instance.
302
:param _transport: the transport this dir is based at.
304
self._format = _format
305
self.transport = _transport.clone('.bzr')
306
self.root_transport = _transport
309
def open_unsupported(base):
310
"""Open a branch which is not supported."""
311
return BzrDir.open(base, _unsupported=True)
314
def open(base, _unsupported=False):
315
"""Open an existing bzrdir, rooted at 'base' (url)
317
_unsupported is a private parameter to the BzrDir class.
319
t = get_transport(base)
320
mutter("trying to open %r with transport %r", base, t)
321
format = BzrDirFormat.find_format(t)
322
if not _unsupported and not format.is_supported():
323
# see open_downlevel to open legacy branches.
324
raise errors.UnsupportedFormatError(
325
'sorry, format %s not supported' % format,
326
['use a different bzr version',
327
'or remove the .bzr directory'
328
' and "bzr init" again'])
329
return format.open(t, _found=True)
331
def open_branch(self, unsupported=False):
332
"""Open the branch object at this BzrDir if one is present.
334
If unsupported is True, then no longer supported branch formats can
337
TODO: static convenience version of this?
339
raise NotImplementedError(self.open_branch)
342
def open_containing(url):
343
"""Open an existing branch which contains url.
345
:param url: url to search from.
346
See open_containing_transport for more detail.
348
return BzrDir.open_containing_transport(get_transport(url))
351
def open_containing_transport(a_transport):
352
"""Open an existing branch which contains a_transport.base
354
This probes for a branch at a_transport, and searches upwards from there.
356
Basically we keep looking up until we find the control directory or
357
run into the root. If there isn't one, raises NotBranchError.
358
If there is one and it is either an unrecognised format or an unsupported
359
format, UnknownFormatError or UnsupportedFormatError are raised.
360
If there is one, it is returned, along with the unused portion of url.
362
# this gets the normalised url back. I.e. '.' -> the full path.
363
url = a_transport.base
366
format = BzrDirFormat.find_format(a_transport)
367
return format.open(a_transport), a_transport.relpath(url)
368
except errors.NotBranchError, e:
369
mutter('not a branch in: %r %s', a_transport.base, e)
370
new_t = a_transport.clone('..')
371
if new_t.base == a_transport.base:
372
# reached the root, whatever that may be
373
raise errors.NotBranchError(path=url)
376
def open_repository(self, _unsupported=False):
377
"""Open the repository object at this BzrDir if one is present.
379
This will not follow the Branch object pointer - its strictly a direct
380
open facility. Most client code should use open_branch().repository to
383
_unsupported is a private parameter, not part of the api.
384
TODO: static convenience version of this?
386
raise NotImplementedError(self.open_repository)
388
def open_workingtree(self, _unsupported=False):
389
"""Open the workingtree object at this BzrDir if one is present.
391
TODO: static convenience version of this?
393
raise NotImplementedError(self.open_workingtree)
395
def sprout(self, url, revision_id=None, basis=None):
396
"""Create a copy of this bzrdir prepared for use as a new line of
399
If urls last component does not exist, it will be created.
401
Attributes related to the identity of the source branch like
402
branch nickname will be cleaned, a working tree is created
403
whether one existed before or not; and a local branch is always
406
if revision_id is not None, then the clone operation may tune
407
itself to download less data.
410
result = self._format.initialize(url)
411
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
413
source_branch = self.open_branch()
414
source_repository = source_branch.repository
415
except errors.NotBranchError:
418
source_repository = self.open_repository()
419
except errors.NoRepositoryPresent:
420
# copy the basis one if there is one
421
source_repository = basis_repo
422
if source_repository is not None:
423
source_repository.clone(result,
424
revision_id=revision_id,
427
# no repo available, make a new one
428
result.create_repository()
429
if source_branch is not None:
430
source_branch.sprout(result, revision_id=revision_id)
432
result.create_branch()
434
self.open_workingtree().clone(result,
435
revision_id=revision_id,
437
except (errors.NotBranchError, errors.NotLocalUrl):
438
result.create_workingtree()
442
class BzrDirPreSplitOut(BzrDir):
443
"""A common class for the all-in-one formats."""
445
def clone(self, url, revision_id=None, basis=None):
446
"""See BzrDir.clone()."""
447
from bzrlib.workingtree import WorkingTreeFormat2
449
result = self._format.initialize(url, _cloning=True)
450
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
451
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
452
self.open_branch().clone(result, revision_id=revision_id)
454
self.open_workingtree().clone(result, basis=basis_tree)
455
except errors.NotLocalUrl:
456
# make a new one, this format always has to have one.
457
WorkingTreeFormat2().initialize(result)
460
def create_branch(self):
461
"""See BzrDir.create_branch."""
462
return self.open_branch()
464
def create_repository(self, shared=False):
465
"""See BzrDir.create_repository."""
467
raise errors.IncompatibleFormat('shared repository', self._format)
468
return self.open_repository()
470
def create_workingtree(self):
471
"""See BzrDir.create_workingtree."""
472
return self.open_workingtree()
474
def get_branch_transport(self, branch_format):
475
"""See BzrDir.get_branch_transport()."""
476
if branch_format is None:
477
return self.transport
479
branch_format.get_format_string()
480
except NotImplementedError:
481
return self.transport
482
raise errors.IncompatibleFormat(branch_format, self._format)
484
def get_repository_transport(self, repository_format):
485
"""See BzrDir.get_repository_transport()."""
486
if repository_format is None:
487
return self.transport
489
repository_format.get_format_string()
490
except NotImplementedError:
491
return self.transport
492
raise errors.IncompatibleFormat(repository_format, self._format)
494
def get_workingtree_transport(self, workingtree_format):
495
"""See BzrDir.get_workingtree_transport()."""
496
if workingtree_format is None:
497
return self.transport
499
workingtree_format.get_format_string()
500
except NotImplementedError:
501
return self.transport
502
raise errors.IncompatibleFormat(workingtree_format, self._format)
504
def open_branch(self, unsupported=False):
505
"""See BzrDir.open_branch."""
506
from bzrlib.branch import BzrBranchFormat4
507
format = BzrBranchFormat4()
508
self._check_supported(format, unsupported)
509
return format.open(self, _found=True)
511
def sprout(self, url, revision_id=None, basis=None):
512
"""See BzrDir.sprout()."""
513
from bzrlib.workingtree import WorkingTreeFormat2
515
result = self._format.initialize(url, _cloning=True)
516
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
518
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
519
except errors.NoRepositoryPresent:
522
self.open_branch().sprout(result, revision_id=revision_id)
523
except errors.NotBranchError:
526
self.open_workingtree().clone(result, basis=basis_tree)
527
except (errors.NotBranchError, errors.NotLocalUrl):
528
# we always want a working tree
529
WorkingTreeFormat2().initialize(result)
533
class BzrDir4(BzrDirPreSplitOut):
534
"""A .bzr version 4 control object."""
536
def create_repository(self, shared=False):
537
"""See BzrDir.create_repository."""
538
from bzrlib.repository import RepositoryFormat4
539
return RepositoryFormat4().initialize(self, shared)
541
def open_repository(self):
542
"""See BzrDir.open_repository."""
543
from bzrlib.repository import RepositoryFormat4
544
return RepositoryFormat4().open(self, _found=True)
547
class BzrDir5(BzrDirPreSplitOut):
548
"""A .bzr version 5 control object."""
550
def open_repository(self):
551
"""See BzrDir.open_repository."""
552
from bzrlib.repository import RepositoryFormat5
553
return RepositoryFormat5().open(self, _found=True)
555
def open_workingtree(self, _unsupported=False):
556
"""See BzrDir.create_workingtree."""
557
from bzrlib.workingtree import WorkingTreeFormat2
558
return WorkingTreeFormat2().open(self, _found=True)
561
class BzrDir6(BzrDirPreSplitOut):
562
"""A .bzr version 6 control object."""
564
def open_repository(self):
565
"""See BzrDir.open_repository."""
566
from bzrlib.repository import RepositoryFormat6
567
return RepositoryFormat6().open(self, _found=True)
569
def open_workingtree(self, _unsupported=False):
570
"""See BzrDir.create_workingtree."""
571
from bzrlib.workingtree import WorkingTreeFormat2
572
return WorkingTreeFormat2().open(self, _found=True)
575
class BzrDirMeta1(BzrDir):
576
"""A .bzr meta version 1 control object.
578
This is the first control object where the
579
individual formats are really split out.
582
def create_branch(self):
583
"""See BzrDir.create_branch."""
584
from bzrlib.branch import BranchFormat
585
return BranchFormat.get_default_format().initialize(self)
587
def create_repository(self, shared=False):
588
"""See BzrDir.create_repository."""
589
from bzrlib.repository import RepositoryFormat
590
return RepositoryFormat.get_default_format().initialize(self, shared)
592
def create_workingtree(self):
593
"""See BzrDir.create_workingtree."""
594
from bzrlib.workingtree import WorkingTreeFormat
595
return WorkingTreeFormat.get_default_format().initialize(self)
597
def get_branch_transport(self, branch_format):
598
"""See BzrDir.get_branch_transport()."""
599
if branch_format is None:
600
return self.transport.clone('branch')
602
branch_format.get_format_string()
603
except NotImplementedError:
604
raise errors.IncompatibleFormat(branch_format, self._format)
606
self.transport.mkdir('branch')
607
except errors.FileExists:
609
return self.transport.clone('branch')
611
def get_repository_transport(self, repository_format):
612
"""See BzrDir.get_repository_transport()."""
613
if repository_format is None:
614
return self.transport.clone('repository')
616
repository_format.get_format_string()
617
except NotImplementedError:
618
raise errors.IncompatibleFormat(repository_format, self._format)
620
self.transport.mkdir('repository')
621
except errors.FileExists:
623
return self.transport.clone('repository')
625
def get_workingtree_transport(self, workingtree_format):
626
"""See BzrDir.get_workingtree_transport()."""
627
if workingtree_format is None:
628
return self.transport.clone('checkout')
630
workingtree_format.get_format_string()
631
except NotImplementedError:
632
raise errors.IncompatibleFormat(workingtree_format, self._format)
634
self.transport.mkdir('checkout')
635
except errors.FileExists:
637
return self.transport.clone('checkout')
639
def open_branch(self, unsupported=False):
640
"""See BzrDir.open_branch."""
641
from bzrlib.branch import BranchFormat
642
format = BranchFormat.find_format(self)
643
self._check_supported(format, unsupported)
644
return format.open(self, _found=True)
646
def open_repository(self, unsupported=False):
647
"""See BzrDir.open_repository."""
648
from bzrlib.repository import RepositoryFormat
649
format = RepositoryFormat.find_format(self)
650
self._check_supported(format, unsupported)
651
return format.open(self, _found=True)
653
def open_workingtree(self, unsupported=False):
654
"""See BzrDir.create_workingtree."""
655
from bzrlib.workingtree import WorkingTreeFormat
656
format = WorkingTreeFormat.find_format(self)
657
self._check_supported(format, unsupported)
658
return format.open(self, _found=True)
661
class BzrDirFormat(object):
662
"""An encapsulation of the initialization and open routines for a format.
664
Formats provide three things:
665
* An initialization routine,
669
Formats are placed in an dict by their format string for reference
670
during bzrdir opening. These should be subclasses of BzrDirFormat
673
Once a format is deprecated, just deprecate the initialize and open
674
methods on the format class. Do not deprecate the object, as the
675
object will be created every system load.
678
_default_format = None
679
"""The default format used for new .bzr dirs."""
682
"""The known formats."""
685
def find_format(klass, transport):
686
"""Return the format registered for URL."""
688
format_string = transport.get(".bzr/branch-format").read()
689
return klass._formats[format_string]
690
except errors.NoSuchFile:
691
raise errors.NotBranchError(path=transport.base)
693
raise errors.UnknownFormatError(format_string)
696
def get_default_format(klass):
697
"""Return the current default format."""
698
return klass._default_format
700
def get_format_string(self):
701
"""Return the ASCII format string that identifies this format."""
702
raise NotImplementedError(self.get_format_string)
704
def initialize(self, url):
705
"""Create a bzr control dir at this url and return an opened copy."""
706
# Since we don't have a .bzr directory, inherit the
707
# mode from the root directory
708
t = get_transport(url)
709
temp_control = LockableFiles(t, '')
710
temp_control._transport.mkdir('.bzr',
711
# FIXME: RBC 20060121 dont peek under
713
mode=temp_control._dir_mode)
714
file_mode = temp_control._file_mode
716
mutter('created control directory in ' + t.base)
717
control = t.clone('.bzr')
718
lock_file = 'branch-lock'
719
utf8_files = [('README',
720
"This is a Bazaar-NG control directory.\n"
721
"Do not change any files in this directory.\n"),
722
('branch-format', self.get_format_string()),
724
# NB: no need to escape relative paths that are url safe.
725
control.put(lock_file, StringIO(), mode=file_mode)
726
control_files = LockableFiles(control, lock_file)
727
control_files.lock_write()
729
for file, content in utf8_files:
730
control_files.put_utf8(file, content)
732
control_files.unlock()
733
return self.open(t, _found=True)
735
def is_supported(self):
736
"""Is this format supported?
738
Supported formats must be initializable and openable.
739
Unsupported formats may not support initialization or committing or
740
some other features depending on the reason for not being supported.
744
def open(self, transport, _found=False):
745
"""Return an instance of this format for the dir transport points at.
747
_found is a private parameter, do not use it.
750
assert isinstance(BzrDirFormat.find_format(transport),
752
return self._open(transport)
754
def _open(self, transport):
755
"""Template method helper for opening BzrDirectories.
757
This performs the actual open and any additional logic or parameter
760
raise NotImplementedError(self._open)
763
def register_format(klass, format):
764
klass._formats[format.get_format_string()] = format
767
def set_default_format(klass, format):
768
klass._default_format = format
771
def unregister_format(klass, format):
772
assert klass._formats[format.get_format_string()] is format
773
del klass._formats[format.get_format_string()]
776
class BzrDirFormat4(BzrDirFormat):
779
This format is a combined format for working tree, branch and repository.
781
- Format 1 working trees [always]
782
- Format 4 branches [always]
783
- Format 4 repositories [always]
785
This format is deprecated: it indexes texts using a text it which is
786
removed in format 5; write support for this format has been removed.
789
def get_format_string(self):
790
"""See BzrDirFormat.get_format_string()."""
791
return "Bazaar-NG branch, format 0.0.4\n"
793
def initialize(self, url):
794
"""Format 4 branches cannot be created."""
795
raise errors.UninitializableFormat(self)
797
def is_supported(self):
798
"""Format 4 is not supported.
800
It is not supported because the model changed from 4 to 5 and the
801
conversion logic is expensive - so doing it on the fly was not
806
def _open(self, transport):
807
"""See BzrDirFormat._open."""
808
return BzrDir4(transport, self)
811
class BzrDirFormat5(BzrDirFormat):
812
"""Bzr control format 5.
814
This format is a combined format for working tree, branch and repository.
816
- Format 2 working trees [always]
817
- Format 4 branches [always]
818
- Format 5 repositories [always]
819
Unhashed stores in the repository.
822
def get_format_string(self):
823
"""See BzrDirFormat.get_format_string()."""
824
return "Bazaar-NG branch, format 5\n"
826
def initialize(self, url, _cloning=False):
827
"""Format 5 dirs always have working tree, branch and repository.
829
Except when they are being cloned.
831
from bzrlib.branch import BzrBranchFormat4
832
from bzrlib.repository import RepositoryFormat5
833
from bzrlib.workingtree import WorkingTreeFormat2
834
result = super(BzrDirFormat5, self).initialize(url)
835
RepositoryFormat5().initialize(result, _internal=True)
837
BzrBranchFormat4().initialize(result)
838
WorkingTreeFormat2().initialize(result)
841
def _open(self, transport):
842
"""See BzrDirFormat._open."""
843
return BzrDir5(transport, self)
846
class BzrDirFormat6(BzrDirFormat):
847
"""Bzr control format 6.
849
This format is a combined format for working tree, branch and repository.
851
- Format 2 working trees [always]
852
- Format 4 branches [always]
853
- Format 6 repositories [always]
856
def get_format_string(self):
857
"""See BzrDirFormat.get_format_string()."""
858
return "Bazaar-NG branch, format 6\n"
860
def initialize(self, url, _cloning=False):
861
"""Format 6 dirs always have working tree, branch and repository.
863
Except when they are being cloned.
865
from bzrlib.branch import BzrBranchFormat4
866
from bzrlib.repository import RepositoryFormat6
867
from bzrlib.workingtree import WorkingTreeFormat2
868
result = super(BzrDirFormat6, self).initialize(url)
869
RepositoryFormat6().initialize(result, _internal=True)
871
BzrBranchFormat4().initialize(result)
873
WorkingTreeFormat2().initialize(result)
874
except errors.NotLocalUrl:
875
# emulate pre-check behaviour for working tree and silently
880
def _open(self, transport):
881
"""See BzrDirFormat._open."""
882
return BzrDir6(transport, self)
885
class BzrDirMetaFormat1(BzrDirFormat):
886
"""Bzr meta control format 1
888
This is the first format with split out working tree, branch and repository
891
- Format 3 working trees [optional]
892
- Format 5 branches [optional]
893
- Format 7 repositories [optional]
896
def get_format_string(self):
897
"""See BzrDirFormat.get_format_string()."""
898
return "Bazaar-NG meta directory, format 1\n"
900
def _open(self, transport):
901
"""See BzrDirFormat._open."""
902
return BzrDirMeta1(transport, self)
905
BzrDirFormat.register_format(BzrDirFormat4())
906
BzrDirFormat.register_format(BzrDirFormat5())
907
BzrDirFormat.register_format(BzrDirMetaFormat1())
908
__default_format = BzrDirFormat6()
909
BzrDirFormat.register_format(__default_format)
910
BzrDirFormat.set_default_format(__default_format)
913
class BzrDirTestProviderAdapter(object):
914
"""A tool to generate a suite testing multiple bzrdir formats at once.
916
This is done by copying the test once for each transport and injecting
917
the transport_server, transport_readonly_server, and bzrdir_format
918
classes into each copy. Each copy is also given a new id() to make it
922
def __init__(self, transport_server, transport_readonly_server, formats):
923
self._transport_server = transport_server
924
self._transport_readonly_server = transport_readonly_server
925
self._formats = formats
927
def adapt(self, test):
929
for format in self._formats:
930
new_test = deepcopy(test)
931
new_test.transport_server = self._transport_server
932
new_test.transport_readonly_server = self._transport_readonly_server
933
new_test.bzrdir_format = format
934
def make_new_test_id():
935
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
936
return lambda: new_id
937
new_test.id = make_new_test_id()
938
result.addTest(new_test)
942
class ScratchDir(BzrDir6):
943
"""Special test class: a bzrdir that cleans up itself..
946
>>> base = d.transport.base
949
>>> b.transport.__del__()
954
def __init__(self, files=[], dirs=[], transport=None):
955
"""Make a test branch.
957
This creates a temporary directory and runs init-tree in it.
959
If any files are listed, they are created in the working copy.
961
if transport is None:
962
transport = bzrlib.transport.local.ScratchTransport()
963
# local import for scope restriction
964
BzrDirFormat6().initialize(transport.base)
965
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
966
self.create_repository()
968
self.create_workingtree()
970
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
972
# BzrBranch creates a clone to .bzr and then forgets about the
973
# original transport. A ScratchTransport() deletes itself and
974
# everything underneath it when it goes away, so we need to
975
# grab a local copy to prevent that from happening
976
self._transport = transport
979
self._transport.mkdir(d)
982
self._transport.put(f, 'content of %s' % f)
986
>>> orig = ScratchDir(files=["file1", "file2"])
987
>>> os.listdir(orig.base)
988
[u'.bzr', u'file1', u'file2']
989
>>> clone = orig.clone()
990
>>> if os.name != 'nt':
991
... os.path.samefile(orig.base, clone.base)
993
... orig.base == clone.base
996
>>> os.listdir(clone.base)
997
[u'.bzr', u'file1', u'file2']
999
from shutil import copytree
1000
from bzrlib.osutils import mkdtemp
1003
copytree(self.base, base, symlinks=True)
1005
transport=bzrlib.transport.local.ScratchTransport(base))