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
55
from bzrlib.store.revision.text import TextRevisionStore
43
56
from bzrlib.store.text import TextStore
44
57
from bzrlib.store.versioned import WeaveStore
45
from bzrlib.trace import mutter
46
58
from bzrlib.transactions import WriteTransaction
47
59
from bzrlib.transport import get_transport
60
from bzrlib.weave import Weave
63
from bzrlib.trace import mutter
48
64
from bzrlib.transport.local import LocalTransport
49
import bzrlib.urlutils as urlutils
50
from bzrlib.weave import Weave
51
from bzrlib.xml4 import serializer_v4
55
67
class BzrDir(object):
86
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)
90
107
def _check_supported(format, allow_unsupported):
91
108
"""Check whether format is a supported format.
330
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)
332
364
def find_repository(self):
333
365
"""Find the repository that should be used for a_bzrdir.
460
492
_unsupported is a private parameter to the BzrDir class.
462
494
t = get_transport(base)
463
mutter("trying to open %r with transport %r", base, t)
464
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)
465
505
BzrDir._check_supported(format, _unsupported)
466
return format.open(t, _found=True)
506
return format.open(transport, _found=True)
468
508
def open_branch(self, unsupported=False):
469
509
"""Open the branch object at this BzrDir if one is present.
503
543
url = a_transport.base
506
format = BzrDirFormat.find_format(a_transport)
507
BzrDir._check_supported(format, False)
508
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))
509
548
except errors.NotBranchError, e:
510
## mutter('not a branch in: %r %s', a_transport.base, e)
512
550
new_t = a_transport.clone('..')
513
551
if new_t.base == a_transport.base:
563
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:
566
626
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
567
627
"""Create a copy of this bzrdir prepared for use as a new line of
615
676
# XXX FIXME RBC 20060214 need tests for this when the basis
617
678
result_repo.fetch(basis_repo, revision_id=revision_id)
618
result_repo.fetch(source_repository, revision_id=revision_id)
679
if source_repository is not None:
680
result_repo.fetch(source_repository, revision_id=revision_id)
619
681
if source_branch is not None:
620
682
source_branch.sprout(result, revision_id=revision_id)
623
685
# TODO: jam 20060426 we probably need a test in here in the
624
686
# case that the newly sprouted branch is a remote one
625
687
if result_repo is None or result_repo.make_working_trees():
626
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:
633
700
def __init__(self, _transport, _format):
634
701
"""See BzrDir.__init__."""
635
702
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
636
assert self._format._lock_class == TransportLock
703
assert self._format._lock_class == lockable_files.TransportLock
637
704
assert self._format._lock_file_name == 'branch-lock'
638
self._control_files = LockableFiles(self.get_branch_transport(None),
705
self._control_files = lockable_files.LockableFiles(
706
self.get_branch_transport(None),
639
707
self._format._lock_file_name,
640
708
self._format._lock_class)
685
753
# done on this format anyway. So - acceptable wart.
686
754
result = self.open_workingtree()
687
755
if revision_id is not None:
688
result.set_last_revision(revision_id)
756
if revision_id == _mod_revision.NULL_REVISION:
757
result.set_parent_ids([])
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,
691
771
def get_branch_transport(self, branch_format):
692
772
"""See BzrDir.get_branch_transport()."""
693
773
if branch_format is None:
733
813
self._check_supported(format, unsupported)
734
814
return format.open(self, _found=True)
736
def sprout(self, url, revision_id=None, basis=None):
816
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
737
817
"""See BzrDir.sprout()."""
738
818
from bzrlib.workingtree import WorkingTreeFormat2
739
819
self._make_tail(url)
833
913
from bzrlib.workingtree import WorkingTreeFormat
834
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')
836
927
def _get_mkdir_mode(self):
837
928
"""Figure out the mode to use when creating a bzrdir subdir."""
838
temp_control = LockableFiles(self.transport, '', TransportLock)
929
temp_control = lockable_files.LockableFiles(self.transport, '',
930
lockable_files.TransportLock)
839
931
return temp_control._dir_mode
841
933
def get_branch_transport(self, branch_format):
1017
1109
"""Initialize a new bzrdir in the base directory of a Transport."""
1018
1110
# Since we don't have a .bzr directory, inherit the
1019
1111
# mode from the root directory
1020
temp_control = LockableFiles(transport, '', TransportLock)
1112
temp_control = lockable_files.LockableFiles(transport,
1113
'', lockable_files.TransportLock)
1021
1114
temp_control._transport.mkdir('.bzr',
1022
1115
# FIXME: RBC 20060121 don't peek under
1032
1125
('branch-format', self.get_format_string()),
1034
1127
# NB: no need to escape relative paths that are url safe.
1035
control_files = LockableFiles(control, self._lock_file_name,
1128
control_files = lockable_files.LockableFiles(control,
1129
self._lock_file_name, self._lock_class)
1037
1130
control_files.create_lock()
1038
1131
control_files.lock_write()
1217
1314
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1218
1315
RepositoryFormat5().initialize(result, _internal=True)
1219
1316
if not _cloning:
1220
BzrBranchFormat4().initialize(result)
1221
WorkingTreeFormat2().initialize(result)
1317
branch = BzrBranchFormat4().initialize(result)
1319
WorkingTreeFormat2().initialize(result)
1320
except errors.NotLocalUrl:
1321
# Even though we can't access the working tree, we need to
1322
# create its control files.
1323
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1224
1326
def _open(self, transport):
1271
1373
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1272
1374
RepositoryFormat6().initialize(result, _internal=True)
1273
1375
if not _cloning:
1274
BzrBranchFormat4().initialize(result)
1376
branch = BzrBranchFormat4().initialize(result)
1276
1378
WorkingTreeFormat2().initialize(result)
1277
1379
except errors.NotLocalUrl:
1278
# emulate pre-check behaviour for working tree and silently
1380
# Even though we can't access the working tree, we need to
1381
# create its control files.
1382
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1283
1385
def _open(self, transport):
1466
1568
self.bzrdir.transport.delete_tree('text-store')
1468
1570
def _convert_working_inv(self):
1469
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1470
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)
1471
1574
# FIXME inventory is a working tree change.
1472
self.branch.control_files.put('inventory', new_inv_xml)
1575
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1474
1577
def _write_all_weaves(self):
1475
1578
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1531
1634
def _load_old_inventory(self, rev_id):
1532
1635
assert rev_id not in self.converted_revs
1533
1636
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1534
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
1535
1639
rev = self.revisions[rev_id]
1536
1640
if rev.inventory_sha1:
1537
1641
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1560
1664
entries = inv.iter_entries()
1562
1666
for path, ie in entries:
1563
assert hasattr(ie, 'revision'), \
1667
assert getattr(ie, 'revision', None) is not None, \
1564
1668
'no revision on {%s} in {%s}' % \
1565
1669
(file_id, rev.revision_id)
1566
new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1670
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1567
1671
new_inv_sha1 = sha_string(new_inv_xml)
1568
1672
self.inv_weave.add_lines(rev.revision_id,
1569
1673
present_parents,
1603
1707
for old_revision in previous_entries:
1604
1708
# if this fails, its a ghost ?
1605
assert old_revision in self.converted_revs
1709
assert old_revision in self.converted_revs, \
1710
"Revision {%s} not in converted_revs" % old_revision
1606
1711
self.snapshot_ie(previous_entries, ie, w, rev_id)
1608
1713
assert getattr(ie, 'revision', None) is not None
1743
1848
for entry in branch_files:
1744
1849
self.move_entry('branch', entry)
1746
self.step('Upgrading working tree')
1747
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1748
self.make_lock('checkout')
1749
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1750
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1751
1851
checkout_files = [('pending-merges', True),
1752
1852
('inventory', True),
1753
1853
('stat-cache', False)]
1754
for entry in checkout_files:
1755
self.move_entry('checkout', entry)
1756
if last_revision is not None:
1757
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1759
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1854
# If a mandatory checkout file is not present, the branch does not have
1855
# a functional checkout. Do not create a checkout in the converted
1857
for name, mandatory in checkout_files:
1858
if mandatory and name not in bzrcontents:
1859
has_checkout = False
1863
if not has_checkout:
1864
self.pb.note('No working tree.')
1865
# If some checkout files are there, we may as well get rid of them.
1866
for name, mandatory in checkout_files:
1867
if name in bzrcontents:
1868
self.bzrdir.transport.delete(name)
1870
self.step('Upgrading working tree')
1871
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1872
self.make_lock('checkout')
1874
'checkout', bzrlib.workingtree.WorkingTreeFormat3())
1875
self.bzrdir.transport.delete_multi(
1876
self.garbage_inventories, self.pb)
1877
for entry in checkout_files:
1878
self.move_entry('checkout', entry)
1879
if last_revision is not None:
1880
self.bzrdir._control_files.put_utf8(
1881
'checkout/last-revision', last_revision)
1882
self.bzrdir._control_files.put_utf8(
1883
'branch-format', BzrDirMetaFormat1().get_format_string())
1760
1884
return BzrDir.open(self.bzrdir.root_transport.base)
1762
1886
def make_lock(self, name):
1763
1887
"""Make a lock for the new control dir name."""
1764
1888
self.step('Make %s lock' % name)
1765
ld = LockDir(self.bzrdir.transport,
1767
file_modebits=self.file_mode,
1768
dir_modebits=self.dir_mode)
1889
ld = lockdir.LockDir(self.bzrdir.transport,
1891
file_modebits=self.file_mode,
1892
dir_modebits=self.dir_mode)
1771
1895
def move_entry(self, new_dir, entry):