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