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
25
from cStringIO import StringIO
26
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.osutils import (
39
from bzrlib.store.text import TextStore
40
from bzrlib.store.weave import WeaveStore
41
from bzrlib.symbol_versioning import *
42
from bzrlib.trace import mutter
43
from bzrlib.transactions import PassThroughTransaction
44
from bzrlib.transport import get_transport
45
from bzrlib.transport.local import LocalTransport
46
from bzrlib.weave import Weave
47
from bzrlib.weavefile import read_weave, write_weave
48
from bzrlib.xml4 import serializer_v4
49
from bzrlib.xml5 import serializer_v5
53
"""A .bzr control diretory.
55
BzrDir instances let you create or open any of the things that can be
56
found within .bzr - checkouts, branches and repositories.
59
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
61
a transport connected to the directory this bzr was opened from.
64
def can_convert_format(self):
65
"""Return true if this bzrdir is one whose format we can convert from."""
68
def _check_supported(self, format, allow_unsupported):
69
"""Check whether format is a supported format.
71
If allow_unsupported is True, this is a no-op.
73
if not allow_unsupported and not format.is_supported():
74
raise errors.UnsupportedFormatError(format)
76
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
77
"""Clone this bzrdir and its contents to url verbatim.
79
If urls last component does not exist, it will be created.
81
if revision_id is not None, then the clone operation may tune
82
itself to download less data.
83
:param force_new_repo: Do not use a shared repository for the target
84
even if one is available.
87
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
88
result = self._format.initialize(url)
90
local_repo = self.find_repository()
91
except errors.NoRepositoryPresent:
94
# may need to copy content in
96
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
99
result_repo = result.find_repository()
100
# fetch content this dir needs.
102
# XXX FIXME RBC 20060214 need tests for this when the basis
104
result_repo.fetch(basis_repo, revision_id=revision_id)
105
result_repo.fetch(local_repo, revision_id=revision_id)
106
except errors.NoRepositoryPresent:
107
# needed to make one anyway.
108
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
109
# 1 if there is a branch present
110
# make sure its content is available in the target repository
113
self.open_branch().clone(result, revision_id=revision_id)
114
except errors.NotBranchError:
117
self.open_workingtree().clone(result, basis=basis_tree)
118
except (errors.NoWorkingTree, errors.NotLocalUrl):
122
def _get_basis_components(self, basis):
123
"""Retrieve the basis components that are available at basis."""
125
return None, None, None
127
basis_tree = basis.open_workingtree()
128
basis_branch = basis_tree.branch
129
basis_repo = basis_branch.repository
130
except (errors.NoWorkingTree, errors.NotLocalUrl):
133
basis_branch = basis.open_branch()
134
basis_repo = basis_branch.repository
135
except errors.NotBranchError:
138
basis_repo = basis.open_repository()
139
except errors.NoRepositoryPresent:
141
return basis_repo, basis_branch, basis_tree
143
def _make_tail(self, url):
144
segments = url.split('/')
145
if segments and segments[-1] not in ('', '.'):
146
parent = '/'.join(segments[:-1])
147
t = bzrlib.transport.get_transport(parent)
149
t.mkdir(segments[-1])
150
except errors.FileExists:
155
"""Create a new BzrDir at the url 'base'.
157
This will call the current default formats initialize with base
158
as the only parameter.
160
If you need a specific format, consider creating an instance
161
of that and calling initialize().
163
segments = base.split('/')
164
if segments and segments[-1] not in ('', '.'):
165
parent = '/'.join(segments[:-1])
166
t = bzrlib.transport.get_transport(parent)
168
t.mkdir(segments[-1])
169
except errors.FileExists:
171
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
173
def create_branch(self):
174
"""Create a branch in this BzrDir.
176
The bzrdirs format will control what branch format is created.
177
For more control see BranchFormatXX.create(a_bzrdir).
179
raise NotImplementedError(self.create_branch)
182
def create_branch_and_repo(base, force_new_repo=False):
183
"""Create a new BzrDir, Branch and Repository at the url 'base'.
185
This will use the current default BzrDirFormat, and use whatever
186
repository format that that uses via bzrdir.create_branch and
187
create_repository. If a shared repository is available that is used
190
The created Branch object is returned.
192
:param base: The URL to create the branch at.
193
:param force_new_repo: If True a new repository is always created.
195
bzrdir = BzrDir.create(base)
196
bzrdir._find_or_create_repository(force_new_repo)
197
return bzrdir.create_branch()
199
def _find_or_create_repository(self, force_new_repo):
200
"""Create a new repository if needed, returning the repository."""
202
return self.create_repository()
204
return self.find_repository()
205
except errors.NoRepositoryPresent:
206
return self.create_repository()
209
def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
210
"""Create a new BzrDir, Branch and Repository at the url 'base'.
212
This is a convenience function - it will use an existing repository
213
if possible, can be told explicitly whether to create a working tree or
216
This will use the current default BzrDirFormat, and use whatever
217
repository format that that uses via bzrdir.create_branch and
218
create_repository. If a shared repository is available that is used
219
preferentially. Whatever repository is used, its tree creation policy
222
The created Branch object is returned.
223
If a working tree cannot be made due to base not being a file:// url,
226
:param base: The URL to create the branch at.
227
:param force_new_repo: If True a new repository is always created.
228
:param force_new_tree: If True or False force creation of a tree or
229
prevent such creation respectively.
231
bzrdir = BzrDir.create(base)
232
repo = bzrdir._find_or_create_repository(force_new_repo)
233
result = bzrdir.create_branch()
234
if force_new_tree or (repo.make_working_trees() and
235
force_new_tree is None):
236
bzrdir.create_workingtree()
240
def create_repository(base, shared=False):
241
"""Create a new BzrDir and Repository at the url 'base'.
243
This will use the current default BzrDirFormat, and use whatever
244
repository format that that uses for bzrdirformat.create_repository.
246
;param shared: Create a shared repository rather than a standalone
248
The Repository object is returned.
250
This must be overridden as an instance method in child classes, where
251
it should take no parameters and construct whatever repository format
252
that child class desires.
254
bzrdir = BzrDir.create(base)
255
return bzrdir.create_repository()
258
def create_standalone_workingtree(base):
259
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
261
'base' must be a local path or a file:// url.
263
This will use the current default BzrDirFormat, and use whatever
264
repository format that that uses for bzrdirformat.create_workingtree,
265
create_branch and create_repository.
267
The WorkingTree object is returned.
269
t = get_transport(safe_unicode(base))
270
if not isinstance(t, LocalTransport):
271
raise errors.NotLocalUrl(base)
272
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
273
force_new_repo=True).bzrdir
274
return bzrdir.create_workingtree()
276
def create_workingtree(self, revision_id=None):
277
"""Create a working tree at this BzrDir.
279
revision_id: create it as of this revision id.
281
raise NotImplementedError(self.create_workingtree)
283
def find_repository(self):
284
"""Find the repository that should be used for a_bzrdir.
286
This does not require a branch as we use it to find the repo for
287
new branches as well as to hook existing branches up to their
291
return self.open_repository()
292
except errors.NoRepositoryPresent:
294
next_transport = self.root_transport.clone('..')
297
found_bzrdir = BzrDir.open_containing_from_transport(
299
except errors.NotBranchError:
300
raise errors.NoRepositoryPresent(self)
302
repository = found_bzrdir.open_repository()
303
except errors.NoRepositoryPresent:
304
next_transport = found_bzrdir.root_transport.clone('..')
306
if ((found_bzrdir.root_transport.base ==
307
self.root_transport.base) or repository.is_shared()):
310
raise errors.NoRepositoryPresent(self)
311
raise errors.NoRepositoryPresent(self)
313
def get_branch_transport(self, branch_format):
314
"""Get the transport for use by branch format in this BzrDir.
316
Note that bzr dirs that do not support format strings will raise
317
IncompatibleFormat if the branch format they are given has
318
a format string, and vice verca.
320
If branch_format is None, the transport is returned with no
321
checking. if it is not None, then the returned transport is
322
guaranteed to point to an existing directory ready for use.
324
raise NotImplementedError(self.get_branch_transport)
326
def get_repository_transport(self, repository_format):
327
"""Get the transport for use by repository format in this BzrDir.
329
Note that bzr dirs that do not support format strings will raise
330
IncompatibleFormat if the repository format they are given has
331
a format string, and vice verca.
333
If repository_format is None, the transport is returned with no
334
checking. if it is not None, then the returned transport is
335
guaranteed to point to an existing directory ready for use.
337
raise NotImplementedError(self.get_repository_transport)
339
def get_workingtree_transport(self, tree_format):
340
"""Get the transport for use by workingtree format in this BzrDir.
342
Note that bzr dirs that do not support format strings will raise
343
IncompatibleFormat if the workingtree format they are given has
344
a format string, and vice verca.
346
If workingtree_format is None, the transport is returned with no
347
checking. if it is not None, then the returned transport is
348
guaranteed to point to an existing directory ready for use.
350
raise NotImplementedError(self.get_workingtree_transport)
352
def __init__(self, _transport, _format):
353
"""Initialize a Bzr control dir object.
355
Only really common logic should reside here, concrete classes should be
356
made with varying behaviours.
358
:param _format: the format that is creating this BzrDir instance.
359
:param _transport: the transport this dir is based at.
361
self._format = _format
362
self.transport = _transport.clone('.bzr')
363
self.root_transport = _transport
365
def needs_format_conversion(self, format=None):
366
"""Return true if this bzrdir needs convert_format run on it.
368
For instance, if the repository format is out of date but the
369
branch and working tree are not, this should return True.
371
:param format: Optional parameter indicating a specific desired
372
format we plan to arrive at.
374
# for now, if the format is not the same as the system default,
375
# an upgrade is needed. In the future we will want to scan
376
# the individual repository/branch/checkout formats too
378
format = BzrDirFormat.get_default_format().__class__
379
return not isinstance(self._format, format)
382
def open_unsupported(base):
383
"""Open a branch which is not supported."""
384
return BzrDir.open(base, _unsupported=True)
387
def open(base, _unsupported=False):
388
"""Open an existing bzrdir, rooted at 'base' (url)
390
_unsupported is a private parameter to the BzrDir class.
392
t = get_transport(base)
393
mutter("trying to open %r with transport %r", base, t)
394
format = BzrDirFormat.find_format(t)
395
if not _unsupported and not format.is_supported():
396
# see open_downlevel to open legacy branches.
397
raise errors.UnsupportedFormatError(
398
'sorry, format %s not supported' % format,
399
['use a different bzr version',
400
'or remove the .bzr directory'
401
' and "bzr init" again'])
402
return format.open(t, _found=True)
404
def open_branch(self, unsupported=False):
405
"""Open the branch object at this BzrDir if one is present.
407
If unsupported is True, then no longer supported branch formats can
410
TODO: static convenience version of this?
412
raise NotImplementedError(self.open_branch)
415
def open_containing(url):
416
"""Open an existing branch which contains url.
418
:param url: url to search from.
419
See open_containing_from_transport for more detail.
421
return BzrDir.open_containing_from_transport(get_transport(url))
424
def open_containing_from_transport(a_transport):
425
"""Open an existing branch which contains a_transport.base
427
This probes for a branch at a_transport, and searches upwards from there.
429
Basically we keep looking up until we find the control directory or
430
run into the root. If there isn't one, raises NotBranchError.
431
If there is one and it is either an unrecognised format or an unsupported
432
format, UnknownFormatError or UnsupportedFormatError are raised.
433
If there is one, it is returned, along with the unused portion of url.
435
# this gets the normalised url back. I.e. '.' -> the full path.
436
url = a_transport.base
439
format = BzrDirFormat.find_format(a_transport)
440
return format.open(a_transport), a_transport.relpath(url)
441
except errors.NotBranchError, e:
442
mutter('not a branch in: %r %s', a_transport.base, e)
443
new_t = a_transport.clone('..')
444
if new_t.base == a_transport.base:
445
# reached the root, whatever that may be
446
raise errors.NotBranchError(path=url)
449
def open_repository(self, _unsupported=False):
450
"""Open the repository object at this BzrDir if one is present.
452
This will not follow the Branch object pointer - its strictly a direct
453
open facility. Most client code should use open_branch().repository to
456
_unsupported is a private parameter, not part of the api.
457
TODO: static convenience version of this?
459
raise NotImplementedError(self.open_repository)
461
def open_workingtree(self, _unsupported=False):
462
"""Open the workingtree object at this BzrDir if one is present.
464
TODO: static convenience version of this?
466
raise NotImplementedError(self.open_workingtree)
468
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
469
"""Create a copy of this bzrdir prepared for use as a new line of
472
If urls last component does not exist, it will be created.
474
Attributes related to the identity of the source branch like
475
branch nickname will be cleaned, a working tree is created
476
whether one existed before or not; and a local branch is always
479
if revision_id is not None, then the clone operation may tune
480
itself to download less data.
483
result = self._format.initialize(url)
484
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
486
source_branch = self.open_branch()
487
source_repository = source_branch.repository
488
except errors.NotBranchError:
491
source_repository = self.open_repository()
492
except errors.NoRepositoryPresent:
493
# copy the entire basis one if there is one
494
# but there is no repository.
495
source_repository = basis_repo
500
result_repo = result.find_repository()
501
except errors.NoRepositoryPresent:
503
if source_repository is None and result_repo is not None:
505
elif source_repository is None and result_repo is None:
506
# no repo available, make a new one
507
result.create_repository()
508
elif source_repository is not None and result_repo is None:
509
# have soure, and want to make a new target repo
510
source_repository.clone(result,
511
revision_id=revision_id,
514
# fetch needed content into target.
516
# XXX FIXME RBC 20060214 need tests for this when the basis
518
result_repo.fetch(basis_repo, revision_id=revision_id)
519
result_repo.fetch(source_repository, revision_id=revision_id)
520
if source_branch is not None:
521
source_branch.sprout(result, revision_id=revision_id)
523
result.create_branch()
525
self.open_workingtree().clone(result,
526
revision_id=revision_id,
528
except (errors.NoWorkingTree, errors.NotLocalUrl):
529
result.create_workingtree()
533
class BzrDirPreSplitOut(BzrDir):
534
"""A common class for the all-in-one formats."""
536
def __init__(self, _transport, _format):
537
"""See BzrDir.__init__."""
538
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
539
self._control_files = LockableFiles(self.get_branch_transport(None),
542
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
543
"""See BzrDir.clone()."""
544
from bzrlib.workingtree import WorkingTreeFormat2
546
result = self._format.initialize(url, _cloning=True)
547
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
548
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
549
self.open_branch().clone(result, revision_id=revision_id)
551
self.open_workingtree().clone(result, basis=basis_tree)
552
except errors.NotLocalUrl:
553
# make a new one, this format always has to have one.
554
WorkingTreeFormat2().initialize(result)
557
def create_branch(self):
558
"""See BzrDir.create_branch."""
559
return self.open_branch()
561
def create_repository(self, shared=False):
562
"""See BzrDir.create_repository."""
564
raise errors.IncompatibleFormat('shared repository', self._format)
565
return self.open_repository()
567
def create_workingtree(self, revision_id=None):
568
"""See BzrDir.create_workingtree."""
569
# this looks buggy but is not -really-
570
# clone and sprout will have set the revision_id
571
# and that will have set it for us, its only
572
# specific uses of create_workingtree in isolation
573
# that can do wonky stuff here, and that only
574
# happens for creating checkouts, which cannot be
575
# done on this format anyway. So - acceptable wart.
576
result = self.open_workingtree()
577
if revision_id is not None:
578
result.set_last_revision(revision_id)
581
def get_branch_transport(self, branch_format):
582
"""See BzrDir.get_branch_transport()."""
583
if branch_format is None:
584
return self.transport
586
branch_format.get_format_string()
587
except NotImplementedError:
588
return self.transport
589
raise errors.IncompatibleFormat(branch_format, self._format)
591
def get_repository_transport(self, repository_format):
592
"""See BzrDir.get_repository_transport()."""
593
if repository_format is None:
594
return self.transport
596
repository_format.get_format_string()
597
except NotImplementedError:
598
return self.transport
599
raise errors.IncompatibleFormat(repository_format, self._format)
601
def get_workingtree_transport(self, workingtree_format):
602
"""See BzrDir.get_workingtree_transport()."""
603
if workingtree_format is None:
604
return self.transport
606
workingtree_format.get_format_string()
607
except NotImplementedError:
608
return self.transport
609
raise errors.IncompatibleFormat(workingtree_format, self._format)
611
def open_branch(self, unsupported=False):
612
"""See BzrDir.open_branch."""
613
from bzrlib.branch import BzrBranchFormat4
614
format = BzrBranchFormat4()
615
self._check_supported(format, unsupported)
616
return format.open(self, _found=True)
618
def sprout(self, url, revision_id=None, basis=None):
619
"""See BzrDir.sprout()."""
620
from bzrlib.workingtree import WorkingTreeFormat2
622
result = self._format.initialize(url, _cloning=True)
623
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
625
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
626
except errors.NoRepositoryPresent:
629
self.open_branch().sprout(result, revision_id=revision_id)
630
except errors.NotBranchError:
633
self.open_workingtree().clone(result, basis=basis_tree)
634
except (errors.NotBranchError, errors.NotLocalUrl):
635
# we always want a working tree
636
WorkingTreeFormat2().initialize(result)
640
class BzrDir4(BzrDirPreSplitOut):
641
"""A .bzr version 4 control object.
643
This is a deprecated format and may be removed after sept 2006.
646
def create_repository(self, shared=False):
647
"""See BzrDir.create_repository."""
648
from bzrlib.repository import RepositoryFormat4
649
return RepositoryFormat4().initialize(self, shared)
651
def needs_format_conversion(self, format=None):
652
"""Format 4 dirs are always in need of conversion."""
655
def open_repository(self):
656
"""See BzrDir.open_repository."""
657
from bzrlib.repository import RepositoryFormat4
658
return RepositoryFormat4().open(self, _found=True)
661
class BzrDir5(BzrDirPreSplitOut):
662
"""A .bzr version 5 control object.
664
This is a deprecated format and may be removed after sept 2006.
667
def open_repository(self):
668
"""See BzrDir.open_repository."""
669
from bzrlib.repository import RepositoryFormat5
670
return RepositoryFormat5().open(self, _found=True)
672
def open_workingtree(self, _unsupported=False):
673
"""See BzrDir.create_workingtree."""
674
from bzrlib.workingtree import WorkingTreeFormat2
675
return WorkingTreeFormat2().open(self, _found=True)
678
class BzrDir6(BzrDirPreSplitOut):
679
"""A .bzr version 6 control object.
681
This is a deprecated format and may be removed after sept 2006.
684
def open_repository(self):
685
"""See BzrDir.open_repository."""
686
from bzrlib.repository import RepositoryFormat6
687
return RepositoryFormat6().open(self, _found=True)
689
def open_workingtree(self, _unsupported=False):
690
"""See BzrDir.create_workingtree."""
691
from bzrlib.workingtree import WorkingTreeFormat2
692
return WorkingTreeFormat2().open(self, _found=True)
695
class BzrDirMeta1(BzrDir):
696
"""A .bzr meta version 1 control object.
698
This is the first control object where the
699
individual formats are really split out.
702
def can_convert_format(self):
703
"""See BzrDir.can_convert_format()."""
706
def create_branch(self):
707
"""See BzrDir.create_branch."""
708
from bzrlib.branch import BranchFormat
709
return BranchFormat.get_default_format().initialize(self)
711
def create_repository(self, shared=False):
712
"""See BzrDir.create_repository."""
713
from bzrlib.repository import RepositoryFormat
714
return RepositoryFormat.get_default_format().initialize(self, shared)
716
def create_workingtree(self, revision_id=None):
717
"""See BzrDir.create_workingtree."""
718
from bzrlib.workingtree import WorkingTreeFormat
719
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
721
def get_branch_transport(self, branch_format):
722
"""See BzrDir.get_branch_transport()."""
723
if branch_format is None:
724
return self.transport.clone('branch')
726
branch_format.get_format_string()
727
except NotImplementedError:
728
raise errors.IncompatibleFormat(branch_format, self._format)
730
self.transport.mkdir('branch')
731
except errors.FileExists:
733
return self.transport.clone('branch')
735
def get_repository_transport(self, repository_format):
736
"""See BzrDir.get_repository_transport()."""
737
if repository_format is None:
738
return self.transport.clone('repository')
740
repository_format.get_format_string()
741
except NotImplementedError:
742
raise errors.IncompatibleFormat(repository_format, self._format)
744
self.transport.mkdir('repository')
745
except errors.FileExists:
747
return self.transport.clone('repository')
749
def get_workingtree_transport(self, workingtree_format):
750
"""See BzrDir.get_workingtree_transport()."""
751
if workingtree_format is None:
752
return self.transport.clone('checkout')
754
workingtree_format.get_format_string()
755
except NotImplementedError:
756
raise errors.IncompatibleFormat(workingtree_format, self._format)
758
self.transport.mkdir('checkout')
759
except errors.FileExists:
761
return self.transport.clone('checkout')
763
def needs_format_conversion(self, format=None):
764
"""See BzrDir.needs_format_conversion()."""
765
# currently there are no possible conversions for meta1 formats.
768
def open_branch(self, unsupported=False):
769
"""See BzrDir.open_branch."""
770
from bzrlib.branch import BranchFormat
771
format = BranchFormat.find_format(self)
772
self._check_supported(format, unsupported)
773
return format.open(self, _found=True)
775
def open_repository(self, unsupported=False):
776
"""See BzrDir.open_repository."""
777
from bzrlib.repository import RepositoryFormat
778
format = RepositoryFormat.find_format(self)
779
self._check_supported(format, unsupported)
780
return format.open(self, _found=True)
782
def open_workingtree(self, unsupported=False):
783
"""See BzrDir.open_workingtree."""
784
from bzrlib.workingtree import WorkingTreeFormat
785
format = WorkingTreeFormat.find_format(self)
786
self._check_supported(format, unsupported)
787
return format.open(self, _found=True)
790
class BzrDirFormat(object):
791
"""An encapsulation of the initialization and open routines for a format.
793
Formats provide three things:
794
* An initialization routine,
798
Formats are placed in an dict by their format string for reference
799
during bzrdir opening. These should be subclasses of BzrDirFormat
802
Once a format is deprecated, just deprecate the initialize and open
803
methods on the format class. Do not deprecate the object, as the
804
object will be created every system load.
807
_default_format = None
808
"""The default format used for new .bzr dirs."""
811
"""The known formats."""
814
def find_format(klass, transport):
815
"""Return the format registered for URL."""
817
format_string = transport.get(".bzr/branch-format").read()
818
return klass._formats[format_string]
819
except errors.NoSuchFile:
820
raise errors.NotBranchError(path=transport.base)
822
raise errors.UnknownFormatError(format_string)
825
def get_default_format(klass):
826
"""Return the current default format."""
827
return klass._default_format
829
def get_format_string(self):
830
"""Return the ASCII format string that identifies this format."""
831
raise NotImplementedError(self.get_format_string)
833
def get_converter(self, format=None):
834
"""Return the converter to use to convert bzrdirs needing converts.
836
This returns a bzrlib.bzrdir.Converter object.
838
This should return the best upgrader to step this format towards the
839
current default format. In the case of plugins we can/shouold provide
840
some means for them to extend the range of returnable converters.
842
:param format: Optional format to override the default foramt of the
845
raise NotImplementedError(self.get_converter)
847
def initialize(self, url):
848
"""Create a bzr control dir at this url and return an opened copy."""
849
# Since we don't have a .bzr directory, inherit the
850
# mode from the root directory
851
t = get_transport(url)
852
temp_control = LockableFiles(t, '')
853
temp_control._transport.mkdir('.bzr',
854
# FIXME: RBC 20060121 dont peek under
856
mode=temp_control._dir_mode)
857
file_mode = temp_control._file_mode
859
mutter('created control directory in ' + t.base)
860
control = t.clone('.bzr')
861
lock_file = 'branch-lock'
862
utf8_files = [('README',
863
"This is a Bazaar-NG control directory.\n"
864
"Do not change any files in this directory.\n"),
865
('branch-format', self.get_format_string()),
867
# NB: no need to escape relative paths that are url safe.
868
control.put(lock_file, StringIO(), mode=file_mode)
869
control_files = LockableFiles(control, lock_file)
870
control_files.lock_write()
872
for file, content in utf8_files:
873
control_files.put_utf8(file, content)
875
control_files.unlock()
876
return self.open(t, _found=True)
878
def is_supported(self):
879
"""Is this format supported?
881
Supported formats must be initializable and openable.
882
Unsupported formats may not support initialization or committing or
883
some other features depending on the reason for not being supported.
887
def open(self, transport, _found=False):
888
"""Return an instance of this format for the dir transport points at.
890
_found is a private parameter, do not use it.
893
assert isinstance(BzrDirFormat.find_format(transport),
895
return self._open(transport)
897
def _open(self, transport):
898
"""Template method helper for opening BzrDirectories.
900
This performs the actual open and any additional logic or parameter
903
raise NotImplementedError(self._open)
906
def register_format(klass, format):
907
klass._formats[format.get_format_string()] = format
910
def set_default_format(klass, format):
911
klass._default_format = format
914
return self.get_format_string()[:-1]
917
def unregister_format(klass, format):
918
assert klass._formats[format.get_format_string()] is format
919
del klass._formats[format.get_format_string()]
922
class BzrDirFormat4(BzrDirFormat):
925
This format is a combined format for working tree, branch and repository.
927
- Format 1 working trees [always]
928
- Format 4 branches [always]
929
- Format 4 repositories [always]
931
This format is deprecated: it indexes texts using a text it which is
932
removed in format 5; write support for this format has been removed.
935
def get_format_string(self):
936
"""See BzrDirFormat.get_format_string()."""
937
return "Bazaar-NG branch, format 0.0.4\n"
939
def get_converter(self, format=None):
940
"""See BzrDirFormat.get_converter()."""
941
# there is one and only one upgrade path here.
942
return ConvertBzrDir4To5()
944
def initialize(self, url):
945
"""Format 4 branches cannot be created."""
946
raise errors.UninitializableFormat(self)
948
def is_supported(self):
949
"""Format 4 is not supported.
951
It is not supported because the model changed from 4 to 5 and the
952
conversion logic is expensive - so doing it on the fly was not
957
def _open(self, transport):
958
"""See BzrDirFormat._open."""
959
return BzrDir4(transport, self)
962
class BzrDirFormat5(BzrDirFormat):
963
"""Bzr control format 5.
965
This format is a combined format for working tree, branch and repository.
967
- Format 2 working trees [always]
968
- Format 4 branches [always]
969
- Format 5 repositories [always]
970
Unhashed stores in the repository.
973
def get_format_string(self):
974
"""See BzrDirFormat.get_format_string()."""
975
return "Bazaar-NG branch, format 5\n"
977
def get_converter(self, format=None):
978
"""See BzrDirFormat.get_converter()."""
979
# there is one and only one upgrade path here.
980
return ConvertBzrDir5To6()
982
def initialize(self, url, _cloning=False):
983
"""Format 5 dirs always have working tree, branch and repository.
985
Except when they are being cloned.
987
from bzrlib.branch import BzrBranchFormat4
988
from bzrlib.repository import RepositoryFormat5
989
from bzrlib.workingtree import WorkingTreeFormat2
990
result = super(BzrDirFormat5, self).initialize(url)
991
RepositoryFormat5().initialize(result, _internal=True)
993
BzrBranchFormat4().initialize(result)
994
WorkingTreeFormat2().initialize(result)
997
def _open(self, transport):
998
"""See BzrDirFormat._open."""
999
return BzrDir5(transport, self)
1002
class BzrDirFormat6(BzrDirFormat):
1003
"""Bzr control format 6.
1005
This format is a combined format for working tree, branch and repository.
1007
- Format 2 working trees [always]
1008
- Format 4 branches [always]
1009
- Format 6 repositories [always]
1012
def get_format_string(self):
1013
"""See BzrDirFormat.get_format_string()."""
1014
return "Bazaar-NG branch, format 6\n"
1016
def get_converter(self, format=None):
1017
"""See BzrDirFormat.get_converter()."""
1018
# there is one and only one upgrade path here.
1019
return ConvertBzrDir6ToMeta()
1021
def initialize(self, url, _cloning=False):
1022
"""Format 6 dirs always have working tree, branch and repository.
1024
Except when they are being cloned.
1026
from bzrlib.branch import BzrBranchFormat4
1027
from bzrlib.repository import RepositoryFormat6
1028
from bzrlib.workingtree import WorkingTreeFormat2
1029
result = super(BzrDirFormat6, self).initialize(url)
1030
RepositoryFormat6().initialize(result, _internal=True)
1032
BzrBranchFormat4().initialize(result)
1034
WorkingTreeFormat2().initialize(result)
1035
except errors.NotLocalUrl:
1036
# emulate pre-check behaviour for working tree and silently
1041
def _open(self, transport):
1042
"""See BzrDirFormat._open."""
1043
return BzrDir6(transport, self)
1046
class BzrDirMetaFormat1(BzrDirFormat):
1047
"""Bzr meta control format 1
1049
This is the first format with split out working tree, branch and repository
1052
- Format 3 working trees [optional]
1053
- Format 5 branches [optional]
1054
- Format 7 repositories [optional]
1057
def get_format_string(self):
1058
"""See BzrDirFormat.get_format_string()."""
1059
return "Bazaar-NG meta directory, format 1\n"
1061
def _open(self, transport):
1062
"""See BzrDirFormat._open."""
1063
return BzrDirMeta1(transport, self)
1066
BzrDirFormat.register_format(BzrDirFormat4())
1067
BzrDirFormat.register_format(BzrDirFormat5())
1068
BzrDirFormat.register_format(BzrDirMetaFormat1())
1069
__default_format = BzrDirFormat6()
1070
BzrDirFormat.register_format(__default_format)
1071
BzrDirFormat.set_default_format(__default_format)
1074
class BzrDirTestProviderAdapter(object):
1075
"""A tool to generate a suite testing multiple bzrdir formats at once.
1077
This is done by copying the test once for each transport and injecting
1078
the transport_server, transport_readonly_server, and bzrdir_format
1079
classes into each copy. Each copy is also given a new id() to make it
1083
def __init__(self, transport_server, transport_readonly_server, formats):
1084
self._transport_server = transport_server
1085
self._transport_readonly_server = transport_readonly_server
1086
self._formats = formats
1088
def adapt(self, test):
1089
result = TestSuite()
1090
for format in self._formats:
1091
new_test = deepcopy(test)
1092
new_test.transport_server = self._transport_server
1093
new_test.transport_readonly_server = self._transport_readonly_server
1094
new_test.bzrdir_format = format
1095
def make_new_test_id():
1096
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1097
return lambda: new_id
1098
new_test.id = make_new_test_id()
1099
result.addTest(new_test)
1103
class ScratchDir(BzrDir6):
1104
"""Special test class: a bzrdir that cleans up itself..
1106
>>> d = ScratchDir()
1107
>>> base = d.transport.base
1110
>>> b.transport.__del__()
1115
def __init__(self, files=[], dirs=[], transport=None):
1116
"""Make a test branch.
1118
This creates a temporary directory and runs init-tree in it.
1120
If any files are listed, they are created in the working copy.
1122
if transport is None:
1123
transport = bzrlib.transport.local.ScratchTransport()
1124
# local import for scope restriction
1125
BzrDirFormat6().initialize(transport.base)
1126
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1127
self.create_repository()
1128
self.create_branch()
1129
self.create_workingtree()
1131
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1133
# BzrBranch creates a clone to .bzr and then forgets about the
1134
# original transport. A ScratchTransport() deletes itself and
1135
# everything underneath it when it goes away, so we need to
1136
# grab a local copy to prevent that from happening
1137
self._transport = transport
1140
self._transport.mkdir(d)
1143
self._transport.put(f, 'content of %s' % f)
1147
>>> orig = ScratchDir(files=["file1", "file2"])
1148
>>> os.listdir(orig.base)
1149
[u'.bzr', u'file1', u'file2']
1150
>>> clone = orig.clone()
1151
>>> if os.name != 'nt':
1152
... os.path.samefile(orig.base, clone.base)
1154
... orig.base == clone.base
1157
>>> os.listdir(clone.base)
1158
[u'.bzr', u'file1', u'file2']
1160
from shutil import copytree
1161
from bzrlib.osutils import mkdtemp
1164
copytree(self.base, base, symlinks=True)
1166
transport=bzrlib.transport.local.ScratchTransport(base))
1169
class Converter(object):
1170
"""Converts a disk format object from one format to another."""
1172
def convert(self, to_convert, pb):
1173
"""Perform the conversion of to_convert, giving feedback via pb.
1175
:param to_convert: The disk object to convert.
1176
:param pb: a progress bar to use for progress information.
1180
class ConvertBzrDir4To5(Converter):
1181
"""Converts format 4 bzr dirs to format 5."""
1184
super(ConvertBzrDir4To5, self).__init__()
1185
self.converted_revs = set()
1186
self.absent_revisions = set()
1190
def convert(self, to_convert, pb):
1191
"""See Converter.convert()."""
1192
self.bzrdir = to_convert
1194
self.pb.note('starting upgrade from format 4 to 5')
1195
if isinstance(self.bzrdir.transport, LocalTransport):
1196
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1197
self._convert_to_weaves()
1198
return BzrDir.open(self.bzrdir.root_transport.base)
1200
def _convert_to_weaves(self):
1201
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1204
stat = self.bzrdir.transport.stat('weaves')
1205
if not S_ISDIR(stat.st_mode):
1206
self.bzrdir.transport.delete('weaves')
1207
self.bzrdir.transport.mkdir('weaves')
1208
except errors.NoSuchFile:
1209
self.bzrdir.transport.mkdir('weaves')
1210
self.inv_weave = Weave('inventory')
1211
# holds in-memory weaves for all files
1212
self.text_weaves = {}
1213
self.bzrdir.transport.delete('branch-format')
1214
self.branch = self.bzrdir.open_branch()
1215
self._convert_working_inv()
1216
rev_history = self.branch.revision_history()
1217
# to_read is a stack holding the revisions we still need to process;
1218
# appending to it adds new highest-priority revisions
1219
self.known_revisions = set(rev_history)
1220
self.to_read = rev_history[-1:]
1222
rev_id = self.to_read.pop()
1223
if (rev_id not in self.revisions
1224
and rev_id not in self.absent_revisions):
1225
self._load_one_rev(rev_id)
1227
to_import = self._make_order()
1228
for i, rev_id in enumerate(to_import):
1229
self.pb.update('converting revision', i, len(to_import))
1230
self._convert_one_rev(rev_id)
1232
self._write_all_weaves()
1233
self._write_all_revs()
1234
self.pb.note('upgraded to weaves:')
1235
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1236
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1237
self.pb.note(' %6d texts', self.text_count)
1238
self._cleanup_spare_files_after_format4()
1239
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1241
def _cleanup_spare_files_after_format4(self):
1242
# FIXME working tree upgrade foo.
1243
for n in 'merged-patches', 'pending-merged-patches':
1245
## assert os.path.getsize(p) == 0
1246
self.bzrdir.transport.delete(n)
1247
except errors.NoSuchFile:
1249
self.bzrdir.transport.delete_tree('inventory-store')
1250
self.bzrdir.transport.delete_tree('text-store')
1252
def _convert_working_inv(self):
1253
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1254
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1255
# FIXME inventory is a working tree change.
1256
self.branch.control_files.put('inventory', new_inv_xml)
1258
def _write_all_weaves(self):
1259
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1260
weave_transport = self.bzrdir.transport.clone('weaves')
1261
weaves = WeaveStore(weave_transport, prefixed=False)
1262
transaction = PassThroughTransaction()
1264
controlweaves.put_weave('inventory', self.inv_weave, transaction)
1267
for file_id, file_weave in self.text_weaves.items():
1268
self.pb.update('writing weave', i, len(self.text_weaves))
1269
weaves.put_weave(file_id, file_weave, transaction)
1274
def _write_all_revs(self):
1275
"""Write all revisions out in new form."""
1276
self.bzrdir.transport.delete_tree('revision-store')
1277
self.bzrdir.transport.mkdir('revision-store')
1278
revision_transport = self.bzrdir.transport.clone('revision-store')
1280
revision_store = TextStore(revision_transport,
1284
for i, rev_id in enumerate(self.converted_revs):
1285
self.pb.update('write revision', i, len(self.converted_revs))
1286
rev_tmp = StringIO()
1287
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
1289
revision_store.add(rev_tmp, rev_id)
1293
def _load_one_rev(self, rev_id):
1294
"""Load a revision object into memory.
1296
Any parents not either loaded or abandoned get queued to be
1298
self.pb.update('loading revision',
1299
len(self.revisions),
1300
len(self.known_revisions))
1301
if not self.branch.repository.revision_store.has_id(rev_id):
1303
self.pb.note('revision {%s} not present in branch; '
1304
'will be converted as a ghost',
1306
self.absent_revisions.add(rev_id)
1308
rev_xml = self.branch.repository.revision_store.get(rev_id).read()
1309
rev = serializer_v4.read_revision_from_string(rev_xml)
1310
for parent_id in rev.parent_ids:
1311
self.known_revisions.add(parent_id)
1312
self.to_read.append(parent_id)
1313
self.revisions[rev_id] = rev
1315
def _load_old_inventory(self, rev_id):
1316
assert rev_id not in self.converted_revs
1317
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1318
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1319
rev = self.revisions[rev_id]
1320
if rev.inventory_sha1:
1321
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1322
'inventory sha mismatch for {%s}' % rev_id
1325
def _load_updated_inventory(self, rev_id):
1326
assert rev_id in self.converted_revs
1327
inv_xml = self.inv_weave.get_text(rev_id)
1328
inv = serializer_v5.read_inventory_from_string(inv_xml)
1331
def _convert_one_rev(self, rev_id):
1332
"""Convert revision and all referenced objects to new format."""
1333
rev = self.revisions[rev_id]
1334
inv = self._load_old_inventory(rev_id)
1335
present_parents = [p for p in rev.parent_ids
1336
if p not in self.absent_revisions]
1337
self._convert_revision_contents(rev, inv, present_parents)
1338
self._store_new_weave(rev, inv, present_parents)
1339
self.converted_revs.add(rev_id)
1341
def _store_new_weave(self, rev, inv, present_parents):
1342
# the XML is now updated with text versions
1346
if ie.kind == 'root_directory':
1348
assert hasattr(ie, 'revision'), \
1349
'no revision on {%s} in {%s}' % \
1350
(file_id, rev.revision_id)
1351
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1352
new_inv_sha1 = sha_string(new_inv_xml)
1353
self.inv_weave.add(rev.revision_id,
1355
new_inv_xml.splitlines(True),
1357
rev.inventory_sha1 = new_inv_sha1
1359
def _convert_revision_contents(self, rev, inv, present_parents):
1360
"""Convert all the files within a revision.
1362
Also upgrade the inventory to refer to the text revision ids."""
1363
rev_id = rev.revision_id
1364
mutter('converting texts of revision {%s}',
1366
parent_invs = map(self._load_updated_inventory, present_parents)
1369
self._convert_file_version(rev, ie, parent_invs)
1371
def _convert_file_version(self, rev, ie, parent_invs):
1372
"""Convert one version of one file.
1374
The file needs to be added into the weave if it is a merge
1375
of >=2 parents or if it's changed from its parent.
1377
if ie.kind == 'root_directory':
1379
file_id = ie.file_id
1380
rev_id = rev.revision_id
1381
w = self.text_weaves.get(file_id)
1384
self.text_weaves[file_id] = w
1385
text_changed = False
1386
previous_entries = ie.find_previous_heads(parent_invs, w)
1387
for old_revision in previous_entries:
1388
# if this fails, its a ghost ?
1389
assert old_revision in self.converted_revs
1390
self.snapshot_ie(previous_entries, ie, w, rev_id)
1392
assert getattr(ie, 'revision', None) is not None
1394
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1395
# TODO: convert this logic, which is ~= snapshot to
1396
# a call to:. This needs the path figured out. rather than a work_tree
1397
# a v4 revision_tree can be given, or something that looks enough like
1398
# one to give the file content to the entry if it needs it.
1399
# and we need something that looks like a weave store for snapshot to
1401
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1402
if len(previous_revisions) == 1:
1403
previous_ie = previous_revisions.values()[0]
1404
if ie._unchanged(previous_ie):
1405
ie.revision = previous_ie.revision
1407
parent_indexes = map(w.lookup, previous_revisions)
1409
text = self.branch.repository.text_store.get(ie.text_id)
1410
file_lines = text.readlines()
1411
assert sha_strings(file_lines) == ie.text_sha1
1412
assert sum(map(len, file_lines)) == ie.text_size
1413
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1414
self.text_count += 1
1416
w.add(rev_id, parent_indexes, [], None)
1417
ie.revision = rev_id
1419
def _make_order(self):
1420
"""Return a suitable order for importing revisions.
1422
The order must be such that an revision is imported after all
1423
its (present) parents.
1425
todo = set(self.revisions.keys())
1426
done = self.absent_revisions.copy()
1429
# scan through looking for a revision whose parents
1431
for rev_id in sorted(list(todo)):
1432
rev = self.revisions[rev_id]
1433
parent_ids = set(rev.parent_ids)
1434
if parent_ids.issubset(done):
1435
# can take this one now
1436
order.append(rev_id)
1442
class ConvertBzrDir5To6(Converter):
1443
"""Converts format 5 bzr dirs to format 6."""
1445
def convert(self, to_convert, pb):
1446
"""See Converter.convert()."""
1447
self.bzrdir = to_convert
1449
self.pb.note('starting upgrade from format 5 to 6')
1450
self._convert_to_prefixed()
1451
return BzrDir.open(self.bzrdir.root_transport.base)
1453
def _convert_to_prefixed(self):
1454
from bzrlib.store import hash_prefix
1455
self.bzrdir.transport.delete('branch-format')
1456
for store_name in ["weaves", "revision-store"]:
1457
self.pb.note("adding prefixes to %s" % store_name)
1458
store_transport = self.bzrdir.transport.clone(store_name)
1459
for filename in store_transport.list_dir('.'):
1460
if (filename.endswith(".weave") or
1461
filename.endswith(".gz") or
1462
filename.endswith(".sig")):
1463
file_id = os.path.splitext(filename)[0]
1466
prefix_dir = hash_prefix(file_id)
1467
# FIXME keep track of the dirs made RBC 20060121
1469
store_transport.move(filename, prefix_dir + '/' + filename)
1470
except errors.NoSuchFile: # catches missing dirs strangely enough
1471
store_transport.mkdir(prefix_dir)
1472
store_transport.move(filename, prefix_dir + '/' + filename)
1473
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1476
class ConvertBzrDir6ToMeta(Converter):
1477
"""Converts format 6 bzr dirs to metadirs."""
1479
def convert(self, to_convert, pb):
1480
"""See Converter.convert()."""
1481
self.bzrdir = to_convert
1484
self.total = 20 # the steps we know about
1485
self.garbage_inventories = []
1487
self.pb.note('starting upgrade from format 6 to metadir')
1488
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1489
# its faster to move specific files around than to open and use the apis...
1490
# first off, nuke ancestry.weave, it was never used.
1492
self.step('Removing ancestry.weave')
1493
self.bzrdir.transport.delete('ancestry.weave')
1494
except errors.NoSuchFile:
1496
# find out whats there
1497
self.step('Finding branch files')
1498
last_revision = self.bzrdir.open_workingtree().last_revision()
1499
bzrcontents = self.bzrdir.transport.list_dir('.')
1500
for name in bzrcontents:
1501
if name.startswith('basis-inventory.'):
1502
self.garbage_inventories.append(name)
1503
# create new directories for repository, working tree and branch
1504
dir_mode = self.bzrdir._control_files._dir_mode
1505
self.file_mode = self.bzrdir._control_files._file_mode
1506
repository_names = [('inventory.weave', True),
1507
('revision-store', True),
1509
self.step('Upgrading repository ')
1510
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1511
self.make_lock('repository')
1512
# we hard code the formats here because we are converting into
1513
# the meta format. The meta format upgrader can take this to a
1514
# future format within each component.
1515
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1516
for entry in repository_names:
1517
self.move_entry('repository', entry)
1519
self.step('Upgrading branch ')
1520
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1521
self.make_lock('branch')
1522
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1523
branch_files = [('revision-history', True),
1524
('branch-name', True),
1526
for entry in branch_files:
1527
self.move_entry('branch', entry)
1529
self.step('Upgrading working tree')
1530
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1531
self.make_lock('checkout')
1532
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1533
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1534
checkout_files = [('pending-merges', True),
1535
('inventory', True),
1536
('stat-cache', False)]
1537
for entry in checkout_files:
1538
self.move_entry('checkout', entry)
1539
if last_revision is not None:
1540
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1542
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1543
return BzrDir.open(self.bzrdir.root_transport.base)
1545
def make_lock(self, name):
1546
"""Make a lock for the new control dir name."""
1547
self.step('Make %s lock' % name)
1548
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1550
def move_entry(self, new_dir, entry):
1551
"""Move then entry name into new_dir."""
1553
mandatory = entry[1]
1554
self.step('Moving %s' % name)
1556
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1557
except errors.NoSuchFile:
1561
def put_format(self, dirname, format):
1562
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1564
def step(self, message):
1565
"""Update the pb by a step."""
1567
self.pb.update('Upgrading repository ', self.count, self.total)