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_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, revision_id=None):
223
"""Create a working tree at this BzrDir.
225
revision_id: create it as of this revision id.
227
raise NotImplementedError(self.create_workingtree)
229
def find_repository(self):
230
"""Find the repository that should be used for a_bzrdir.
232
This does not require a branch as we use it to find the repo for
233
new branches as well as to hook existing branches up to their
237
return self.open_repository()
238
except errors.NoRepositoryPresent:
240
next_transport = self.root_transport.clone('..')
243
found_bzrdir = BzrDir.open_containing_transport(
245
except errors.NotBranchError:
246
raise errors.NoRepositoryPresent(self)
248
repository = found_bzrdir.open_repository()
249
except errors.NoRepositoryPresent:
250
next_transport = found_bzrdir.root_transport.clone('..')
252
if ((found_bzrdir.root_transport.base ==
253
self.root_transport.base) or repository.is_shared()):
256
raise errors.NoRepositoryPresent(self)
257
raise errors.NoRepositoryPresent(self)
259
def get_branch_transport(self, branch_format):
260
"""Get the transport for use by branch format in this BzrDir.
262
Note that bzr dirs that do not support format strings will raise
263
IncompatibleFormat if the branch format they are given has
264
a format string, and vice verca.
266
If branch_format is None, the transport is returned with no
267
checking. if it is not None, then the returned transport is
268
guaranteed to point to an existing directory ready for use.
270
raise NotImplementedError(self.get_branch_transport)
272
def get_repository_transport(self, repository_format):
273
"""Get the transport for use by repository format in this BzrDir.
275
Note that bzr dirs that do not support format strings will raise
276
IncompatibleFormat if the repository format they are given has
277
a format string, and vice verca.
279
If repository_format is None, the transport is returned with no
280
checking. if it is not None, then the returned transport is
281
guaranteed to point to an existing directory ready for use.
283
raise NotImplementedError(self.get_repository_transport)
285
def get_workingtree_transport(self, tree_format):
286
"""Get the transport for use by workingtree format in this BzrDir.
288
Note that bzr dirs that do not support format strings will raise
289
IncompatibleFormat if the workingtree format they are given has
290
a format string, and vice verca.
292
If workingtree_format is None, the transport is returned with no
293
checking. if it is not None, then the returned transport is
294
guaranteed to point to an existing directory ready for use.
296
raise NotImplementedError(self.get_workingtree_transport)
298
def __init__(self, _transport, _format):
299
"""Initialize a Bzr control dir object.
301
Only really common logic should reside here, concrete classes should be
302
made with varying behaviours.
304
:param _format: the format that is creating this BzrDir instance.
305
:param _transport: the transport this dir is based at.
307
self._format = _format
308
self.transport = _transport.clone('.bzr')
309
self.root_transport = _transport
312
def open_unsupported(base):
313
"""Open a branch which is not supported."""
314
return BzrDir.open(base, _unsupported=True)
317
def open(base, _unsupported=False):
318
"""Open an existing bzrdir, rooted at 'base' (url)
320
_unsupported is a private parameter to the BzrDir class.
322
t = get_transport(base)
323
mutter("trying to open %r with transport %r", base, t)
324
format = BzrDirFormat.find_format(t)
325
if not _unsupported and not format.is_supported():
326
# see open_downlevel to open legacy branches.
327
raise errors.UnsupportedFormatError(
328
'sorry, format %s not supported' % format,
329
['use a different bzr version',
330
'or remove the .bzr directory'
331
' and "bzr init" again'])
332
return format.open(t, _found=True)
334
def open_branch(self, unsupported=False):
335
"""Open the branch object at this BzrDir if one is present.
337
If unsupported is True, then no longer supported branch formats can
340
TODO: static convenience version of this?
342
raise NotImplementedError(self.open_branch)
345
def open_containing(url):
346
"""Open an existing branch which contains url.
348
:param url: url to search from.
349
See open_containing_transport for more detail.
351
return BzrDir.open_containing_transport(get_transport(url))
354
def open_containing_transport(a_transport):
355
"""Open an existing branch which contains a_transport.base
357
This probes for a branch at a_transport, and searches upwards from there.
359
Basically we keep looking up until we find the control directory or
360
run into the root. If there isn't one, raises NotBranchError.
361
If there is one and it is either an unrecognised format or an unsupported
362
format, UnknownFormatError or UnsupportedFormatError are raised.
363
If there is one, it is returned, along with the unused portion of url.
365
# this gets the normalised url back. I.e. '.' -> the full path.
366
url = a_transport.base
369
format = BzrDirFormat.find_format(a_transport)
370
return format.open(a_transport), a_transport.relpath(url)
371
except errors.NotBranchError, e:
372
mutter('not a branch in: %r %s', a_transport.base, e)
373
new_t = a_transport.clone('..')
374
if new_t.base == a_transport.base:
375
# reached the root, whatever that may be
376
raise errors.NotBranchError(path=url)
379
def open_repository(self, _unsupported=False):
380
"""Open the repository object at this BzrDir if one is present.
382
This will not follow the Branch object pointer - its strictly a direct
383
open facility. Most client code should use open_branch().repository to
386
_unsupported is a private parameter, not part of the api.
387
TODO: static convenience version of this?
389
raise NotImplementedError(self.open_repository)
391
def open_workingtree(self, _unsupported=False):
392
"""Open the workingtree object at this BzrDir if one is present.
394
TODO: static convenience version of this?
396
raise NotImplementedError(self.open_workingtree)
398
def sprout(self, url, revision_id=None, basis=None):
399
"""Create a copy of this bzrdir prepared for use as a new line of
402
If urls last component does not exist, it will be created.
404
Attributes related to the identity of the source branch like
405
branch nickname will be cleaned, a working tree is created
406
whether one existed before or not; and a local branch is always
409
if revision_id is not None, then the clone operation may tune
410
itself to download less data.
413
result = self._format.initialize(url)
414
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
416
source_branch = self.open_branch()
417
source_repository = source_branch.repository
418
except errors.NotBranchError:
421
source_repository = self.open_repository()
422
except errors.NoRepositoryPresent:
423
# copy the basis one if there is one
424
source_repository = basis_repo
425
if source_repository is not None:
426
source_repository.clone(result,
427
revision_id=revision_id,
430
# no repo available, make a new one
431
result.create_repository()
432
if source_branch is not None:
433
source_branch.sprout(result, revision_id=revision_id)
435
result.create_branch()
437
self.open_workingtree().clone(result,
438
revision_id=revision_id,
440
except (errors.NoWorkingTree, errors.NotLocalUrl):
441
result.create_workingtree()
445
class BzrDirPreSplitOut(BzrDir):
446
"""A common class for the all-in-one formats."""
448
def clone(self, url, revision_id=None, basis=None):
449
"""See BzrDir.clone()."""
450
from bzrlib.workingtree import WorkingTreeFormat2
452
result = self._format.initialize(url, _cloning=True)
453
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
454
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
455
self.open_branch().clone(result, revision_id=revision_id)
457
self.open_workingtree().clone(result, basis=basis_tree)
458
except errors.NotLocalUrl:
459
# make a new one, this format always has to have one.
460
WorkingTreeFormat2().initialize(result)
463
def create_branch(self):
464
"""See BzrDir.create_branch."""
465
return self.open_branch()
467
def create_repository(self, shared=False):
468
"""See BzrDir.create_repository."""
470
raise errors.IncompatibleFormat('shared repository', self._format)
471
return self.open_repository()
473
def create_workingtree(self, revision_id=None):
474
"""See BzrDir.create_workingtree."""
475
# this looks buggy but is not -really-
476
# clone and sprout will have set the revision_id
477
# and that will have set it for us, its only
478
# specific uses of create_workingtree in isolation
479
# that can do wonky stuff here, and that only
480
# happens for creating checkouts, which cannot be
481
# done on this format anyway. So - acceptable wart.
482
result = self.open_workingtree()
483
if revision_id is not None:
484
result.set_last_revision(revision_id)
487
def get_branch_transport(self, branch_format):
488
"""See BzrDir.get_branch_transport()."""
489
if branch_format is None:
490
return self.transport
492
branch_format.get_format_string()
493
except NotImplementedError:
494
return self.transport
495
raise errors.IncompatibleFormat(branch_format, self._format)
497
def get_repository_transport(self, repository_format):
498
"""See BzrDir.get_repository_transport()."""
499
if repository_format is None:
500
return self.transport
502
repository_format.get_format_string()
503
except NotImplementedError:
504
return self.transport
505
raise errors.IncompatibleFormat(repository_format, self._format)
507
def get_workingtree_transport(self, workingtree_format):
508
"""See BzrDir.get_workingtree_transport()."""
509
if workingtree_format is None:
510
return self.transport
512
workingtree_format.get_format_string()
513
except NotImplementedError:
514
return self.transport
515
raise errors.IncompatibleFormat(workingtree_format, self._format)
517
def open_branch(self, unsupported=False):
518
"""See BzrDir.open_branch."""
519
from bzrlib.branch import BzrBranchFormat4
520
format = BzrBranchFormat4()
521
self._check_supported(format, unsupported)
522
return format.open(self, _found=True)
524
def sprout(self, url, revision_id=None, basis=None):
525
"""See BzrDir.sprout()."""
526
from bzrlib.workingtree import WorkingTreeFormat2
528
result = self._format.initialize(url, _cloning=True)
529
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
531
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
532
except errors.NoRepositoryPresent:
535
self.open_branch().sprout(result, revision_id=revision_id)
536
except errors.NotBranchError:
539
self.open_workingtree().clone(result, basis=basis_tree)
540
except (errors.NotBranchError, errors.NotLocalUrl):
541
# we always want a working tree
542
WorkingTreeFormat2().initialize(result)
546
class BzrDir4(BzrDirPreSplitOut):
547
"""A .bzr version 4 control object.
549
This is a deprecated format and may be removed after sept 2006.
552
def create_repository(self, shared=False):
553
"""See BzrDir.create_repository."""
554
from bzrlib.repository import RepositoryFormat4
555
return RepositoryFormat4().initialize(self, shared)
557
def open_repository(self):
558
"""See BzrDir.open_repository."""
559
from bzrlib.repository import RepositoryFormat4
560
return RepositoryFormat4().open(self, _found=True)
563
class BzrDir5(BzrDirPreSplitOut):
564
"""A .bzr version 5 control object.
566
This is a deprecated format and may be removed after sept 2006.
569
def open_repository(self):
570
"""See BzrDir.open_repository."""
571
from bzrlib.repository import RepositoryFormat5
572
return RepositoryFormat5().open(self, _found=True)
574
def open_workingtree(self, _unsupported=False):
575
"""See BzrDir.create_workingtree."""
576
from bzrlib.workingtree import WorkingTreeFormat2
577
return WorkingTreeFormat2().open(self, _found=True)
580
class BzrDir6(BzrDirPreSplitOut):
581
"""A .bzr version 6 control object.
583
This is a deprecated format and may be removed after sept 2006.
586
def open_repository(self):
587
"""See BzrDir.open_repository."""
588
from bzrlib.repository import RepositoryFormat6
589
return RepositoryFormat6().open(self, _found=True)
591
def open_workingtree(self, _unsupported=False):
592
"""See BzrDir.create_workingtree."""
593
from bzrlib.workingtree import WorkingTreeFormat2
594
return WorkingTreeFormat2().open(self, _found=True)
597
class BzrDirMeta1(BzrDir):
598
"""A .bzr meta version 1 control object.
600
This is the first control object where the
601
individual formats are really split out.
604
def create_branch(self):
605
"""See BzrDir.create_branch."""
606
from bzrlib.branch import BranchFormat
607
return BranchFormat.get_default_format().initialize(self)
609
def create_repository(self, shared=False):
610
"""See BzrDir.create_repository."""
611
from bzrlib.repository import RepositoryFormat
612
return RepositoryFormat.get_default_format().initialize(self, shared)
614
def create_workingtree(self, revision_id=None):
615
"""See BzrDir.create_workingtree."""
616
from bzrlib.workingtree import WorkingTreeFormat
617
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
619
def get_branch_transport(self, branch_format):
620
"""See BzrDir.get_branch_transport()."""
621
if branch_format is None:
622
return self.transport.clone('branch')
624
branch_format.get_format_string()
625
except NotImplementedError:
626
raise errors.IncompatibleFormat(branch_format, self._format)
628
self.transport.mkdir('branch')
629
except errors.FileExists:
631
return self.transport.clone('branch')
633
def get_repository_transport(self, repository_format):
634
"""See BzrDir.get_repository_transport()."""
635
if repository_format is None:
636
return self.transport.clone('repository')
638
repository_format.get_format_string()
639
except NotImplementedError:
640
raise errors.IncompatibleFormat(repository_format, self._format)
642
self.transport.mkdir('repository')
643
except errors.FileExists:
645
return self.transport.clone('repository')
647
def get_workingtree_transport(self, workingtree_format):
648
"""See BzrDir.get_workingtree_transport()."""
649
if workingtree_format is None:
650
return self.transport.clone('checkout')
652
workingtree_format.get_format_string()
653
except NotImplementedError:
654
raise errors.IncompatibleFormat(workingtree_format, self._format)
656
self.transport.mkdir('checkout')
657
except errors.FileExists:
659
return self.transport.clone('checkout')
661
def open_branch(self, unsupported=False):
662
"""See BzrDir.open_branch."""
663
from bzrlib.branch import BranchFormat
664
format = BranchFormat.find_format(self)
665
self._check_supported(format, unsupported)
666
return format.open(self, _found=True)
668
def open_repository(self, unsupported=False):
669
"""See BzrDir.open_repository."""
670
from bzrlib.repository import RepositoryFormat
671
format = RepositoryFormat.find_format(self)
672
self._check_supported(format, unsupported)
673
return format.open(self, _found=True)
675
def open_workingtree(self, unsupported=False):
676
"""See BzrDir.open_workingtree."""
677
from bzrlib.workingtree import WorkingTreeFormat
678
format = WorkingTreeFormat.find_format(self)
679
self._check_supported(format, unsupported)
680
return format.open(self, _found=True)
683
class BzrDirFormat(object):
684
"""An encapsulation of the initialization and open routines for a format.
686
Formats provide three things:
687
* An initialization routine,
691
Formats are placed in an dict by their format string for reference
692
during bzrdir opening. These should be subclasses of BzrDirFormat
695
Once a format is deprecated, just deprecate the initialize and open
696
methods on the format class. Do not deprecate the object, as the
697
object will be created every system load.
700
_default_format = None
701
"""The default format used for new .bzr dirs."""
704
"""The known formats."""
707
def find_format(klass, transport):
708
"""Return the format registered for URL."""
710
format_string = transport.get(".bzr/branch-format").read()
711
return klass._formats[format_string]
712
except errors.NoSuchFile:
713
raise errors.NotBranchError(path=transport.base)
715
raise errors.UnknownFormatError(format_string)
718
def get_default_format(klass):
719
"""Return the current default format."""
720
return klass._default_format
722
def get_format_string(self):
723
"""Return the ASCII format string that identifies this format."""
724
raise NotImplementedError(self.get_format_string)
726
def initialize(self, url):
727
"""Create a bzr control dir at this url and return an opened copy."""
728
# Since we don't have a .bzr directory, inherit the
729
# mode from the root directory
730
t = get_transport(url)
731
temp_control = LockableFiles(t, '')
732
temp_control._transport.mkdir('.bzr',
733
# FIXME: RBC 20060121 dont peek under
735
mode=temp_control._dir_mode)
736
file_mode = temp_control._file_mode
738
mutter('created control directory in ' + t.base)
739
control = t.clone('.bzr')
740
lock_file = 'branch-lock'
741
utf8_files = [('README',
742
"This is a Bazaar-NG control directory.\n"
743
"Do not change any files in this directory.\n"),
744
('branch-format', self.get_format_string()),
746
# NB: no need to escape relative paths that are url safe.
747
control.put(lock_file, StringIO(), mode=file_mode)
748
control_files = LockableFiles(control, lock_file)
749
control_files.lock_write()
751
for file, content in utf8_files:
752
control_files.put_utf8(file, content)
754
control_files.unlock()
755
return self.open(t, _found=True)
757
def is_supported(self):
758
"""Is this format supported?
760
Supported formats must be initializable and openable.
761
Unsupported formats may not support initialization or committing or
762
some other features depending on the reason for not being supported.
766
def open(self, transport, _found=False):
767
"""Return an instance of this format for the dir transport points at.
769
_found is a private parameter, do not use it.
772
assert isinstance(BzrDirFormat.find_format(transport),
774
return self._open(transport)
776
def _open(self, transport):
777
"""Template method helper for opening BzrDirectories.
779
This performs the actual open and any additional logic or parameter
782
raise NotImplementedError(self._open)
785
def register_format(klass, format):
786
klass._formats[format.get_format_string()] = format
789
def set_default_format(klass, format):
790
klass._default_format = format
793
def unregister_format(klass, format):
794
assert klass._formats[format.get_format_string()] is format
795
del klass._formats[format.get_format_string()]
798
class BzrDirFormat4(BzrDirFormat):
801
This format is a combined format for working tree, branch and repository.
803
- Format 1 working trees [always]
804
- Format 4 branches [always]
805
- Format 4 repositories [always]
807
This format is deprecated: it indexes texts using a text it which is
808
removed in format 5; write support for this format has been removed.
811
def get_format_string(self):
812
"""See BzrDirFormat.get_format_string()."""
813
return "Bazaar-NG branch, format 0.0.4\n"
815
def initialize(self, url):
816
"""Format 4 branches cannot be created."""
817
raise errors.UninitializableFormat(self)
819
def is_supported(self):
820
"""Format 4 is not supported.
822
It is not supported because the model changed from 4 to 5 and the
823
conversion logic is expensive - so doing it on the fly was not
828
def _open(self, transport):
829
"""See BzrDirFormat._open."""
830
return BzrDir4(transport, self)
833
class BzrDirFormat5(BzrDirFormat):
834
"""Bzr control format 5.
836
This format is a combined format for working tree, branch and repository.
838
- Format 2 working trees [always]
839
- Format 4 branches [always]
840
- Format 5 repositories [always]
841
Unhashed stores in the repository.
844
def get_format_string(self):
845
"""See BzrDirFormat.get_format_string()."""
846
return "Bazaar-NG branch, format 5\n"
848
def initialize(self, url, _cloning=False):
849
"""Format 5 dirs always have working tree, branch and repository.
851
Except when they are being cloned.
853
from bzrlib.branch import BzrBranchFormat4
854
from bzrlib.repository import RepositoryFormat5
855
from bzrlib.workingtree import WorkingTreeFormat2
856
result = super(BzrDirFormat5, self).initialize(url)
857
RepositoryFormat5().initialize(result, _internal=True)
859
BzrBranchFormat4().initialize(result)
860
WorkingTreeFormat2().initialize(result)
863
def _open(self, transport):
864
"""See BzrDirFormat._open."""
865
return BzrDir5(transport, self)
868
class BzrDirFormat6(BzrDirFormat):
869
"""Bzr control format 6.
871
This format is a combined format for working tree, branch and repository.
873
- Format 2 working trees [always]
874
- Format 4 branches [always]
875
- Format 6 repositories [always]
878
def get_format_string(self):
879
"""See BzrDirFormat.get_format_string()."""
880
return "Bazaar-NG branch, format 6\n"
882
def initialize(self, url, _cloning=False):
883
"""Format 6 dirs always have working tree, branch and repository.
885
Except when they are being cloned.
887
from bzrlib.branch import BzrBranchFormat4
888
from bzrlib.repository import RepositoryFormat6
889
from bzrlib.workingtree import WorkingTreeFormat2
890
result = super(BzrDirFormat6, self).initialize(url)
891
RepositoryFormat6().initialize(result, _internal=True)
893
BzrBranchFormat4().initialize(result)
895
WorkingTreeFormat2().initialize(result)
896
except errors.NotLocalUrl:
897
# emulate pre-check behaviour for working tree and silently
902
def _open(self, transport):
903
"""See BzrDirFormat._open."""
904
return BzrDir6(transport, self)
907
class BzrDirMetaFormat1(BzrDirFormat):
908
"""Bzr meta control format 1
910
This is the first format with split out working tree, branch and repository
913
- Format 3 working trees [optional]
914
- Format 5 branches [optional]
915
- Format 7 repositories [optional]
918
def get_format_string(self):
919
"""See BzrDirFormat.get_format_string()."""
920
return "Bazaar-NG meta directory, format 1\n"
922
def _open(self, transport):
923
"""See BzrDirFormat._open."""
924
return BzrDirMeta1(transport, self)
927
BzrDirFormat.register_format(BzrDirFormat4())
928
BzrDirFormat.register_format(BzrDirFormat5())
929
BzrDirFormat.register_format(BzrDirMetaFormat1())
930
__default_format = BzrDirFormat6()
931
BzrDirFormat.register_format(__default_format)
932
BzrDirFormat.set_default_format(__default_format)
935
class BzrDirTestProviderAdapter(object):
936
"""A tool to generate a suite testing multiple bzrdir formats at once.
938
This is done by copying the test once for each transport and injecting
939
the transport_server, transport_readonly_server, and bzrdir_format
940
classes into each copy. Each copy is also given a new id() to make it
944
def __init__(self, transport_server, transport_readonly_server, formats):
945
self._transport_server = transport_server
946
self._transport_readonly_server = transport_readonly_server
947
self._formats = formats
949
def adapt(self, test):
951
for format in self._formats:
952
new_test = deepcopy(test)
953
new_test.transport_server = self._transport_server
954
new_test.transport_readonly_server = self._transport_readonly_server
955
new_test.bzrdir_format = format
956
def make_new_test_id():
957
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
958
return lambda: new_id
959
new_test.id = make_new_test_id()
960
result.addTest(new_test)
964
class ScratchDir(BzrDir6):
965
"""Special test class: a bzrdir that cleans up itself..
968
>>> base = d.transport.base
971
>>> b.transport.__del__()
976
def __init__(self, files=[], dirs=[], transport=None):
977
"""Make a test branch.
979
This creates a temporary directory and runs init-tree in it.
981
If any files are listed, they are created in the working copy.
983
if transport is None:
984
transport = bzrlib.transport.local.ScratchTransport()
985
# local import for scope restriction
986
BzrDirFormat6().initialize(transport.base)
987
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
988
self.create_repository()
990
self.create_workingtree()
992
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
994
# BzrBranch creates a clone to .bzr and then forgets about the
995
# original transport. A ScratchTransport() deletes itself and
996
# everything underneath it when it goes away, so we need to
997
# grab a local copy to prevent that from happening
998
self._transport = transport
1001
self._transport.mkdir(d)
1004
self._transport.put(f, 'content of %s' % f)
1008
>>> orig = ScratchDir(files=["file1", "file2"])
1009
>>> os.listdir(orig.base)
1010
[u'.bzr', u'file1', u'file2']
1011
>>> clone = orig.clone()
1012
>>> if os.name != 'nt':
1013
... os.path.samefile(orig.base, clone.base)
1015
... orig.base == clone.base
1018
>>> os.listdir(clone.base)
1019
[u'.bzr', u'file1', u'file2']
1021
from shutil import copytree
1022
from bzrlib.osutils import mkdtemp
1025
copytree(self.base, base, symlinks=True)
1027
transport=bzrlib.transport.local.ScratchTransport(base))