1
1
# Copyright (C) 2005, 2006 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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
23
31
from copy import deepcopy
32
from cStringIO import StringIO
25
from cStringIO import StringIO
34
from stat import S_ISDIR
26
35
from unittest import TestSuite
48
import bzrlib.revision
39
49
from bzrlib.store.revision.text import TextRevisionStore
40
50
from bzrlib.store.text import TextStore
41
51
from bzrlib.store.versioned import WeaveStore
42
from bzrlib.symbol_versioning import *
43
52
from bzrlib.trace import mutter
44
53
from bzrlib.transactions import WriteTransaction
45
54
from bzrlib.transport import get_transport
84
93
"""Return true if this bzrdir is one whose format we can convert from."""
96
def check_conversion_target(self, target_format):
97
target_repo_format = target_format.repository_format
98
source_repo_format = self._format.repository_format
99
source_repo_format.check_conversion_target(target_repo_format)
88
102
def _check_supported(format, allow_unsupported):
89
103
"""Check whether format is a supported format.
93
107
if not allow_unsupported and not format.is_supported():
94
108
# see open_downlevel to open legacy branches.
95
raise errors.UnsupportedFormatError(
96
'sorry, format %s not supported' % format,
97
['use a different bzr version',
98
'or remove the .bzr directory'
99
' and "bzr init" again'])
109
raise errors.UnsupportedFormatError(format=format)
101
111
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
102
112
"""Clone this bzrdir and its contents to url verbatim.
294
304
This will use the current default BzrDirFormat, and use whatever
295
305
repository format that that uses for bzrdirformat.create_repository.
297
;param shared: Create a shared repository rather than a standalone
307
:param shared: Create a shared repository rather than a standalone
299
309
The Repository object is returned.
315
325
repository format that that uses for bzrdirformat.create_workingtree,
316
326
create_branch and create_repository.
318
The WorkingTree object is returned.
328
:return: The WorkingTree object.
320
330
t = get_transport(safe_unicode(base))
321
331
if not isinstance(t, LocalTransport):
462
472
_unsupported is a private parameter to the BzrDir class.
464
474
t = get_transport(base)
465
mutter("trying to open %r with transport %r", base, t)
466
format = BzrDirFormat.find_format(t)
475
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
478
def open_from_transport(transport, _unsupported=False):
479
"""Open a bzrdir within a particular directory.
481
:param transport: Transport containing the bzrdir.
482
:param _unsupported: private.
484
format = BzrDirFormat.find_format(transport)
467
485
BzrDir._check_supported(format, _unsupported)
468
return format.open(t, _found=True)
486
return format.open(transport, _found=True)
470
488
def open_branch(self, unsupported=False):
471
489
"""Open the branch object at this BzrDir if one is present.
505
523
url = a_transport.base
508
format = BzrDirFormat.find_format(a_transport)
509
BzrDir._check_supported(format, False)
510
return format.open(a_transport), urlutils.unescape(a_transport.relpath(url))
526
result = BzrDir.open_from_transport(a_transport)
527
return result, urlutils.unescape(a_transport.relpath(url))
511
528
except errors.NotBranchError, e:
512
## mutter('not a branch in: %r %s', a_transport.base, e)
514
530
new_t = a_transport.clone('..')
515
531
if new_t.base == a_transport.base:
565
581
except errors.NoWorkingTree:
584
def cloning_metadir(self, basis=None):
585
"""Produce a metadir suitable for cloning with"""
586
def related_repository(bzrdir):
588
branch = bzrdir.open_branch()
589
return branch.repository
590
except errors.NotBranchError:
592
return bzrdir.open_repository()
593
result_format = self._format.__class__()
596
source_repository = related_repository(self)
597
except errors.NoRepositoryPresent:
600
source_repository = related_repository(self)
601
result_format.repository_format = source_repository._format
602
except errors.NoRepositoryPresent:
568
606
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
569
607
"""Create a copy of this bzrdir prepared for use as a new line of
580
618
itself to download less data.
582
620
self._make_tail(url)
583
result = self._format.initialize(url)
621
cloning_format = self.cloning_metadir(basis)
622
result = cloning_format.initialize(url)
584
623
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
586
625
source_branch = self.open_branch()
617
656
# XXX FIXME RBC 20060214 need tests for this when the basis
619
658
result_repo.fetch(basis_repo, revision_id=revision_id)
620
result_repo.fetch(source_repository, revision_id=revision_id)
659
if source_repository is not None:
660
result_repo.fetch(source_repository, revision_id=revision_id)
621
661
if source_branch is not None:
622
662
source_branch.sprout(result, revision_id=revision_id)
687
727
# done on this format anyway. So - acceptable wart.
688
728
result = self.open_workingtree()
689
729
if revision_id is not None:
690
result.set_last_revision(revision_id)
730
if revision_id == bzrlib.revision.NULL_REVISION:
731
result.set_parent_ids([])
733
result.set_parent_ids([revision_id])
693
736
def get_branch_transport(self, branch_format):
735
778
self._check_supported(format, unsupported)
736
779
return format.open(self, _found=True)
738
def sprout(self, url, revision_id=None, basis=None):
781
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
739
782
"""See BzrDir.sprout()."""
740
783
from bzrlib.workingtree import WorkingTreeFormat2
741
784
self._make_tail(url)
972
1015
"""Return the .bzrdir style transport present at URL."""
974
1017
format_string = transport.get(".bzr/branch-format").read()
1018
except errors.NoSuchFile:
1019
raise errors.NotBranchError(path=transport.base)
975
1022
return klass._formats[format_string]
976
except errors.NoSuchFile:
977
raise errors.NotBranchError(path=transport.base)
978
1023
except KeyError:
979
raise errors.UnknownFormatError(format_string)
1024
raise errors.UnknownFormatError(format=format_string)
982
1027
def get_default_format(klass):
1173
1222
def __return_repository_format(self):
1174
1223
"""Circular import protection."""
1175
1224
from bzrlib.repository import RepositoryFormat4
1176
return RepositoryFormat4(self)
1225
return RepositoryFormat4()
1177
1226
repository_format = property(__return_repository_format)
1217
1266
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1218
1267
RepositoryFormat5().initialize(result, _internal=True)
1219
1268
if not _cloning:
1220
BzrBranchFormat4().initialize(result)
1221
WorkingTreeFormat2().initialize(result)
1269
branch = BzrBranchFormat4().initialize(result)
1271
WorkingTreeFormat2().initialize(result)
1272
except errors.NotLocalUrl:
1273
# Even though we can't access the working tree, we need to
1274
# create its control files.
1275
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1224
1278
def _open(self, transport):
1228
1282
def __return_repository_format(self):
1229
1283
"""Circular import protection."""
1230
1284
from bzrlib.repository import RepositoryFormat5
1231
return RepositoryFormat5(self)
1285
return RepositoryFormat5()
1232
1286
repository_format = property(__return_repository_format)
1271
1325
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1272
1326
RepositoryFormat6().initialize(result, _internal=True)
1273
1327
if not _cloning:
1274
BzrBranchFormat4().initialize(result)
1328
branch = BzrBranchFormat4().initialize(result)
1276
1330
WorkingTreeFormat2().initialize(result)
1277
1331
except errors.NotLocalUrl:
1278
# emulate pre-check behaviour for working tree and silently
1332
# Even though we can't access the working tree, we need to
1333
# create its control files.
1334
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1283
1337
def _open(self, transport):
1287
1341
def __return_repository_format(self):
1288
1342
"""Circular import protection."""
1289
1343
from bzrlib.repository import RepositoryFormat6
1290
return RepositoryFormat6(self)
1344
return RepositoryFormat6()
1291
1345
repository_format = property(__return_repository_format)
1379
class ScratchDir(BzrDir6):
1380
"""Special test class: a bzrdir that cleans up itself..
1382
>>> d = ScratchDir()
1383
>>> base = d.transport.base
1386
>>> b.transport.__del__()
1391
def __init__(self, files=[], dirs=[], transport=None):
1392
"""Make a test branch.
1394
This creates a temporary directory and runs init-tree in it.
1396
If any files are listed, they are created in the working copy.
1398
if transport is None:
1399
transport = bzrlib.transport.local.ScratchTransport()
1400
# local import for scope restriction
1401
BzrDirFormat6().initialize(transport.base)
1402
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1403
self.create_repository()
1404
self.create_branch()
1405
self.create_workingtree()
1407
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1409
# BzrBranch creates a clone to .bzr and then forgets about the
1410
# original transport. A ScratchTransport() deletes itself and
1411
# everything underneath it when it goes away, so we need to
1412
# grab a local copy to prevent that from happening
1413
self._transport = transport
1416
self._transport.mkdir(d)
1419
self._transport.put(f, 'content of %s' % f)
1423
>>> orig = ScratchDir(files=["file1", "file2"])
1424
>>> os.listdir(orig.base)
1425
[u'.bzr', u'file1', u'file2']
1426
>>> clone = orig.clone()
1427
>>> if os.name != 'nt':
1428
... os.path.samefile(orig.base, clone.base)
1430
... orig.base == clone.base
1433
>>> os.listdir(clone.base)
1434
[u'.bzr', u'file1', u'file2']
1436
from shutil import copytree
1437
from bzrlib.osutils import mkdtemp
1440
copytree(self.base, base, symlinks=True)
1442
transport=bzrlib.transport.local.ScratchTransport(base))
1445
1433
class Converter(object):
1446
1434
"""Converts a disk format object from one format to another."""
1535
1523
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1536
1524
new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1537
1525
# FIXME inventory is a working tree change.
1538
self.branch.control_files.put('inventory', new_inv_xml)
1526
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1540
1528
def _write_all_weaves(self):
1541
1529
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1623
1612
def _store_new_weave(self, rev, inv, present_parents):
1624
1613
# the XML is now updated with text versions
1628
if ie.kind == 'root_directory':
1630
assert hasattr(ie, 'revision'), \
1615
entries = inv.iter_entries()
1617
for path, ie in entries:
1618
assert getattr(ie, 'revision', None) is not None, \
1631
1619
'no revision on {%s} in {%s}' % \
1632
1620
(file_id, rev.revision_id)
1633
1621
new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1645
1633
mutter('converting texts of revision {%s}',
1647
1635
parent_invs = map(self._load_updated_inventory, present_parents)
1636
entries = inv.iter_entries()
1638
for path, ie in entries:
1650
1639
self._convert_file_version(rev, ie, parent_invs)
1652
1641
def _convert_file_version(self, rev, ie, parent_invs):
1811
1798
for entry in branch_files:
1812
1799
self.move_entry('branch', entry)
1814
self.step('Upgrading working tree')
1815
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1816
self.make_lock('checkout')
1817
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1818
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1819
1801
checkout_files = [('pending-merges', True),
1820
1802
('inventory', True),
1821
1803
('stat-cache', False)]
1822
for entry in checkout_files:
1823
self.move_entry('checkout', entry)
1824
if last_revision is not None:
1825
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1827
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1804
# If a mandatory checkout file is not present, the branch does not have
1805
# a functional checkout. Do not create a checkout in the converted
1807
for name, mandatory in checkout_files:
1808
if mandatory and name not in bzrcontents:
1809
has_checkout = False
1813
if not has_checkout:
1814
self.pb.note('No working tree.')
1815
# If some checkout files are there, we may as well get rid of them.
1816
for name, mandatory in checkout_files:
1817
if name in bzrcontents:
1818
self.bzrdir.transport.delete(name)
1820
self.step('Upgrading working tree')
1821
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1822
self.make_lock('checkout')
1824
'checkout', bzrlib.workingtree.WorkingTreeFormat3())
1825
self.bzrdir.transport.delete_multi(
1826
self.garbage_inventories, self.pb)
1827
for entry in checkout_files:
1828
self.move_entry('checkout', entry)
1829
if last_revision is not None:
1830
self.bzrdir._control_files.put_utf8(
1831
'checkout/last-revision', last_revision)
1832
self.bzrdir._control_files.put_utf8(
1833
'branch-format', BzrDirMetaFormat1().get_format_string())
1828
1834
return BzrDir.open(self.bzrdir.root_transport.base)
1830
1836
def make_lock(self, name):