23
23
# TODO: remove unittest dependency; put that stuff inside the test suite
25
from copy import deepcopy
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
26
31
from cStringIO import StringIO
34
from bzrlib.lazy_import import lazy_import
35
lazy_import(globals(), """
36
from copy import deepcopy
28
37
from stat import S_ISDIR
29
from unittest import TestSuite
32
import bzrlib.errors as errors
33
from bzrlib.lockable_files import LockableFiles, TransportLock
34
from bzrlib.lockdir import LockDir
45
revision as _mod_revision,
35
50
from bzrlib.osutils import (
42
import bzrlib.revision
43
55
from bzrlib.store.revision.text import TextRevisionStore
44
56
from bzrlib.store.text import TextStore
45
57
from bzrlib.store.versioned import WeaveStore
46
from bzrlib.trace import mutter
47
58
from bzrlib.transactions import WriteTransaction
48
59
from bzrlib.transport import get_transport
60
from bzrlib.weave import Weave
63
from bzrlib.trace import mutter
49
64
from bzrlib.transport.local import LocalTransport
50
import bzrlib.urlutils as urlutils
51
from bzrlib.weave import Weave
52
from bzrlib.xml4 import serializer_v4
56
67
class BzrDir(object):
87
98
"""Return true if this bzrdir is one whose format we can convert from."""
101
def check_conversion_target(self, target_format):
102
target_repo_format = target_format.repository_format
103
source_repo_format = self._format.repository_format
104
source_repo_format.check_conversion_target(target_repo_format)
91
107
def _check_supported(format, allow_unsupported):
92
108
"""Check whether format is a supported format.
177
193
def _make_tail(self, url):
178
194
head, tail = urlutils.split(url)
179
195
if tail and tail != '.':
180
t = bzrlib.transport.get_transport(head)
196
t = get_transport(head)
183
199
except errors.FileExists:
293
309
This will use the current default BzrDirFormat, and use whatever
294
310
repository format that that uses for bzrdirformat.create_repository.
296
;param shared: Create a shared repository rather than a standalone
312
:param shared: Create a shared repository rather than a standalone
298
314
The Repository object is returned.
314
330
repository format that that uses for bzrdirformat.create_workingtree,
315
331
create_branch and create_repository.
317
The WorkingTree object is returned.
333
:return: The WorkingTree object.
319
335
t = get_transport(safe_unicode(base))
320
336
if not isinstance(t, LocalTransport):
331
347
raise NotImplementedError(self.create_workingtree)
349
def destroy_workingtree(self):
350
"""Destroy the working tree at this BzrDir.
352
Formats that do not support this may raise UnsupportedOperation.
354
raise NotImplementedError(self.destroy_workingtree)
356
def destroy_workingtree_metadata(self):
357
"""Destroy the control files for the working tree at this BzrDir.
359
The contents of working tree files are not affected.
360
Formats that do not support this may raise UnsupportedOperation.
362
raise NotImplementedError(self.destroy_workingtree_metadata)
333
364
def find_repository(self):
334
365
"""Find the repository that should be used for a_bzrdir.
461
492
_unsupported is a private parameter to the BzrDir class.
463
494
t = get_transport(base)
464
# mutter("trying to open %r with transport %r", base, t)
465
format = BzrDirFormat.find_format(t)
495
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
498
def open_from_transport(transport, _unsupported=False):
499
"""Open a bzrdir within a particular directory.
501
:param transport: Transport containing the bzrdir.
502
:param _unsupported: private.
504
format = BzrDirFormat.find_format(transport)
466
505
BzrDir._check_supported(format, _unsupported)
467
return format.open(t, _found=True)
506
return format.open(transport, _found=True)
469
508
def open_branch(self, unsupported=False):
470
509
"""Open the branch object at this BzrDir if one is present.
504
543
url = a_transport.base
507
format = BzrDirFormat.find_format(a_transport)
508
BzrDir._check_supported(format, False)
509
return format.open(a_transport), urlutils.unescape(a_transport.relpath(url))
546
result = BzrDir.open_from_transport(a_transport)
547
return result, urlutils.unescape(a_transport.relpath(url))
510
548
except errors.NotBranchError, e:
511
## mutter('not a branch in: %r %s', a_transport.base, e)
513
550
new_t = a_transport.clone('..')
514
551
if new_t.base == a_transport.base:
564
601
except errors.NoWorkingTree:
604
def cloning_metadir(self, basis=None):
605
"""Produce a metadir suitable for cloning with"""
606
def related_repository(bzrdir):
608
branch = bzrdir.open_branch()
609
return branch.repository
610
except errors.NotBranchError:
612
return bzrdir.open_repository()
613
result_format = self._format.__class__()
616
source_repository = related_repository(self)
617
except errors.NoRepositoryPresent:
620
source_repository = related_repository(self)
621
result_format.repository_format = source_repository._format
622
except errors.NoRepositoryPresent:
567
626
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
568
627
"""Create a copy of this bzrdir prepared for use as a new line of
579
638
itself to download less data.
581
640
self._make_tail(url)
582
result = self._format.initialize(url)
641
cloning_format = self.cloning_metadir(basis)
642
result = cloning_format.initialize(url)
583
643
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
585
645
source_branch = self.open_branch()
625
685
# TODO: jam 20060426 we probably need a test in here in the
626
686
# case that the newly sprouted branch is a remote one
627
687
if result_repo is None or result_repo.make_working_trees():
628
result.create_workingtree()
688
wt = result.create_workingtree()
689
if wt.inventory.root is None:
691
wt.set_root_id(self.open_workingtree.get_root_id())
692
except errors.NoWorkingTree:
635
700
def __init__(self, _transport, _format):
636
701
"""See BzrDir.__init__."""
637
702
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
638
assert self._format._lock_class == TransportLock
703
assert self._format._lock_class == lockable_files.TransportLock
639
704
assert self._format._lock_file_name == 'branch-lock'
640
self._control_files = LockableFiles(self.get_branch_transport(None),
705
self._control_files = lockable_files.LockableFiles(
706
self.get_branch_transport(None),
641
707
self._format._lock_file_name,
642
708
self._format._lock_class)
687
753
# done on this format anyway. So - acceptable wart.
688
754
result = self.open_workingtree()
689
755
if revision_id is not None:
690
if revision_id == bzrlib.revision.NULL_REVISION:
756
if revision_id == _mod_revision.NULL_REVISION:
691
757
result.set_parent_ids([])
693
759
result.set_parent_ids([revision_id])
762
def destroy_workingtree(self):
763
"""See BzrDir.destroy_workingtree."""
764
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
766
def destroy_workingtree_metadata(self):
767
"""See BzrDir.destroy_workingtree_metadata."""
768
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
696
771
def get_branch_transport(self, branch_format):
697
772
"""See BzrDir.get_branch_transport()."""
698
773
if branch_format is None:
838
913
from bzrlib.workingtree import WorkingTreeFormat
839
914
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
916
def destroy_workingtree(self):
917
"""See BzrDir.destroy_workingtree."""
918
wt = self.open_workingtree()
919
repository = wt.branch.repository
920
empty = repository.revision_tree(bzrlib.revision.NULL_REVISION)
921
wt.revert([], old_tree=empty)
922
self.destroy_workingtree_metadata()
924
def destroy_workingtree_metadata(self):
925
self.transport.delete_tree('checkout')
841
927
def _get_mkdir_mode(self):
842
928
"""Figure out the mode to use when creating a bzrdir subdir."""
843
temp_control = LockableFiles(self.transport, '', TransportLock)
929
temp_control = lockable_files.LockableFiles(self.transport, '',
930
lockable_files.TransportLock)
844
931
return temp_control._dir_mode
846
933
def get_branch_transport(self, branch_format):
1022
1109
"""Initialize a new bzrdir in the base directory of a Transport."""
1023
1110
# Since we don't have a .bzr directory, inherit the
1024
1111
# mode from the root directory
1025
temp_control = LockableFiles(transport, '', TransportLock)
1112
temp_control = lockable_files.LockableFiles(transport,
1113
'', lockable_files.TransportLock)
1026
1114
temp_control._transport.mkdir('.bzr',
1027
1115
# FIXME: RBC 20060121 don't peek under
1037
1125
('branch-format', self.get_format_string()),
1039
1127
# NB: no need to escape relative paths that are url safe.
1040
control_files = LockableFiles(control, self._lock_file_name,
1128
control_files = lockable_files.LockableFiles(control,
1129
self._lock_file_name, self._lock_class)
1042
1130
control_files.create_lock()
1043
1131
control_files.lock_write()
1148
def same_model(self, target_format):
1149
return (self.repository_format.rich_root_data ==
1150
target_format.rich_root_data)
1061
1153
def known_formats(klass):
1062
1154
"""Return all the known formats.
1143
1235
removed in format 5; write support for this format has been removed.
1146
_lock_class = TransportLock
1238
_lock_class = lockable_files.TransportLock
1148
1240
def get_format_string(self):
1149
1241
"""See BzrDirFormat.get_format_string()."""
1178
1270
def __return_repository_format(self):
1179
1271
"""Circular import protection."""
1180
1272
from bzrlib.repository import RepositoryFormat4
1181
return RepositoryFormat4(self)
1273
return RepositoryFormat4()
1182
1274
repository_format = property(__return_repository_format)
1193
1285
Unhashed stores in the repository.
1196
_lock_class = TransportLock
1288
_lock_class = lockable_files.TransportLock
1198
1290
def get_format_string(self):
1199
1291
"""See BzrDirFormat.get_format_string()."""
1238
1330
def __return_repository_format(self):
1239
1331
"""Circular import protection."""
1240
1332
from bzrlib.repository import RepositoryFormat5
1241
return RepositoryFormat5(self)
1333
return RepositoryFormat5()
1242
1334
repository_format = property(__return_repository_format)
1252
1344
- Format 6 repositories [always]
1255
_lock_class = TransportLock
1347
_lock_class = lockable_files.TransportLock
1257
1349
def get_format_string(self):
1258
1350
"""See BzrDirFormat.get_format_string()."""
1297
1389
def __return_repository_format(self):
1298
1390
"""Circular import protection."""
1299
1391
from bzrlib.repository import RepositoryFormat6
1300
return RepositoryFormat6(self)
1392
return RepositoryFormat6()
1301
1393
repository_format = property(__return_repository_format)
1312
1404
- Format 7 repositories [optional]
1315
_lock_class = LockDir
1407
_lock_class = lockdir.LockDir
1317
1409
def get_converter(self, format=None):
1318
1410
"""See BzrDirFormat.get_converter()."""
1476
1568
self.bzrdir.transport.delete_tree('text-store')
1478
1570
def _convert_working_inv(self):
1479
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1480
new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1571
inv = xml4.serializer_v4.read_inventory(
1572
self.branch.control_files.get('inventory'))
1573
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1481
1574
# FIXME inventory is a working tree change.
1482
1575
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1509
1602
prefixed=False,
1510
1603
compressed=True))
1512
transaction = bzrlib.transactions.WriteTransaction()
1605
transaction = WriteTransaction()
1513
1606
for i, rev_id in enumerate(self.converted_revs):
1514
1607
self.pb.update('write revision', i, len(self.converted_revs))
1515
1608
_revision_store.add_revision(self.revisions[rev_id], transaction)
1541
1634
def _load_old_inventory(self, rev_id):
1542
1635
assert rev_id not in self.converted_revs
1543
1636
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1544
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1637
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1638
inv.revision_id = rev_id
1545
1639
rev = self.revisions[rev_id]
1546
1640
if rev.inventory_sha1:
1547
1641
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1551
1645
def _load_updated_inventory(self, rev_id):
1552
1646
assert rev_id in self.converted_revs
1553
1647
inv_xml = self.inv_weave.get_text(rev_id)
1554
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(inv_xml)
1648
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1557
1651
def _convert_one_rev(self, rev_id):
1570
1664
entries = inv.iter_entries()
1572
1666
for path, ie in entries:
1573
assert hasattr(ie, 'revision'), \
1667
assert getattr(ie, 'revision', None) is not None, \
1574
1668
'no revision on {%s} in {%s}' % \
1575
1669
(file_id, rev.revision_id)
1576
new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1670
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1577
1671
new_inv_sha1 = sha_string(new_inv_xml)
1578
1672
self.inv_weave.add_lines(rev.revision_id,
1579
1673
present_parents,
1613
1707
for old_revision in previous_entries:
1614
1708
# if this fails, its a ghost ?
1615
assert old_revision in self.converted_revs
1709
assert old_revision in self.converted_revs, \
1710
"Revision {%s} not in converted_revs" % old_revision
1616
1711
self.snapshot_ie(previous_entries, ie, w, rev_id)
1618
1713
assert getattr(ie, 'revision', None) is not None
1791
1886
def make_lock(self, name):
1792
1887
"""Make a lock for the new control dir name."""
1793
1888
self.step('Make %s lock' % name)
1794
ld = LockDir(self.bzrdir.transport,
1796
file_modebits=self.file_mode,
1797
dir_modebits=self.dir_mode)
1889
ld = lockdir.LockDir(self.bzrdir.transport,
1891
file_modebits=self.file_mode,
1892
dir_modebits=self.dir_mode)
1800
1895
def move_entry(self, new_dir, entry):