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.NoWorkingTree, 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_branch_convenience(base, force_new_repo=False, force_new_tree=None):
188
"""Create a new BzrDir, Branch and Repository at the url 'base'.
190
This is a convenience function - it will use an existing repository
191
if possible, can be told explicitly whether to create a working tree or
194
This will use the current default BzrDirFormat, and use whatever
195
repository format that that uses via bzrdir.create_branch and
196
create_repository. If a shared repository is available that is used
197
preferentially. Whatever repository is used, its tree creation policy
200
The created Branch object is returned.
201
If a working tree cannot be made due to base not being a file:// url,
204
:param base: The URL to create the branch at.
205
:param force_new_repo: If True a new repository is always created.
206
:param force_new_tree: If True or False force creation of a tree or
207
prevent such creation respectively.
209
bzrdir = BzrDir.create(base)
211
bzrdir.create_repository()
213
repo = bzrdir.find_repository()
214
except errors.NoRepositoryPresent:
215
repo = bzrdir.create_repository()
216
result = bzrdir.create_branch()
217
if force_new_tree or (repo.make_working_trees() and
218
force_new_tree is None):
219
bzrdir.create_workingtree()
223
def create_repository(base, shared=False):
224
"""Create a new BzrDir and Repository at the url 'base'.
226
This will use the current default BzrDirFormat, and use whatever
227
repository format that that uses for bzrdirformat.create_repository.
229
;param shared: Create a shared repository rather than a standalone
231
The Repository object is returned.
233
This must be overridden as an instance method in child classes, where
234
it should take no parameters and construct whatever repository format
235
that child class desires.
237
bzrdir = BzrDir.create(base)
238
return bzrdir.create_repository()
241
def create_standalone_workingtree(base):
242
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
244
'base' must be a local path or a file:// url.
246
This will use the current default BzrDirFormat, and use whatever
247
repository format that that uses for bzrdirformat.create_workingtree,
248
create_branch and create_repository.
250
The WorkingTree object is returned.
252
t = get_transport(safe_unicode(base))
253
if not isinstance(t, LocalTransport):
254
raise errors.NotLocalUrl(base)
255
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
256
force_new_repo=True).bzrdir
257
return bzrdir.create_workingtree()
259
def create_workingtree(self, revision_id=None):
260
"""Create a working tree at this BzrDir.
262
revision_id: create it as of this revision id.
264
raise NotImplementedError(self.create_workingtree)
266
def find_repository(self):
267
"""Find the repository that should be used for a_bzrdir.
269
This does not require a branch as we use it to find the repo for
270
new branches as well as to hook existing branches up to their
274
return self.open_repository()
275
except errors.NoRepositoryPresent:
277
next_transport = self.root_transport.clone('..')
280
found_bzrdir = BzrDir.open_containing_transport(
282
except errors.NotBranchError:
283
raise errors.NoRepositoryPresent(self)
285
repository = found_bzrdir.open_repository()
286
except errors.NoRepositoryPresent:
287
next_transport = found_bzrdir.root_transport.clone('..')
289
if ((found_bzrdir.root_transport.base ==
290
self.root_transport.base) or repository.is_shared()):
293
raise errors.NoRepositoryPresent(self)
294
raise errors.NoRepositoryPresent(self)
296
def get_branch_transport(self, branch_format):
297
"""Get the transport for use by branch format in this BzrDir.
299
Note that bzr dirs that do not support format strings will raise
300
IncompatibleFormat if the branch format they are given has
301
a format string, and vice verca.
303
If branch_format is None, the transport is returned with no
304
checking. if it is not None, then the returned transport is
305
guaranteed to point to an existing directory ready for use.
307
raise NotImplementedError(self.get_branch_transport)
309
def get_repository_transport(self, repository_format):
310
"""Get the transport for use by repository format in this BzrDir.
312
Note that bzr dirs that do not support format strings will raise
313
IncompatibleFormat if the repository format they are given has
314
a format string, and vice verca.
316
If repository_format is None, the transport is returned with no
317
checking. if it is not None, then the returned transport is
318
guaranteed to point to an existing directory ready for use.
320
raise NotImplementedError(self.get_repository_transport)
322
def get_workingtree_transport(self, tree_format):
323
"""Get the transport for use by workingtree format in this BzrDir.
325
Note that bzr dirs that do not support format strings will raise
326
IncompatibleFormat if the workingtree format they are given has
327
a format string, and vice verca.
329
If workingtree_format is None, the transport is returned with no
330
checking. if it is not None, then the returned transport is
331
guaranteed to point to an existing directory ready for use.
333
raise NotImplementedError(self.get_workingtree_transport)
335
def __init__(self, _transport, _format):
336
"""Initialize a Bzr control dir object.
338
Only really common logic should reside here, concrete classes should be
339
made with varying behaviours.
341
:param _format: the format that is creating this BzrDir instance.
342
:param _transport: the transport this dir is based at.
344
self._format = _format
345
self.transport = _transport.clone('.bzr')
346
self.root_transport = _transport
349
def open_unsupported(base):
350
"""Open a branch which is not supported."""
351
return BzrDir.open(base, _unsupported=True)
354
def open(base, _unsupported=False):
355
"""Open an existing bzrdir, rooted at 'base' (url)
357
_unsupported is a private parameter to the BzrDir class.
359
t = get_transport(base)
360
mutter("trying to open %r with transport %r", base, t)
361
format = BzrDirFormat.find_format(t)
362
if not _unsupported and not format.is_supported():
363
# see open_downlevel to open legacy branches.
364
raise errors.UnsupportedFormatError(
365
'sorry, format %s not supported' % format,
366
['use a different bzr version',
367
'or remove the .bzr directory'
368
' and "bzr init" again'])
369
return format.open(t, _found=True)
371
def open_branch(self, unsupported=False):
372
"""Open the branch object at this BzrDir if one is present.
374
If unsupported is True, then no longer supported branch formats can
377
TODO: static convenience version of this?
379
raise NotImplementedError(self.open_branch)
382
def open_containing(url):
383
"""Open an existing branch which contains url.
385
:param url: url to search from.
386
See open_containing_transport for more detail.
388
return BzrDir.open_containing_transport(get_transport(url))
391
def open_containing_transport(a_transport):
392
"""Open an existing branch which contains a_transport.base
394
This probes for a branch at a_transport, and searches upwards from there.
396
Basically we keep looking up until we find the control directory or
397
run into the root. If there isn't one, raises NotBranchError.
398
If there is one and it is either an unrecognised format or an unsupported
399
format, UnknownFormatError or UnsupportedFormatError are raised.
400
If there is one, it is returned, along with the unused portion of url.
402
# this gets the normalised url back. I.e. '.' -> the full path.
403
url = a_transport.base
406
format = BzrDirFormat.find_format(a_transport)
407
return format.open(a_transport), a_transport.relpath(url)
408
except errors.NotBranchError, e:
409
mutter('not a branch in: %r %s', a_transport.base, e)
410
new_t = a_transport.clone('..')
411
if new_t.base == a_transport.base:
412
# reached the root, whatever that may be
413
raise errors.NotBranchError(path=url)
416
def open_repository(self, _unsupported=False):
417
"""Open the repository object at this BzrDir if one is present.
419
This will not follow the Branch object pointer - its strictly a direct
420
open facility. Most client code should use open_branch().repository to
423
_unsupported is a private parameter, not part of the api.
424
TODO: static convenience version of this?
426
raise NotImplementedError(self.open_repository)
428
def open_workingtree(self, _unsupported=False):
429
"""Open the workingtree object at this BzrDir if one is present.
431
TODO: static convenience version of this?
433
raise NotImplementedError(self.open_workingtree)
435
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
436
"""Create a copy of this bzrdir prepared for use as a new line of
439
If urls last component does not exist, it will be created.
441
Attributes related to the identity of the source branch like
442
branch nickname will be cleaned, a working tree is created
443
whether one existed before or not; and a local branch is always
446
if revision_id is not None, then the clone operation may tune
447
itself to download less data.
450
result = self._format.initialize(url)
451
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
453
source_branch = self.open_branch()
454
source_repository = source_branch.repository
455
except errors.NotBranchError:
458
source_repository = self.open_repository()
459
except errors.NoRepositoryPresent:
460
# copy the entire basis one if there is one
461
# but there is no repository.
462
source_repository = basis_repo
467
result_repo = result.find_repository()
468
except errors.NoRepositoryPresent:
470
if source_repository is None and result_repo is not None:
472
elif source_repository is None and result_repo is None:
473
# no repo available, make a new one
474
result.create_repository()
475
elif source_repository is not None and result_repo is None:
476
# have soure, and want to make a new target repo
477
source_repository.clone(result,
478
revision_id=revision_id,
481
# fetch needed content into target.
483
# XXX FIXME RBC 20060214 need tests for this when the basis
485
result_repo.fetch(basis_repo, revision_id=revision_id)
486
result_repo.fetch(source_repository, revision_id=revision_id)
487
if source_branch is not None:
488
source_branch.sprout(result, revision_id=revision_id)
490
result.create_branch()
492
self.open_workingtree().clone(result,
493
revision_id=revision_id,
495
except (errors.NoWorkingTree, errors.NotLocalUrl):
496
result.create_workingtree()
500
class BzrDirPreSplitOut(BzrDir):
501
"""A common class for the all-in-one formats."""
503
def __init__(self, _transport, _format):
504
"""See BzrDir.__init__."""
505
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
506
self._control_files = LockableFiles(self.get_branch_transport(None),
509
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
510
"""See BzrDir.clone()."""
511
from bzrlib.workingtree import WorkingTreeFormat2
513
result = self._format.initialize(url, _cloning=True)
514
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
515
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
516
self.open_branch().clone(result, revision_id=revision_id)
518
self.open_workingtree().clone(result, basis=basis_tree)
519
except errors.NotLocalUrl:
520
# make a new one, this format always has to have one.
521
WorkingTreeFormat2().initialize(result)
524
def create_branch(self):
525
"""See BzrDir.create_branch."""
526
return self.open_branch()
528
def create_repository(self, shared=False):
529
"""See BzrDir.create_repository."""
531
raise errors.IncompatibleFormat('shared repository', self._format)
532
return self.open_repository()
534
def create_workingtree(self, revision_id=None):
535
"""See BzrDir.create_workingtree."""
536
# this looks buggy but is not -really-
537
# clone and sprout will have set the revision_id
538
# and that will have set it for us, its only
539
# specific uses of create_workingtree in isolation
540
# that can do wonky stuff here, and that only
541
# happens for creating checkouts, which cannot be
542
# done on this format anyway. So - acceptable wart.
543
result = self.open_workingtree()
544
if revision_id is not None:
545
result.set_last_revision(revision_id)
548
def get_branch_transport(self, branch_format):
549
"""See BzrDir.get_branch_transport()."""
550
if branch_format is None:
551
return self.transport
553
branch_format.get_format_string()
554
except NotImplementedError:
555
return self.transport
556
raise errors.IncompatibleFormat(branch_format, self._format)
558
def get_repository_transport(self, repository_format):
559
"""See BzrDir.get_repository_transport()."""
560
if repository_format is None:
561
return self.transport
563
repository_format.get_format_string()
564
except NotImplementedError:
565
return self.transport
566
raise errors.IncompatibleFormat(repository_format, self._format)
568
def get_workingtree_transport(self, workingtree_format):
569
"""See BzrDir.get_workingtree_transport()."""
570
if workingtree_format is None:
571
return self.transport
573
workingtree_format.get_format_string()
574
except NotImplementedError:
575
return self.transport
576
raise errors.IncompatibleFormat(workingtree_format, self._format)
578
def open_branch(self, unsupported=False):
579
"""See BzrDir.open_branch."""
580
from bzrlib.branch import BzrBranchFormat4
581
format = BzrBranchFormat4()
582
self._check_supported(format, unsupported)
583
return format.open(self, _found=True)
585
def sprout(self, url, revision_id=None, basis=None):
586
"""See BzrDir.sprout()."""
587
from bzrlib.workingtree import WorkingTreeFormat2
589
result = self._format.initialize(url, _cloning=True)
590
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
592
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
593
except errors.NoRepositoryPresent:
596
self.open_branch().sprout(result, revision_id=revision_id)
597
except errors.NotBranchError:
600
self.open_workingtree().clone(result, basis=basis_tree)
601
except (errors.NotBranchError, errors.NotLocalUrl):
602
# we always want a working tree
603
WorkingTreeFormat2().initialize(result)
607
class BzrDir4(BzrDirPreSplitOut):
608
"""A .bzr version 4 control object.
610
This is a deprecated format and may be removed after sept 2006.
613
def create_repository(self, shared=False):
614
"""See BzrDir.create_repository."""
615
from bzrlib.repository import RepositoryFormat4
616
return RepositoryFormat4().initialize(self, shared)
618
def open_repository(self):
619
"""See BzrDir.open_repository."""
620
from bzrlib.repository import RepositoryFormat4
621
return RepositoryFormat4().open(self, _found=True)
624
class BzrDir5(BzrDirPreSplitOut):
625
"""A .bzr version 5 control object.
627
This is a deprecated format and may be removed after sept 2006.
630
def open_repository(self):
631
"""See BzrDir.open_repository."""
632
from bzrlib.repository import RepositoryFormat5
633
return RepositoryFormat5().open(self, _found=True)
635
def open_workingtree(self, _unsupported=False):
636
"""See BzrDir.create_workingtree."""
637
from bzrlib.workingtree import WorkingTreeFormat2
638
return WorkingTreeFormat2().open(self, _found=True)
641
class BzrDir6(BzrDirPreSplitOut):
642
"""A .bzr version 6 control object.
644
This is a deprecated format and may be removed after sept 2006.
647
def open_repository(self):
648
"""See BzrDir.open_repository."""
649
from bzrlib.repository import RepositoryFormat6
650
return RepositoryFormat6().open(self, _found=True)
652
def open_workingtree(self, _unsupported=False):
653
"""See BzrDir.create_workingtree."""
654
from bzrlib.workingtree import WorkingTreeFormat2
655
return WorkingTreeFormat2().open(self, _found=True)
658
class BzrDirMeta1(BzrDir):
659
"""A .bzr meta version 1 control object.
661
This is the first control object where the
662
individual formats are really split out.
665
def create_branch(self):
666
"""See BzrDir.create_branch."""
667
from bzrlib.branch import BranchFormat
668
return BranchFormat.get_default_format().initialize(self)
670
def create_repository(self, shared=False):
671
"""See BzrDir.create_repository."""
672
from bzrlib.repository import RepositoryFormat
673
return RepositoryFormat.get_default_format().initialize(self, shared)
675
def create_workingtree(self, revision_id=None):
676
"""See BzrDir.create_workingtree."""
677
from bzrlib.workingtree import WorkingTreeFormat
678
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
680
def get_branch_transport(self, branch_format):
681
"""See BzrDir.get_branch_transport()."""
682
if branch_format is None:
683
return self.transport.clone('branch')
685
branch_format.get_format_string()
686
except NotImplementedError:
687
raise errors.IncompatibleFormat(branch_format, self._format)
689
self.transport.mkdir('branch')
690
except errors.FileExists:
692
return self.transport.clone('branch')
694
def get_repository_transport(self, repository_format):
695
"""See BzrDir.get_repository_transport()."""
696
if repository_format is None:
697
return self.transport.clone('repository')
699
repository_format.get_format_string()
700
except NotImplementedError:
701
raise errors.IncompatibleFormat(repository_format, self._format)
703
self.transport.mkdir('repository')
704
except errors.FileExists:
706
return self.transport.clone('repository')
708
def get_workingtree_transport(self, workingtree_format):
709
"""See BzrDir.get_workingtree_transport()."""
710
if workingtree_format is None:
711
return self.transport.clone('checkout')
713
workingtree_format.get_format_string()
714
except NotImplementedError:
715
raise errors.IncompatibleFormat(workingtree_format, self._format)
717
self.transport.mkdir('checkout')
718
except errors.FileExists:
720
return self.transport.clone('checkout')
722
def open_branch(self, unsupported=False):
723
"""See BzrDir.open_branch."""
724
from bzrlib.branch import BranchFormat
725
format = BranchFormat.find_format(self)
726
self._check_supported(format, unsupported)
727
return format.open(self, _found=True)
729
def open_repository(self, unsupported=False):
730
"""See BzrDir.open_repository."""
731
from bzrlib.repository import RepositoryFormat
732
format = RepositoryFormat.find_format(self)
733
self._check_supported(format, unsupported)
734
return format.open(self, _found=True)
736
def open_workingtree(self, unsupported=False):
737
"""See BzrDir.open_workingtree."""
738
from bzrlib.workingtree import WorkingTreeFormat
739
format = WorkingTreeFormat.find_format(self)
740
self._check_supported(format, unsupported)
741
return format.open(self, _found=True)
744
class BzrDirFormat(object):
745
"""An encapsulation of the initialization and open routines for a format.
747
Formats provide three things:
748
* An initialization routine,
752
Formats are placed in an dict by their format string for reference
753
during bzrdir opening. These should be subclasses of BzrDirFormat
756
Once a format is deprecated, just deprecate the initialize and open
757
methods on the format class. Do not deprecate the object, as the
758
object will be created every system load.
761
_default_format = None
762
"""The default format used for new .bzr dirs."""
765
"""The known formats."""
768
def find_format(klass, transport):
769
"""Return the format registered for URL."""
771
format_string = transport.get(".bzr/branch-format").read()
772
return klass._formats[format_string]
773
except errors.NoSuchFile:
774
raise errors.NotBranchError(path=transport.base)
776
raise errors.UnknownFormatError(format_string)
779
def get_default_format(klass):
780
"""Return the current default format."""
781
return klass._default_format
783
def get_format_string(self):
784
"""Return the ASCII format string that identifies this format."""
785
raise NotImplementedError(self.get_format_string)
787
def initialize(self, url):
788
"""Create a bzr control dir at this url and return an opened copy."""
789
# Since we don't have a .bzr directory, inherit the
790
# mode from the root directory
791
t = get_transport(url)
792
temp_control = LockableFiles(t, '')
793
temp_control._transport.mkdir('.bzr',
794
# FIXME: RBC 20060121 dont peek under
796
mode=temp_control._dir_mode)
797
file_mode = temp_control._file_mode
799
mutter('created control directory in ' + t.base)
800
control = t.clone('.bzr')
801
lock_file = 'branch-lock'
802
utf8_files = [('README',
803
"This is a Bazaar-NG control directory.\n"
804
"Do not change any files in this directory.\n"),
805
('branch-format', self.get_format_string()),
807
# NB: no need to escape relative paths that are url safe.
808
control.put(lock_file, StringIO(), mode=file_mode)
809
control_files = LockableFiles(control, lock_file)
810
control_files.lock_write()
812
for file, content in utf8_files:
813
control_files.put_utf8(file, content)
815
control_files.unlock()
816
return self.open(t, _found=True)
818
def is_supported(self):
819
"""Is this format supported?
821
Supported formats must be initializable and openable.
822
Unsupported formats may not support initialization or committing or
823
some other features depending on the reason for not being supported.
827
def open(self, transport, _found=False):
828
"""Return an instance of this format for the dir transport points at.
830
_found is a private parameter, do not use it.
833
assert isinstance(BzrDirFormat.find_format(transport),
835
return self._open(transport)
837
def _open(self, transport):
838
"""Template method helper for opening BzrDirectories.
840
This performs the actual open and any additional logic or parameter
843
raise NotImplementedError(self._open)
846
def register_format(klass, format):
847
klass._formats[format.get_format_string()] = format
850
def set_default_format(klass, format):
851
klass._default_format = format
854
return self.get_format_string()[:-1]
857
def unregister_format(klass, format):
858
assert klass._formats[format.get_format_string()] is format
859
del klass._formats[format.get_format_string()]
862
class BzrDirFormat4(BzrDirFormat):
865
This format is a combined format for working tree, branch and repository.
867
- Format 1 working trees [always]
868
- Format 4 branches [always]
869
- Format 4 repositories [always]
871
This format is deprecated: it indexes texts using a text it which is
872
removed in format 5; write support for this format has been removed.
875
def get_format_string(self):
876
"""See BzrDirFormat.get_format_string()."""
877
return "Bazaar-NG branch, format 0.0.4\n"
879
def initialize(self, url):
880
"""Format 4 branches cannot be created."""
881
raise errors.UninitializableFormat(self)
883
def is_supported(self):
884
"""Format 4 is not supported.
886
It is not supported because the model changed from 4 to 5 and the
887
conversion logic is expensive - so doing it on the fly was not
892
def _open(self, transport):
893
"""See BzrDirFormat._open."""
894
return BzrDir4(transport, self)
897
class BzrDirFormat5(BzrDirFormat):
898
"""Bzr control format 5.
900
This format is a combined format for working tree, branch and repository.
902
- Format 2 working trees [always]
903
- Format 4 branches [always]
904
- Format 5 repositories [always]
905
Unhashed stores in the repository.
908
def get_format_string(self):
909
"""See BzrDirFormat.get_format_string()."""
910
return "Bazaar-NG branch, format 5\n"
912
def initialize(self, url, _cloning=False):
913
"""Format 5 dirs always have working tree, branch and repository.
915
Except when they are being cloned.
917
from bzrlib.branch import BzrBranchFormat4
918
from bzrlib.repository import RepositoryFormat5
919
from bzrlib.workingtree import WorkingTreeFormat2
920
result = super(BzrDirFormat5, self).initialize(url)
921
RepositoryFormat5().initialize(result, _internal=True)
923
BzrBranchFormat4().initialize(result)
924
WorkingTreeFormat2().initialize(result)
927
def _open(self, transport):
928
"""See BzrDirFormat._open."""
929
return BzrDir5(transport, self)
932
class BzrDirFormat6(BzrDirFormat):
933
"""Bzr control format 6.
935
This format is a combined format for working tree, branch and repository.
937
- Format 2 working trees [always]
938
- Format 4 branches [always]
939
- Format 6 repositories [always]
942
def get_format_string(self):
943
"""See BzrDirFormat.get_format_string()."""
944
return "Bazaar-NG branch, format 6\n"
946
def initialize(self, url, _cloning=False):
947
"""Format 6 dirs always have working tree, branch and repository.
949
Except when they are being cloned.
951
from bzrlib.branch import BzrBranchFormat4
952
from bzrlib.repository import RepositoryFormat6
953
from bzrlib.workingtree import WorkingTreeFormat2
954
result = super(BzrDirFormat6, self).initialize(url)
955
RepositoryFormat6().initialize(result, _internal=True)
957
BzrBranchFormat4().initialize(result)
959
WorkingTreeFormat2().initialize(result)
960
except errors.NotLocalUrl:
961
# emulate pre-check behaviour for working tree and silently
966
def _open(self, transport):
967
"""See BzrDirFormat._open."""
968
return BzrDir6(transport, self)
971
class BzrDirMetaFormat1(BzrDirFormat):
972
"""Bzr meta control format 1
974
This is the first format with split out working tree, branch and repository
977
- Format 3 working trees [optional]
978
- Format 5 branches [optional]
979
- Format 7 repositories [optional]
982
def get_format_string(self):
983
"""See BzrDirFormat.get_format_string()."""
984
return "Bazaar-NG meta directory, format 1\n"
986
def _open(self, transport):
987
"""See BzrDirFormat._open."""
988
return BzrDirMeta1(transport, self)
991
BzrDirFormat.register_format(BzrDirFormat4())
992
BzrDirFormat.register_format(BzrDirFormat5())
993
BzrDirFormat.register_format(BzrDirMetaFormat1())
994
__default_format = BzrDirFormat6()
995
BzrDirFormat.register_format(__default_format)
996
BzrDirFormat.set_default_format(__default_format)
999
class BzrDirTestProviderAdapter(object):
1000
"""A tool to generate a suite testing multiple bzrdir formats at once.
1002
This is done by copying the test once for each transport and injecting
1003
the transport_server, transport_readonly_server, and bzrdir_format
1004
classes into each copy. Each copy is also given a new id() to make it
1008
def __init__(self, transport_server, transport_readonly_server, formats):
1009
self._transport_server = transport_server
1010
self._transport_readonly_server = transport_readonly_server
1011
self._formats = formats
1013
def adapt(self, test):
1014
result = TestSuite()
1015
for format in self._formats:
1016
new_test = deepcopy(test)
1017
new_test.transport_server = self._transport_server
1018
new_test.transport_readonly_server = self._transport_readonly_server
1019
new_test.bzrdir_format = format
1020
def make_new_test_id():
1021
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1022
return lambda: new_id
1023
new_test.id = make_new_test_id()
1024
result.addTest(new_test)
1028
class ScratchDir(BzrDir6):
1029
"""Special test class: a bzrdir that cleans up itself..
1031
>>> d = ScratchDir()
1032
>>> base = d.transport.base
1035
>>> b.transport.__del__()
1040
def __init__(self, files=[], dirs=[], transport=None):
1041
"""Make a test branch.
1043
This creates a temporary directory and runs init-tree in it.
1045
If any files are listed, they are created in the working copy.
1047
if transport is None:
1048
transport = bzrlib.transport.local.ScratchTransport()
1049
# local import for scope restriction
1050
BzrDirFormat6().initialize(transport.base)
1051
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1052
self.create_repository()
1053
self.create_branch()
1054
self.create_workingtree()
1056
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1058
# BzrBranch creates a clone to .bzr and then forgets about the
1059
# original transport. A ScratchTransport() deletes itself and
1060
# everything underneath it when it goes away, so we need to
1061
# grab a local copy to prevent that from happening
1062
self._transport = transport
1065
self._transport.mkdir(d)
1068
self._transport.put(f, 'content of %s' % f)
1072
>>> orig = ScratchDir(files=["file1", "file2"])
1073
>>> os.listdir(orig.base)
1074
[u'.bzr', u'file1', u'file2']
1075
>>> clone = orig.clone()
1076
>>> if os.name != 'nt':
1077
... os.path.samefile(orig.base, clone.base)
1079
... orig.base == clone.base
1082
>>> os.listdir(clone.base)
1083
[u'.bzr', u'file1', u'file2']
1085
from shutil import copytree
1086
from bzrlib.osutils import mkdtemp
1089
copytree(self.base, base, symlinks=True)
1091
transport=bzrlib.transport.local.ScratchTransport(base))