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)
178
bzrdir._find_or_create_repository(force_new_repo)
179
return bzrdir.create_branch()
181
def _find_or_create_repository(self, force_new_repo):
182
"""Create a new repository if needed, returning the repository."""
184
return self.create_repository()
186
return self.find_repository()
187
except errors.NoRepositoryPresent:
188
return self.create_repository()
191
def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
192
"""Create a new BzrDir, Branch and Repository at the url 'base'.
194
This is a convenience function - it will use an existing repository
195
if possible, can be told explicitly whether to create a working tree or
198
This will use the current default BzrDirFormat, and use whatever
199
repository format that that uses via bzrdir.create_branch and
200
create_repository. If a shared repository is available that is used
201
preferentially. Whatever repository is used, its tree creation policy
204
The created Branch object is returned.
205
If a working tree cannot be made due to base not being a file:// url,
208
:param base: The URL to create the branch at.
209
:param force_new_repo: If True a new repository is always created.
210
:param force_new_tree: If True or False force creation of a tree or
211
prevent such creation respectively.
213
bzrdir = BzrDir.create(base)
214
repo = bzrdir._find_or_create_repository(force_new_repo)
215
result = bzrdir.create_branch()
216
if force_new_tree or (repo.make_working_trees() and
217
force_new_tree is None):
218
bzrdir.create_workingtree()
222
def create_repository(base, shared=False):
223
"""Create a new BzrDir and Repository at the url 'base'.
225
This will use the current default BzrDirFormat, and use whatever
226
repository format that that uses for bzrdirformat.create_repository.
228
;param shared: Create a shared repository rather than a standalone
230
The Repository object is returned.
232
This must be overridden as an instance method in child classes, where
233
it should take no parameters and construct whatever repository format
234
that child class desires.
236
bzrdir = BzrDir.create(base)
237
return bzrdir.create_repository()
240
def create_standalone_workingtree(base):
241
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
243
'base' must be a local path or a file:// url.
245
This will use the current default BzrDirFormat, and use whatever
246
repository format that that uses for bzrdirformat.create_workingtree,
247
create_branch and create_repository.
249
The WorkingTree object is returned.
251
t = get_transport(safe_unicode(base))
252
if not isinstance(t, LocalTransport):
253
raise errors.NotLocalUrl(base)
254
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
255
force_new_repo=True).bzrdir
256
return bzrdir.create_workingtree()
258
def create_workingtree(self, revision_id=None):
259
"""Create a working tree at this BzrDir.
261
revision_id: create it as of this revision id.
263
raise NotImplementedError(self.create_workingtree)
265
def find_repository(self):
266
"""Find the repository that should be used for a_bzrdir.
268
This does not require a branch as we use it to find the repo for
269
new branches as well as to hook existing branches up to their
273
return self.open_repository()
274
except errors.NoRepositoryPresent:
276
next_transport = self.root_transport.clone('..')
279
found_bzrdir = BzrDir.open_containing_from_transport(
281
except errors.NotBranchError:
282
raise errors.NoRepositoryPresent(self)
284
repository = found_bzrdir.open_repository()
285
except errors.NoRepositoryPresent:
286
next_transport = found_bzrdir.root_transport.clone('..')
288
if ((found_bzrdir.root_transport.base ==
289
self.root_transport.base) or repository.is_shared()):
292
raise errors.NoRepositoryPresent(self)
293
raise errors.NoRepositoryPresent(self)
295
def get_branch_transport(self, branch_format):
296
"""Get the transport for use by branch format in this BzrDir.
298
Note that bzr dirs that do not support format strings will raise
299
IncompatibleFormat if the branch format they are given has
300
a format string, and vice verca.
302
If branch_format is None, the transport is returned with no
303
checking. if it is not None, then the returned transport is
304
guaranteed to point to an existing directory ready for use.
306
raise NotImplementedError(self.get_branch_transport)
308
def get_repository_transport(self, repository_format):
309
"""Get the transport for use by repository format in this BzrDir.
311
Note that bzr dirs that do not support format strings will raise
312
IncompatibleFormat if the repository format they are given has
313
a format string, and vice verca.
315
If repository_format is None, the transport is returned with no
316
checking. if it is not None, then the returned transport is
317
guaranteed to point to an existing directory ready for use.
319
raise NotImplementedError(self.get_repository_transport)
321
def get_workingtree_transport(self, tree_format):
322
"""Get the transport for use by workingtree format in this BzrDir.
324
Note that bzr dirs that do not support format strings will raise
325
IncompatibleFormat if the workingtree format they are given has
326
a format string, and vice verca.
328
If workingtree_format is None, the transport is returned with no
329
checking. if it is not None, then the returned transport is
330
guaranteed to point to an existing directory ready for use.
332
raise NotImplementedError(self.get_workingtree_transport)
334
def __init__(self, _transport, _format):
335
"""Initialize a Bzr control dir object.
337
Only really common logic should reside here, concrete classes should be
338
made with varying behaviours.
340
:param _format: the format that is creating this BzrDir instance.
341
:param _transport: the transport this dir is based at.
343
self._format = _format
344
self.transport = _transport.clone('.bzr')
345
self.root_transport = _transport
348
def open_unsupported(base):
349
"""Open a branch which is not supported."""
350
return BzrDir.open(base, _unsupported=True)
353
def open(base, _unsupported=False):
354
"""Open an existing bzrdir, rooted at 'base' (url)
356
_unsupported is a private parameter to the BzrDir class.
358
t = get_transport(base)
359
mutter("trying to open %r with transport %r", base, t)
360
format = BzrDirFormat.find_format(t)
361
if not _unsupported and not format.is_supported():
362
# see open_downlevel to open legacy branches.
363
raise errors.UnsupportedFormatError(
364
'sorry, format %s not supported' % format,
365
['use a different bzr version',
366
'or remove the .bzr directory'
367
' and "bzr init" again'])
368
return format.open(t, _found=True)
370
def open_branch(self, unsupported=False):
371
"""Open the branch object at this BzrDir if one is present.
373
If unsupported is True, then no longer supported branch formats can
376
TODO: static convenience version of this?
378
raise NotImplementedError(self.open_branch)
381
def open_containing(url):
382
"""Open an existing branch which contains url.
384
:param url: url to search from.
385
See open_containing_from_transport for more detail.
387
return BzrDir.open_containing_from_transport(get_transport(url))
390
def open_containing_from_transport(a_transport):
391
"""Open an existing branch which contains a_transport.base
393
This probes for a branch at a_transport, and searches upwards from there.
395
Basically we keep looking up until we find the control directory or
396
run into the root. If there isn't one, raises NotBranchError.
397
If there is one and it is either an unrecognised format or an unsupported
398
format, UnknownFormatError or UnsupportedFormatError are raised.
399
If there is one, it is returned, along with the unused portion of url.
401
# this gets the normalised url back. I.e. '.' -> the full path.
402
url = a_transport.base
405
format = BzrDirFormat.find_format(a_transport)
406
return format.open(a_transport), a_transport.relpath(url)
407
except errors.NotBranchError, e:
408
mutter('not a branch in: %r %s', a_transport.base, e)
409
new_t = a_transport.clone('..')
410
if new_t.base == a_transport.base:
411
# reached the root, whatever that may be
412
raise errors.NotBranchError(path=url)
415
def open_repository(self, _unsupported=False):
416
"""Open the repository object at this BzrDir if one is present.
418
This will not follow the Branch object pointer - its strictly a direct
419
open facility. Most client code should use open_branch().repository to
422
_unsupported is a private parameter, not part of the api.
423
TODO: static convenience version of this?
425
raise NotImplementedError(self.open_repository)
427
def open_workingtree(self, _unsupported=False):
428
"""Open the workingtree object at this BzrDir if one is present.
430
TODO: static convenience version of this?
432
raise NotImplementedError(self.open_workingtree)
434
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
435
"""Create a copy of this bzrdir prepared for use as a new line of
438
If urls last component does not exist, it will be created.
440
Attributes related to the identity of the source branch like
441
branch nickname will be cleaned, a working tree is created
442
whether one existed before or not; and a local branch is always
445
if revision_id is not None, then the clone operation may tune
446
itself to download less data.
449
result = self._format.initialize(url)
450
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
452
source_branch = self.open_branch()
453
source_repository = source_branch.repository
454
except errors.NotBranchError:
457
source_repository = self.open_repository()
458
except errors.NoRepositoryPresent:
459
# copy the entire basis one if there is one
460
# but there is no repository.
461
source_repository = basis_repo
466
result_repo = result.find_repository()
467
except errors.NoRepositoryPresent:
469
if source_repository is None and result_repo is not None:
471
elif source_repository is None and result_repo is None:
472
# no repo available, make a new one
473
result.create_repository()
474
elif source_repository is not None and result_repo is None:
475
# have soure, and want to make a new target repo
476
source_repository.clone(result,
477
revision_id=revision_id,
480
# fetch needed content into target.
482
# XXX FIXME RBC 20060214 need tests for this when the basis
484
result_repo.fetch(basis_repo, revision_id=revision_id)
485
result_repo.fetch(source_repository, revision_id=revision_id)
486
if source_branch is not None:
487
source_branch.sprout(result, revision_id=revision_id)
489
result.create_branch()
491
self.open_workingtree().clone(result,
492
revision_id=revision_id,
494
except (errors.NoWorkingTree, errors.NotLocalUrl):
495
result.create_workingtree()
499
class BzrDirPreSplitOut(BzrDir):
500
"""A common class for the all-in-one formats."""
502
def __init__(self, _transport, _format):
503
"""See BzrDir.__init__."""
504
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
505
self._control_files = LockableFiles(self.get_branch_transport(None),
508
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
509
"""See BzrDir.clone()."""
510
from bzrlib.workingtree import WorkingTreeFormat2
512
result = self._format.initialize(url, _cloning=True)
513
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
514
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
515
self.open_branch().clone(result, revision_id=revision_id)
517
self.open_workingtree().clone(result, basis=basis_tree)
518
except errors.NotLocalUrl:
519
# make a new one, this format always has to have one.
520
WorkingTreeFormat2().initialize(result)
523
def create_branch(self):
524
"""See BzrDir.create_branch."""
525
return self.open_branch()
527
def create_repository(self, shared=False):
528
"""See BzrDir.create_repository."""
530
raise errors.IncompatibleFormat('shared repository', self._format)
531
return self.open_repository()
533
def create_workingtree(self, revision_id=None):
534
"""See BzrDir.create_workingtree."""
535
# this looks buggy but is not -really-
536
# clone and sprout will have set the revision_id
537
# and that will have set it for us, its only
538
# specific uses of create_workingtree in isolation
539
# that can do wonky stuff here, and that only
540
# happens for creating checkouts, which cannot be
541
# done on this format anyway. So - acceptable wart.
542
result = self.open_workingtree()
543
if revision_id is not None:
544
result.set_last_revision(revision_id)
547
def get_branch_transport(self, branch_format):
548
"""See BzrDir.get_branch_transport()."""
549
if branch_format is None:
550
return self.transport
552
branch_format.get_format_string()
553
except NotImplementedError:
554
return self.transport
555
raise errors.IncompatibleFormat(branch_format, self._format)
557
def get_repository_transport(self, repository_format):
558
"""See BzrDir.get_repository_transport()."""
559
if repository_format is None:
560
return self.transport
562
repository_format.get_format_string()
563
except NotImplementedError:
564
return self.transport
565
raise errors.IncompatibleFormat(repository_format, self._format)
567
def get_workingtree_transport(self, workingtree_format):
568
"""See BzrDir.get_workingtree_transport()."""
569
if workingtree_format is None:
570
return self.transport
572
workingtree_format.get_format_string()
573
except NotImplementedError:
574
return self.transport
575
raise errors.IncompatibleFormat(workingtree_format, self._format)
577
def open_branch(self, unsupported=False):
578
"""See BzrDir.open_branch."""
579
from bzrlib.branch import BzrBranchFormat4
580
format = BzrBranchFormat4()
581
self._check_supported(format, unsupported)
582
return format.open(self, _found=True)
584
def sprout(self, url, revision_id=None, basis=None):
585
"""See BzrDir.sprout()."""
586
from bzrlib.workingtree import WorkingTreeFormat2
588
result = self._format.initialize(url, _cloning=True)
589
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
591
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
592
except errors.NoRepositoryPresent:
595
self.open_branch().sprout(result, revision_id=revision_id)
596
except errors.NotBranchError:
599
self.open_workingtree().clone(result, basis=basis_tree)
600
except (errors.NotBranchError, errors.NotLocalUrl):
601
# we always want a working tree
602
WorkingTreeFormat2().initialize(result)
606
class BzrDir4(BzrDirPreSplitOut):
607
"""A .bzr version 4 control object.
609
This is a deprecated format and may be removed after sept 2006.
612
def create_repository(self, shared=False):
613
"""See BzrDir.create_repository."""
614
from bzrlib.repository import RepositoryFormat4
615
return RepositoryFormat4().initialize(self, shared)
617
def open_repository(self):
618
"""See BzrDir.open_repository."""
619
from bzrlib.repository import RepositoryFormat4
620
return RepositoryFormat4().open(self, _found=True)
623
class BzrDir5(BzrDirPreSplitOut):
624
"""A .bzr version 5 control object.
626
This is a deprecated format and may be removed after sept 2006.
629
def open_repository(self):
630
"""See BzrDir.open_repository."""
631
from bzrlib.repository import RepositoryFormat5
632
return RepositoryFormat5().open(self, _found=True)
634
def open_workingtree(self, _unsupported=False):
635
"""See BzrDir.create_workingtree."""
636
from bzrlib.workingtree import WorkingTreeFormat2
637
return WorkingTreeFormat2().open(self, _found=True)
640
class BzrDir6(BzrDirPreSplitOut):
641
"""A .bzr version 6 control object.
643
This is a deprecated format and may be removed after sept 2006.
646
def open_repository(self):
647
"""See BzrDir.open_repository."""
648
from bzrlib.repository import RepositoryFormat6
649
return RepositoryFormat6().open(self, _found=True)
651
def open_workingtree(self, _unsupported=False):
652
"""See BzrDir.create_workingtree."""
653
from bzrlib.workingtree import WorkingTreeFormat2
654
return WorkingTreeFormat2().open(self, _found=True)
657
class BzrDirMeta1(BzrDir):
658
"""A .bzr meta version 1 control object.
660
This is the first control object where the
661
individual formats are really split out.
664
def create_branch(self):
665
"""See BzrDir.create_branch."""
666
from bzrlib.branch import BranchFormat
667
return BranchFormat.get_default_format().initialize(self)
669
def create_repository(self, shared=False):
670
"""See BzrDir.create_repository."""
671
from bzrlib.repository import RepositoryFormat
672
return RepositoryFormat.get_default_format().initialize(self, shared)
674
def create_workingtree(self, revision_id=None):
675
"""See BzrDir.create_workingtree."""
676
from bzrlib.workingtree import WorkingTreeFormat
677
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
679
def get_branch_transport(self, branch_format):
680
"""See BzrDir.get_branch_transport()."""
681
if branch_format is None:
682
return self.transport.clone('branch')
684
branch_format.get_format_string()
685
except NotImplementedError:
686
raise errors.IncompatibleFormat(branch_format, self._format)
688
self.transport.mkdir('branch')
689
except errors.FileExists:
691
return self.transport.clone('branch')
693
def get_repository_transport(self, repository_format):
694
"""See BzrDir.get_repository_transport()."""
695
if repository_format is None:
696
return self.transport.clone('repository')
698
repository_format.get_format_string()
699
except NotImplementedError:
700
raise errors.IncompatibleFormat(repository_format, self._format)
702
self.transport.mkdir('repository')
703
except errors.FileExists:
705
return self.transport.clone('repository')
707
def get_workingtree_transport(self, workingtree_format):
708
"""See BzrDir.get_workingtree_transport()."""
709
if workingtree_format is None:
710
return self.transport.clone('checkout')
712
workingtree_format.get_format_string()
713
except NotImplementedError:
714
raise errors.IncompatibleFormat(workingtree_format, self._format)
716
self.transport.mkdir('checkout')
717
except errors.FileExists:
719
return self.transport.clone('checkout')
721
def open_branch(self, unsupported=False):
722
"""See BzrDir.open_branch."""
723
from bzrlib.branch import BranchFormat
724
format = BranchFormat.find_format(self)
725
self._check_supported(format, unsupported)
726
return format.open(self, _found=True)
728
def open_repository(self, unsupported=False):
729
"""See BzrDir.open_repository."""
730
from bzrlib.repository import RepositoryFormat
731
format = RepositoryFormat.find_format(self)
732
self._check_supported(format, unsupported)
733
return format.open(self, _found=True)
735
def open_workingtree(self, unsupported=False):
736
"""See BzrDir.open_workingtree."""
737
from bzrlib.workingtree import WorkingTreeFormat
738
format = WorkingTreeFormat.find_format(self)
739
self._check_supported(format, unsupported)
740
return format.open(self, _found=True)
743
class BzrDirFormat(object):
744
"""An encapsulation of the initialization and open routines for a format.
746
Formats provide three things:
747
* An initialization routine,
751
Formats are placed in an dict by their format string for reference
752
during bzrdir opening. These should be subclasses of BzrDirFormat
755
Once a format is deprecated, just deprecate the initialize and open
756
methods on the format class. Do not deprecate the object, as the
757
object will be created every system load.
760
_default_format = None
761
"""The default format used for new .bzr dirs."""
764
"""The known formats."""
767
def find_format(klass, transport):
768
"""Return the format registered for URL."""
770
format_string = transport.get(".bzr/branch-format").read()
771
return klass._formats[format_string]
772
except errors.NoSuchFile:
773
raise errors.NotBranchError(path=transport.base)
775
raise errors.UnknownFormatError(format_string)
778
def get_default_format(klass):
779
"""Return the current default format."""
780
return klass._default_format
782
def get_format_string(self):
783
"""Return the ASCII format string that identifies this format."""
784
raise NotImplementedError(self.get_format_string)
786
def initialize(self, url):
787
"""Create a bzr control dir at this url and return an opened copy."""
788
# Since we don't have a .bzr directory, inherit the
789
# mode from the root directory
790
t = get_transport(url)
791
temp_control = LockableFiles(t, '')
792
temp_control._transport.mkdir('.bzr',
793
# FIXME: RBC 20060121 dont peek under
795
mode=temp_control._dir_mode)
796
file_mode = temp_control._file_mode
798
mutter('created control directory in ' + t.base)
799
control = t.clone('.bzr')
800
lock_file = 'branch-lock'
801
utf8_files = [('README',
802
"This is a Bazaar-NG control directory.\n"
803
"Do not change any files in this directory.\n"),
804
('branch-format', self.get_format_string()),
806
# NB: no need to escape relative paths that are url safe.
807
control.put(lock_file, StringIO(), mode=file_mode)
808
control_files = LockableFiles(control, lock_file)
809
control_files.lock_write()
811
for file, content in utf8_files:
812
control_files.put_utf8(file, content)
814
control_files.unlock()
815
return self.open(t, _found=True)
817
def is_supported(self):
818
"""Is this format supported?
820
Supported formats must be initializable and openable.
821
Unsupported formats may not support initialization or committing or
822
some other features depending on the reason for not being supported.
826
def open(self, transport, _found=False):
827
"""Return an instance of this format for the dir transport points at.
829
_found is a private parameter, do not use it.
832
assert isinstance(BzrDirFormat.find_format(transport),
834
return self._open(transport)
836
def _open(self, transport):
837
"""Template method helper for opening BzrDirectories.
839
This performs the actual open and any additional logic or parameter
842
raise NotImplementedError(self._open)
845
def register_format(klass, format):
846
klass._formats[format.get_format_string()] = format
849
def set_default_format(klass, format):
850
klass._default_format = format
853
return self.get_format_string()[:-1]
856
def unregister_format(klass, format):
857
assert klass._formats[format.get_format_string()] is format
858
del klass._formats[format.get_format_string()]
861
class BzrDirFormat4(BzrDirFormat):
864
This format is a combined format for working tree, branch and repository.
866
- Format 1 working trees [always]
867
- Format 4 branches [always]
868
- Format 4 repositories [always]
870
This format is deprecated: it indexes texts using a text it which is
871
removed in format 5; write support for this format has been removed.
874
def get_format_string(self):
875
"""See BzrDirFormat.get_format_string()."""
876
return "Bazaar-NG branch, format 0.0.4\n"
878
def initialize(self, url):
879
"""Format 4 branches cannot be created."""
880
raise errors.UninitializableFormat(self)
882
def is_supported(self):
883
"""Format 4 is not supported.
885
It is not supported because the model changed from 4 to 5 and the
886
conversion logic is expensive - so doing it on the fly was not
891
def _open(self, transport):
892
"""See BzrDirFormat._open."""
893
return BzrDir4(transport, self)
896
class BzrDirFormat5(BzrDirFormat):
897
"""Bzr control format 5.
899
This format is a combined format for working tree, branch and repository.
901
- Format 2 working trees [always]
902
- Format 4 branches [always]
903
- Format 5 repositories [always]
904
Unhashed stores in the repository.
907
def get_format_string(self):
908
"""See BzrDirFormat.get_format_string()."""
909
return "Bazaar-NG branch, format 5\n"
911
def initialize(self, url, _cloning=False):
912
"""Format 5 dirs always have working tree, branch and repository.
914
Except when they are being cloned.
916
from bzrlib.branch import BzrBranchFormat4
917
from bzrlib.repository import RepositoryFormat5
918
from bzrlib.workingtree import WorkingTreeFormat2
919
result = super(BzrDirFormat5, self).initialize(url)
920
RepositoryFormat5().initialize(result, _internal=True)
922
BzrBranchFormat4().initialize(result)
923
WorkingTreeFormat2().initialize(result)
926
def _open(self, transport):
927
"""See BzrDirFormat._open."""
928
return BzrDir5(transport, self)
931
class BzrDirFormat6(BzrDirFormat):
932
"""Bzr control format 6.
934
This format is a combined format for working tree, branch and repository.
936
- Format 2 working trees [always]
937
- Format 4 branches [always]
938
- Format 6 repositories [always]
941
def get_format_string(self):
942
"""See BzrDirFormat.get_format_string()."""
943
return "Bazaar-NG branch, format 6\n"
945
def initialize(self, url, _cloning=False):
946
"""Format 6 dirs always have working tree, branch and repository.
948
Except when they are being cloned.
950
from bzrlib.branch import BzrBranchFormat4
951
from bzrlib.repository import RepositoryFormat6
952
from bzrlib.workingtree import WorkingTreeFormat2
953
result = super(BzrDirFormat6, self).initialize(url)
954
RepositoryFormat6().initialize(result, _internal=True)
956
BzrBranchFormat4().initialize(result)
958
WorkingTreeFormat2().initialize(result)
959
except errors.NotLocalUrl:
960
# emulate pre-check behaviour for working tree and silently
965
def _open(self, transport):
966
"""See BzrDirFormat._open."""
967
return BzrDir6(transport, self)
970
class BzrDirMetaFormat1(BzrDirFormat):
971
"""Bzr meta control format 1
973
This is the first format with split out working tree, branch and repository
976
- Format 3 working trees [optional]
977
- Format 5 branches [optional]
978
- Format 7 repositories [optional]
981
def get_format_string(self):
982
"""See BzrDirFormat.get_format_string()."""
983
return "Bazaar-NG meta directory, format 1\n"
985
def _open(self, transport):
986
"""See BzrDirFormat._open."""
987
return BzrDirMeta1(transport, self)
990
BzrDirFormat.register_format(BzrDirFormat4())
991
BzrDirFormat.register_format(BzrDirFormat5())
992
BzrDirFormat.register_format(BzrDirMetaFormat1())
993
__default_format = BzrDirFormat6()
994
BzrDirFormat.register_format(__default_format)
995
BzrDirFormat.set_default_format(__default_format)
998
class BzrDirTestProviderAdapter(object):
999
"""A tool to generate a suite testing multiple bzrdir formats at once.
1001
This is done by copying the test once for each transport and injecting
1002
the transport_server, transport_readonly_server, and bzrdir_format
1003
classes into each copy. Each copy is also given a new id() to make it
1007
def __init__(self, transport_server, transport_readonly_server, formats):
1008
self._transport_server = transport_server
1009
self._transport_readonly_server = transport_readonly_server
1010
self._formats = formats
1012
def adapt(self, test):
1013
result = TestSuite()
1014
for format in self._formats:
1015
new_test = deepcopy(test)
1016
new_test.transport_server = self._transport_server
1017
new_test.transport_readonly_server = self._transport_readonly_server
1018
new_test.bzrdir_format = format
1019
def make_new_test_id():
1020
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1021
return lambda: new_id
1022
new_test.id = make_new_test_id()
1023
result.addTest(new_test)
1027
class ScratchDir(BzrDir6):
1028
"""Special test class: a bzrdir that cleans up itself..
1030
>>> d = ScratchDir()
1031
>>> base = d.transport.base
1034
>>> b.transport.__del__()
1039
def __init__(self, files=[], dirs=[], transport=None):
1040
"""Make a test branch.
1042
This creates a temporary directory and runs init-tree in it.
1044
If any files are listed, they are created in the working copy.
1046
if transport is None:
1047
transport = bzrlib.transport.local.ScratchTransport()
1048
# local import for scope restriction
1049
BzrDirFormat6().initialize(transport.base)
1050
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1051
self.create_repository()
1052
self.create_branch()
1053
self.create_workingtree()
1055
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1057
# BzrBranch creates a clone to .bzr and then forgets about the
1058
# original transport. A ScratchTransport() deletes itself and
1059
# everything underneath it when it goes away, so we need to
1060
# grab a local copy to prevent that from happening
1061
self._transport = transport
1064
self._transport.mkdir(d)
1067
self._transport.put(f, 'content of %s' % f)
1071
>>> orig = ScratchDir(files=["file1", "file2"])
1072
>>> os.listdir(orig.base)
1073
[u'.bzr', u'file1', u'file2']
1074
>>> clone = orig.clone()
1075
>>> if os.name != 'nt':
1076
... os.path.samefile(orig.base, clone.base)
1078
... orig.base == clone.base
1081
>>> os.listdir(clone.base)
1082
[u'.bzr', u'file1', u'file2']
1084
from shutil import copytree
1085
from bzrlib.osutils import mkdtemp
1088
copytree(self.base, base, symlinks=True)
1090
transport=bzrlib.transport.local.ScratchTransport(base))