23
# TODO: remove unittest dependency; put that stuff inside the test suite
25
# TODO: The Format probe_transport seems a bit redundant with just trying to
26
# open the bzrdir. -- mbp
28
# TODO: Can we move specific formats into separate modules to make this file
31
from cStringIO import StringIO
35
from bzrlib.lazy_import import lazy_import
36
lazy_import(globals(), """
23
37
from copy import deepcopy
25
from cStringIO import StringIO
26
from unittest import TestSuite
38
from stat import S_ISDIR
29
import bzrlib.errors as errors
30
from bzrlib.lockable_files import LockableFiles, TransportLock
31
from bzrlib.lockdir import LockDir
32
from bzrlib.osutils import safe_unicode
47
revision as _mod_revision,
33
55
from bzrlib.osutils import (
40
60
from bzrlib.store.revision.text import TextRevisionStore
41
61
from bzrlib.store.text import TextStore
42
62
from bzrlib.store.versioned import WeaveStore
43
from bzrlib.symbol_versioning import *
44
from bzrlib.trace import mutter
45
63
from bzrlib.transactions import WriteTransaction
46
from bzrlib.transport import get_transport, urlunescape
64
from bzrlib.transport import get_transport
65
from bzrlib.weave import Weave
68
from bzrlib.trace import mutter, note
47
69
from bzrlib.transport.local import LocalTransport
48
from bzrlib.weave import Weave
49
from bzrlib.xml4 import serializer_v4
53
72
class BzrDir(object):
173
193
basis_repo = None
174
194
return basis_repo, basis_branch, basis_tree
196
# TODO: This should be given a Transport, and should chdir up; otherwise
197
# this will open a new connection.
176
198
def _make_tail(self, url):
177
segments = url.split('/')
178
if segments and segments[-1] not in ('', '.'):
179
parent = '/'.join(segments[:-1])
180
t = bzrlib.transport.get_transport(parent)
199
head, tail = urlutils.split(url)
200
if tail and tail != '.':
201
t = get_transport(head)
182
t.mkdir(segments[-1])
183
204
except errors.FileExists:
207
# TODO: Should take a Transport
187
def create(cls, base):
209
def create(cls, base, format=None):
188
210
"""Create a new BzrDir at the url 'base'.
190
212
This will call the current default formats initialize with base
191
213
as the only parameter.
193
If you need a specific format, consider creating an instance
194
of that and calling initialize().
215
:param format: If supplied, the format of branch to create. If not
216
supplied, the default is used.
196
218
if cls is not BzrDir:
197
raise AssertionError("BzrDir.create always creates the default format, "
198
"not one of %r" % cls)
199
segments = base.split('/')
200
if segments and segments[-1] not in ('', '.'):
201
parent = '/'.join(segments[:-1])
202
t = bzrlib.transport.get_transport(parent)
219
raise AssertionError("BzrDir.create always creates the default"
220
" format, not one of %r" % cls)
221
head, tail = urlutils.split(base)
222
if tail and tail != '.':
223
t = get_transport(head)
204
t.mkdir(segments[-1])
205
226
except errors.FileExists:
207
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
229
format = BzrDirFormat.get_default_format()
230
return format.initialize(safe_unicode(base))
209
232
def create_branch(self):
210
233
"""Create a branch in this BzrDir.
489
560
If there is one and it is either an unrecognised format or an unsupported
490
561
format, UnknownFormatError or UnsupportedFormatError are raised.
491
562
If there is one, it is returned, along with the unused portion of url.
564
:return: The BzrDir that contains the path, and a Unicode path
565
for the rest of the URL.
493
567
# this gets the normalised url back. I.e. '.' -> the full path.
494
568
url = a_transport.base
497
format = BzrDirFormat.find_format(a_transport)
498
BzrDir._check_supported(format, False)
499
return format.open(a_transport), a_transport.relpath(url)
571
result = BzrDir.open_from_transport(a_transport)
572
return result, urlutils.unescape(a_transport.relpath(url))
500
573
except errors.NotBranchError, e:
501
mutter('not a branch in: %r %s', a_transport.base, e)
502
575
new_t = a_transport.clone('..')
503
576
if new_t.base == a_transport.base:
504
577
# reached the root, whatever that may be
505
578
raise errors.NotBranchError(path=url)
506
579
a_transport = new_t
582
def open_containing_tree_or_branch(klass, location):
583
"""Return the branch and working tree contained by a location.
585
Returns (tree, branch, relpath).
586
If there is no tree at containing the location, tree will be None.
587
If there is no branch containing the location, an exception will be
589
relpath is the portion of the path that is contained by the branch.
591
bzrdir, relpath = klass.open_containing(location)
593
tree = bzrdir.open_workingtree()
594
except (errors.NoWorkingTree, errors.NotLocalUrl):
596
branch = bzrdir.open_branch()
599
return tree, branch, relpath
508
601
def open_repository(self, _unsupported=False):
509
602
"""Open the repository object at this BzrDir if one is present.
553
646
except errors.NoWorkingTree:
556
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
649
def _cloning_metadir(self, basis=None):
650
def related_repository(bzrdir):
652
branch = bzrdir.open_branch()
653
return branch.repository
654
except errors.NotBranchError:
656
return bzrdir.open_repository()
657
result_format = self._format.__class__()
660
source_repository = related_repository(self)
661
except errors.NoRepositoryPresent:
664
source_repository = related_repository(self)
665
result_format.repository_format = source_repository._format
666
except errors.NoRepositoryPresent:
667
source_repository = None
669
tree = self.open_workingtree()
670
except (errors.NoWorkingTree, errors.NotLocalUrl):
671
result_format.workingtree_format = None
673
result_format.workingtree_format = tree._format.__class__()
674
return result_format, source_repository
676
def cloning_metadir(self, basis=None):
677
"""Produce a metadir suitable for cloning or sprouting with.
679
These operations may produce workingtrees (yes, even though they're
680
"cloning" something that doesn't have a tree, so a viable workingtree
681
format must be selected.
683
format, repository = self._cloning_metadir()
684
if format._workingtree_format is None:
685
if repository is None:
687
tree_format = repository._format._matchingbzrdir.workingtree_format
688
format.workingtree_format = tree_format.__class__()
691
def checkout_metadir(self):
692
return self.cloning_metadir()
694
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False,
557
696
"""Create a copy of this bzrdir prepared for use as a new line of
605
745
# XXX FIXME RBC 20060214 need tests for this when the basis
607
747
result_repo.fetch(basis_repo, revision_id=revision_id)
608
result_repo.fetch(source_repository, revision_id=revision_id)
748
if source_repository is not None:
749
result_repo.fetch(source_repository, revision_id=revision_id)
609
750
if source_branch is not None:
610
751
source_branch.sprout(result, revision_id=revision_id)
612
753
result.create_branch()
754
# TODO: jam 20060426 we probably need a test in here in the
755
# case that the newly sprouted branch is a remote one
613
756
if result_repo is None or result_repo.make_working_trees():
614
result.create_workingtree()
757
wt = result.create_workingtree()
760
if wt.path2id('') is None:
762
wt.set_root_id(self.open_workingtree.get_root_id())
763
except errors.NoWorkingTree:
769
if recurse == 'down':
771
basis = wt.basis_tree()
773
subtrees = basis.iter_references()
774
recurse_branch = wt.branch
775
elif source_branch is not None:
776
basis = source_branch.basis_tree()
778
subtrees = basis.iter_references()
779
recurse_branch = source_branch
784
for path, file_id in subtrees:
785
target = urlutils.join(url, urlutils.escape(path))
786
sublocation = source_branch.reference_parent(file_id, path)
787
sublocation.bzrdir.sprout(target,
788
basis.get_reference_revision(file_id, path),
789
force_new_repo=force_new_repo, recurse=recurse)
791
if basis is not None:
1310
class ScratchDir(BzrDir6):
1311
"""Special test class: a bzrdir that cleans up itself..
1313
>>> d = ScratchDir()
1314
>>> base = d.transport.base
1317
>>> b.transport.__del__()
1322
def __init__(self, files=[], dirs=[], transport=None):
1323
"""Make a test branch.
1325
This creates a temporary directory and runs init-tree in it.
1327
If any files are listed, they are created in the working copy.
1329
if transport is None:
1330
transport = bzrlib.transport.local.ScratchTransport()
1331
# local import for scope restriction
1332
BzrDirFormat6().initialize(transport.base)
1333
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1334
self.create_repository()
1335
self.create_branch()
1336
self.create_workingtree()
1338
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1340
# BzrBranch creates a clone to .bzr and then forgets about the
1341
# original transport. A ScratchTransport() deletes itself and
1342
# everything underneath it when it goes away, so we need to
1343
# grab a local copy to prevent that from happening
1344
self._transport = transport
1347
self._transport.mkdir(d)
1350
self._transport.put(f, 'content of %s' % f)
1354
>>> orig = ScratchDir(files=["file1", "file2"])
1355
>>> os.listdir(orig.base)
1356
[u'.bzr', u'file1', u'file2']
1357
>>> clone = orig.clone()
1358
>>> if os.name != 'nt':
1359
... os.path.samefile(orig.base, clone.base)
1361
... orig.base == clone.base
1364
>>> os.listdir(clone.base)
1365
[u'.bzr', u'file1', u'file2']
1367
from shutil import copytree
1368
from bzrlib.osutils import mkdtemp
1371
copytree(self.base, base, symlinks=True)
1373
transport=bzrlib.transport.local.ScratchTransport(base))
1376
1638
class Converter(object):
1377
1639
"""Converts a disk format object from one format to another."""
1728
1993
# we hard code the formats here because we are converting into
1729
1994
# the meta format. The meta format upgrader can take this to a
1730
1995
# future format within each component.
1731
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1996
self.put_format('repository', RepositoryFormat7())
1732
1997
for entry in repository_names:
1733
1998
self.move_entry('repository', entry)
1735
2000
self.step('Upgrading branch ')
1736
2001
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1737
2002
self.make_lock('branch')
1738
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
2003
self.put_format('branch', BzrBranchFormat5())
1739
2004
branch_files = [('revision-history', True),
1740
2005
('branch-name', True),
1741
2006
('parent', False)]
1742
2007
for entry in branch_files:
1743
2008
self.move_entry('branch', entry)
1745
self.step('Upgrading working tree')
1746
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1747
self.make_lock('checkout')
1748
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1749
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1750
2010
checkout_files = [('pending-merges', True),
1751
2011
('inventory', True),
1752
2012
('stat-cache', False)]
1753
for entry in checkout_files:
1754
self.move_entry('checkout', entry)
1755
if last_revision is not None:
1756
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1758
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
2013
# If a mandatory checkout file is not present, the branch does not have
2014
# a functional checkout. Do not create a checkout in the converted
2016
for name, mandatory in checkout_files:
2017
if mandatory and name not in bzrcontents:
2018
has_checkout = False
2022
if not has_checkout:
2023
self.pb.note('No working tree.')
2024
# If some checkout files are there, we may as well get rid of them.
2025
for name, mandatory in checkout_files:
2026
if name in bzrcontents:
2027
self.bzrdir.transport.delete(name)
2029
from bzrlib.workingtree import WorkingTreeFormat3
2030
self.step('Upgrading working tree')
2031
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2032
self.make_lock('checkout')
2034
'checkout', WorkingTreeFormat3())
2035
self.bzrdir.transport.delete_multi(
2036
self.garbage_inventories, self.pb)
2037
for entry in checkout_files:
2038
self.move_entry('checkout', entry)
2039
if last_revision is not None:
2040
self.bzrdir._control_files.put_utf8(
2041
'checkout/last-revision', last_revision)
2042
self.bzrdir._control_files.put_utf8(
2043
'branch-format', BzrDirMetaFormat1().get_format_string())
1759
2044
return BzrDir.open(self.bzrdir.root_transport.base)
1761
2046
def make_lock(self, name):
1762
2047
"""Make a lock for the new control dir name."""
1763
2048
self.step('Make %s lock' % name)
1764
ld = LockDir(self.bzrdir.transport,
1766
file_modebits=self.file_mode,
1767
dir_modebits=self.dir_mode)
2049
ld = lockdir.LockDir(self.bzrdir.transport,
2051
file_modebits=self.file_mode,
2052
dir_modebits=self.dir_mode)
1770
2055
def move_entry(self, new_dir, entry):
1809
2094
self.pb.note('starting repository conversion')
1810
2095
converter = CopyConverter(self.target_format.repository_format)
1811
2096
converter.convert(repo, pb)
2098
branch = self.bzrdir.open_branch()
2099
except errors.NotBranchError:
2102
# TODO: conversions of Branch and Tree should be done by
2103
# InterXFormat lookups
2104
# Avoid circular imports
2105
from bzrlib import branch as _mod_branch
2106
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2107
self.target_format.get_branch_format().__class__ is
2108
_mod_branch.BzrBranchFormat6):
2109
branch_converter = _mod_branch.Converter5to6()
2110
branch_converter.convert(branch)
2112
tree = self.bzrdir.open_workingtree()
2113
except (errors.NoWorkingTree, errors.NotLocalUrl):
2116
# TODO: conversions of Branch and Tree should be done by
2117
# InterXFormat lookups
2118
if (isinstance(tree, workingtree.WorkingTree3) and
2119
not isinstance(tree, workingtree_4.WorkingTree4) and
2120
isinstance(self.target_format.workingtree_format,
2121
workingtree_4.WorkingTreeFormat4)):
2122
workingtree_4.Converter3to4().convert(tree)
1812
2123
return to_convert
2126
class BzrDirFormatInfo(object):
2128
def __init__(self, native, deprecated):
2129
self.deprecated = deprecated
2130
self.native = native
2133
class BzrDirFormatRegistry(registry.Registry):
2134
"""Registry of user-selectable BzrDir subformats.
2136
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2137
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2140
def register_metadir(self, key,
2141
repository_format, help, native=True, deprecated=False,
2144
"""Register a metadir subformat.
2146
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2147
by the Repository format.
2149
:param repository_format: The fully-qualified repository format class
2151
:param branch_format: Fully-qualified branch format class name as
2153
:param tree_format: Fully-qualified tree format class name as
2156
# This should be expanded to support setting WorkingTree and Branch
2157
# formats, once BzrDirMetaFormat1 supports that.
2158
def _load(full_name):
2159
mod_name, factory_name = full_name.rsplit('.', 1)
2161
mod = __import__(mod_name, globals(), locals(),
2163
except ImportError, e:
2164
raise ImportError('failed to load %s: %s' % (full_name, e))
2166
factory = getattr(mod, factory_name)
2167
except AttributeError:
2168
raise AttributeError('no factory %s in module %r'
2173
bd = BzrDirMetaFormat1()
2174
if branch_format is not None:
2175
bd.set_branch_format(_load(branch_format))
2176
if tree_format is not None:
2177
bd.workingtree_format = _load(tree_format)
2178
if repository_format is not None:
2179
bd.repository_format = _load(repository_format)
2181
self.register(key, helper, help, native, deprecated)
2183
def register(self, key, factory, help, native=True, deprecated=False):
2184
"""Register a BzrDirFormat factory.
2186
The factory must be a callable that takes one parameter: the key.
2187
It must produce an instance of the BzrDirFormat when called.
2189
This function mainly exists to prevent the info object from being
2192
registry.Registry.register(self, key, factory, help,
2193
BzrDirFormatInfo(native, deprecated))
2195
def register_lazy(self, key, module_name, member_name, help, native=True,
2197
registry.Registry.register_lazy(self, key, module_name, member_name,
2198
help, BzrDirFormatInfo(native, deprecated))
2200
def set_default(self, key):
2201
"""Set the 'default' key to be a clone of the supplied key.
2203
This method must be called once and only once.
2205
registry.Registry.register(self, 'default', self.get(key),
2206
self.get_help(key), info=self.get_info(key))
2208
def set_default_repository(self, key):
2209
"""Set the FormatRegistry default and Repository default.
2211
This is a transitional method while Repository.set_default_format
2214
if 'default' in self:
2215
self.remove('default')
2216
self.set_default(key)
2217
format = self.get('default')()
2218
assert isinstance(format, BzrDirMetaFormat1)
2220
def make_bzrdir(self, key):
2221
return self.get(key)()
2223
def help_topic(self, topic):
2224
output = textwrap.dedent("""\
2225
Bazaar directory formats
2226
------------------------
2228
These formats can be used for creating branches, working trees, and
2232
default_help = self.get_help('default')
2234
for key in self.keys():
2235
if key == 'default':
2237
help = self.get_help(key)
2238
if help == default_help:
2239
default_realkey = key
2241
help_pairs.append((key, help))
2243
def wrapped(key, help, info):
2245
help = '(native) ' + help
2246
return ' %s:\n%s\n\n' % (key,
2247
textwrap.fill(help, initial_indent=' ',
2248
subsequent_indent=' '))
2249
output += wrapped('%s/default' % default_realkey, default_help,
2250
self.get_info('default'))
2251
deprecated_pairs = []
2252
for key, help in help_pairs:
2253
info = self.get_info(key)
2255
deprecated_pairs.append((key, help))
2257
output += wrapped(key, help, info)
2258
if len(deprecated_pairs) > 0:
2259
output += "Deprecated formats\n------------------\n\n"
2260
for key, help in deprecated_pairs:
2261
info = self.get_info(key)
2262
output += wrapped(key, help, info)
2267
format_registry = BzrDirFormatRegistry()
2268
format_registry.register('weave', BzrDirFormat6,
2269
'Pre-0.8 format. Slower than knit and does not'
2270
' support checkouts or shared repositories.',
2272
format_registry.register_metadir('knit',
2273
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2274
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2275
branch_format='bzrlib.branch.BzrBranchFormat5',
2276
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2277
format_registry.register_metadir('metaweave',
2278
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2279
'Transitional format in 0.8. Slower than knit.',
2280
branch_format='bzrlib.branch.BzrBranchFormat5',
2281
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2283
format_registry.register_metadir('dirstate',
2284
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2285
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2286
'above when accessed over the network.',
2287
branch_format='bzrlib.branch.BzrBranchFormat5',
2288
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2289
# directly from workingtree_4 triggers a circular import.
2290
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2292
format_registry.register_metadir('dirstate-with-subtree',
2293
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2294
help='New in 0.15: Fast local operations and improved scaling for '
2295
'network operations. Additionally adds support for versioning nested '
2296
'bzr branches. Incompatible with bzr < 0.15.',
2297
branch_format='bzrlib.branch.BzrBranchFormat6',
2298
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2300
format_registry.set_default('dirstate')