1
# Copyright (C) 2006-2010 Canonical Ltd
1
# Copyright (C) 2005-2010 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
91
class ControlComponent(object):
92
"""Abstract base class for control directory components.
94
This provides interfaces that are common across bzrdirs,
95
repositories, branches, and workingtree control directories.
97
They all expose two urls and transports: the *user* URL is the
98
one that stops above the control directory (eg .bzr) and that
99
should normally be used in messages, and the *control* URL is
100
under that in eg .bzr/checkout and is used to read the control
103
This can be used as a mixin and is intended to fit with
108
def control_transport(self):
109
raise NotImplementedError
112
def control_url(self):
113
return self.control_transport.base
116
def user_transport(self):
117
raise NotImplementedError
121
return self.user_transport.base
124
class BzrDir(ControlComponent):
125
91
"""A .bzr control diretory.
127
93
BzrDir instances let you create or open any of the things that can be
294
260
# copied, and finally if we are copying up to a specific
295
261
# revision_id then we can use the pending-ancestry-result which
296
262
# does not require traversing all of history to describe it.
297
if (result_repo.user_url == result.user_url
298
and not require_stacking and
263
if (result_repo.bzrdir.root_transport.base ==
264
result.root_transport.base and not require_stacking and
299
265
revision_id is not None):
300
266
fetch_spec = graph.PendingAncestryResult(
301
267
[revision_id], local_repo)
377
343
bzrdir = BzrDir.open_from_transport(current_transport)
378
except (errors.NotBranchError, errors.PermissionDenied):
344
except errors.NotBranchError:
381
347
recurse, value = evaluate(bzrdir)
384
350
subdirs = list_current(current_transport)
385
except (errors.NoSuchFile, errors.PermissionDenied):
351
except errors.NoSuchFile:
388
354
for subdir in sorted(subdirs, reverse=True):
389
355
pending.append(current_transport.clone(subdir))
391
def list_branches(self):
392
"""Return a sequence of all branches local to this control directory.
396
return [self.open_branch()]
397
except (errors.NotBranchError, errors.NoRepositoryPresent):
401
358
def find_branches(transport):
402
359
"""Find all branches under a transport.
414
371
except errors.NoRepositoryPresent:
417
return False, ([], repository)
418
return True, (bzrdir.list_branches(), None)
420
for branches, repo in BzrDir.find_bzrdirs(transport,
374
return False, (None, repository)
376
branch = bzrdir.open_branch()
377
except errors.NotBranchError:
378
return True, (None, None)
380
return True, (branch, None)
382
for branch, repo in BzrDir.find_bzrdirs(transport, evaluate=evaluate):
422
383
if repo is not None:
423
ret.extend(repo.find_branches())
424
if branches is not None:
384
branches.extend(repo.find_branches())
385
if branch is not None:
386
branches.append(branch)
428
389
def destroy_repository(self):
429
390
"""Destroy the repository in this BzrDir"""
430
391
raise NotImplementedError(self.destroy_repository)
432
def create_branch(self, name=None):
393
def create_branch(self):
433
394
"""Create a branch in this BzrDir.
435
:param name: Name of the colocated branch to create, None for
438
396
The bzrdir's format will control what branch format is created.
439
397
For more control see BranchFormatXX.create(a_bzrdir).
441
399
raise NotImplementedError(self.create_branch)
443
def destroy_branch(self, name=None):
444
"""Destroy a branch in this BzrDir.
446
:param name: Name of the branch to destroy, None for the default
401
def destroy_branch(self):
402
"""Destroy the branch in this BzrDir"""
449
403
raise NotImplementedError(self.destroy_branch)
499
453
except errors.NoRepositoryPresent:
500
454
repository = None
502
if (found_bzrdir.user_url != self.user_url
503
and not repository.is_shared()):
456
if ((found_bzrdir.root_transport.base !=
457
self.root_transport.base) and not repository.is_shared()):
504
458
# Don't look higher, can't use a higher shared repo.
505
459
repository = None
621
575
:return: Tuple with old path name and new path name
623
def name_gen(base='backup.bzr'):
625
name = "%s.~%d~" % (base, counter)
626
while self.root_transport.has(name):
628
name = "%s.~%d~" % (base, counter)
631
backup_dir=name_gen()
632
577
pb = ui.ui_factory.nested_progress_bar()
634
579
# FIXME: bug 300001 -- the backup fails if the backup directory
635
580
# already exists, but it should instead either remove it or make
636
581
# a new backup directory.
583
# FIXME: bug 262450 -- the backup directory should have the same
584
# permissions as the .bzr directory (probably a bug in copy_tree)
638
585
old_path = self.root_transport.abspath('.bzr')
639
new_path = self.root_transport.abspath(backup_dir)
586
new_path = self.root_transport.abspath('backup.bzr')
640
587
ui.ui_factory.note('making backup of %s\n to %s' % (old_path, new_path,))
641
self.root_transport.copy_tree('.bzr', backup_dir)
588
self.root_transport.copy_tree('.bzr', 'backup.bzr')
642
589
return (old_path, new_path)
704
651
next_transport = found_bzrdir.root_transport.clone('..')
705
if (found_bzrdir.user_url == next_transport.base):
652
if (found_bzrdir.root_transport.base == next_transport.base):
706
653
# top of the file system
708
655
# find the next containing bzrdir
725
672
repository = found_bzrdir.open_repository()
726
673
except errors.NoRepositoryPresent:
727
674
return None, False
728
if found_bzrdir.user_url == self.user_url:
675
if found_bzrdir.root_transport.base == self.root_transport.base:
729
676
return repository, True
730
677
elif repository.is_shared():
731
678
return repository, True
737
684
raise errors.NoRepositoryPresent(self)
738
685
return found_repo
740
def get_branch_reference(self, name=None):
687
def get_branch_reference(self):
741
688
"""Return the referenced URL for the branch in this bzrdir.
743
:param name: Optional colocated branch name
744
690
:raises NotBranchError: If there is no Branch.
745
:raises NoColocatedBranchSupport: If a branch name was specified
746
but colocated branches are not supported.
747
691
:return: The URL the branch in this bzrdir references if it is a
748
692
reference branch, or None for regular branches.
751
raise errors.NoColocatedBranchSupport(self)
754
def get_branch_transport(self, branch_format, name=None):
696
def get_branch_transport(self, branch_format):
755
697
"""Get the transport for use by branch format in this BzrDir.
757
699
Note that bzr dirs that do not support format strings will raise
852
794
:param _transport: the transport this dir is based at.
854
796
self._format = _format
855
# these are also under the more standard names of
856
# control_transport and user_transport
857
797
self.transport = _transport.clone('.bzr')
858
798
self.root_transport = _transport
859
799
self._mode_check_done = False
862
def user_transport(self):
863
return self.root_transport
866
def control_transport(self):
867
return self.transport
869
801
def is_control_filename(self, filename):
870
802
"""True if filename is the name of a path which is reserved for bzrdir's.
945
877
BzrDir._check_supported(format, _unsupported)
946
878
return format.open(transport, _found=True)
948
def open_branch(self, name=None, unsupported=False,
949
ignore_fallbacks=False):
880
def open_branch(self, unsupported=False, ignore_fallbacks=False):
950
881
"""Open the branch object at this BzrDir if one is present.
952
883
If unsupported is True, then no longer supported branch formats can
999
930
raise errors.NotBranchError(path=url)
1000
931
a_transport = new_t
1002
def _get_tree_branch(self, name=None):
933
def _get_tree_branch(self):
1003
934
"""Return the branch and tree, if any, for this bzrdir.
1005
:param name: Name of colocated branch to open.
1007
936
Return None for tree if not present or inaccessible.
1008
937
Raise NotBranchError if no branch is present.
1009
938
:return: (tree, branch)
1012
941
tree = self.open_workingtree()
1013
942
except (errors.NoWorkingTree, errors.NotLocalUrl):
1015
branch = self.open_branch(name=name)
944
branch = self.open_branch()
1017
if name is not None:
1018
branch = self.open_branch(name=name)
1020
branch = tree.branch
1021
947
return tree, branch
1096
1022
raise NotImplementedError(self.open_workingtree)
1098
def has_branch(self, name=None):
1024
def has_branch(self):
1099
1025
"""Tell if this bzrdir contains a branch.
1101
1027
Note: if you're going to open the branch, you should just go ahead
1244
1170
repository_policy = result.determine_repository_policy(
1245
1171
force_new_repo, stacked_branch_url, require_stacking=stacked)
1246
1172
result_repo, is_new_repo = repository_policy.acquire_repository()
1247
is_stacked = stacked or (len(result_repo._fallback_repositories) != 0)
1248
if is_new_repo and revision_id is not None and not is_stacked:
1173
if is_new_repo and revision_id is not None and not stacked:
1249
1174
fetch_spec = graph.PendingAncestryResult(
1250
1175
[revision_id], source_repository)
1384
1309
self.create_hook(hooks.HookPoint('pre_open',
1385
1310
"Invoked before attempting to open a BzrDir with the transport "
1386
1311
"that the open will use.", (1, 14), None))
1387
self.create_hook(hooks.HookPoint('post_repo_init',
1388
"Invoked after a repository has been initialized. "
1389
"post_repo_init is called with a "
1390
"bzrlib.bzrdir.RepoInitHookParams.",
1393
1313
# install the default hooks
1394
1314
BzrDir.hooks = BzrDirHooks()
1397
class RepoInitHookParams(object):
1398
"""Object holding parameters passed to *_repo_init hooks.
1400
There are 4 fields that hooks may wish to access:
1402
:ivar repository: Repository created
1403
:ivar format: Repository format
1404
:ivar bzrdir: The bzrdir for the repository
1405
:ivar shared: The repository is shared
1408
def __init__(self, repository, format, a_bzrdir, shared):
1409
"""Create a group of RepoInitHook parameters.
1411
:param repository: Repository created
1412
:param format: Repository format
1413
:param bzrdir: The bzrdir for the repository
1414
:param shared: The repository is shared
1416
self.repository = repository
1417
self.format = format
1418
self.bzrdir = a_bzrdir
1419
self.shared = shared
1421
def __eq__(self, other):
1422
return self.__dict__ == other.__dict__
1426
return "<%s for %s>" % (self.__class__.__name__,
1429
return "<%s for %s>" % (self.__class__.__name__,
1433
1317
class BzrDirPreSplitOut(BzrDir):
1434
1318
"""A common class for the all-in-one formats."""
1474
1358
tree.clone(result)
1477
def create_branch(self, name=None):
1361
def create_branch(self):
1478
1362
"""See BzrDir.create_branch."""
1479
return self._format.get_branch_format().initialize(self, name=name)
1363
return self._format.get_branch_format().initialize(self)
1481
def destroy_branch(self, name=None):
1365
def destroy_branch(self):
1482
1366
"""See BzrDir.destroy_branch."""
1483
1367
raise errors.UnsupportedOperation(self.destroy_branch, self)
1540
1424
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
1543
def get_branch_transport(self, branch_format, name=None):
1427
def get_branch_transport(self, branch_format):
1544
1428
"""See BzrDir.get_branch_transport()."""
1545
if name is not None:
1546
raise errors.NoColocatedBranchSupport(self)
1547
1429
if branch_format is None:
1548
1430
return self.transport
1582
1464
format = BzrDirFormat.get_default_format()
1583
1465
return not isinstance(self._format, format.__class__)
1585
def open_branch(self, name=None, unsupported=False,
1586
ignore_fallbacks=False):
1467
def open_branch(self, unsupported=False, ignore_fallbacks=False):
1587
1468
"""See BzrDir.open_branch."""
1588
1469
from bzrlib.branch import BzrBranchFormat4
1589
1470
format = BzrBranchFormat4()
1590
1471
self._check_supported(format, unsupported)
1591
return format.open(self, name, _found=True)
1472
return format.open(self, _found=True)
1593
1474
def sprout(self, url, revision_id=None, force_new_repo=False,
1594
1475
possible_transports=None, accelerator_tree=None,
1711
1592
"""See BzrDir.can_convert_format()."""
1714
def create_branch(self, name=None):
1595
def create_branch(self):
1715
1596
"""See BzrDir.create_branch."""
1716
return self._format.get_branch_format().initialize(self, name=name)
1597
return self._format.get_branch_format().initialize(self)
1718
def destroy_branch(self, name=None):
1599
def destroy_branch(self):
1719
1600
"""See BzrDir.create_branch."""
1720
if name is not None:
1721
raise errors.NoColocatedBranchSupport(self)
1722
1601
self.transport.delete_tree('branch')
1724
1603
def create_repository(self, shared=False):
1747
1626
def destroy_workingtree_metadata(self):
1748
1627
self.transport.delete_tree('checkout')
1750
def find_branch_format(self, name=None):
1629
def find_branch_format(self):
1751
1630
"""Find the branch 'format' for this bzrdir.
1753
1632
This might be a synthetic object for e.g. RemoteBranch and SVN.
1755
1634
from bzrlib.branch import BranchFormat
1756
return BranchFormat.find_format(self, name=name)
1635
return BranchFormat.find_format(self)
1758
1637
def _get_mkdir_mode(self):
1759
1638
"""Figure out the mode to use when creating a bzrdir subdir."""
1761
1640
lockable_files.TransportLock)
1762
1641
return temp_control._dir_mode
1764
def get_branch_reference(self, name=None):
1643
def get_branch_reference(self):
1765
1644
"""See BzrDir.get_branch_reference()."""
1766
1645
from bzrlib.branch import BranchFormat
1767
format = BranchFormat.find_format(self, name=name)
1768
return format.get_reference(self, name=name)
1646
format = BranchFormat.find_format(self)
1647
return format.get_reference(self)
1770
def get_branch_transport(self, branch_format, name=None):
1649
def get_branch_transport(self, branch_format):
1771
1650
"""See BzrDir.get_branch_transport()."""
1772
if name is not None:
1773
raise errors.NoColocatedBranchSupport(self)
1774
1651
# XXX: this shouldn't implicitly create the directory if it's just
1775
1652
# promising to get a transport -- mbp 20090727
1776
1653
if branch_format is None:
1848
1725
except errors.NoRepositoryPresent:
1850
for branch in self.list_branches():
1851
if not isinstance(branch._format,
1728
if not isinstance(self.open_branch()._format,
1852
1729
format.get_branch_format().__class__):
1853
1730
# the branch needs an upgrade.
1732
except errors.NotBranchError:
1856
1735
my_wt = self.open_workingtree(recommend_upgrade=False)
1857
1736
if not isinstance(my_wt._format,
1865
def open_branch(self, name=None, unsupported=False,
1866
ignore_fallbacks=False):
1744
def open_branch(self, unsupported=False, ignore_fallbacks=False):
1867
1745
"""See BzrDir.open_branch."""
1868
format = self.find_branch_format(name=name)
1746
format = self.find_branch_format()
1869
1747
self._check_supported(format, unsupported)
1870
return format.open(self, name=name,
1871
_found=True, ignore_fallbacks=ignore_fallbacks)
1748
return format.open(self, _found=True, ignore_fallbacks=ignore_fallbacks)
1873
1750
def open_repository(self, unsupported=False):
1874
1751
"""See BzrDir.open_repository."""
1906
1783
Once a format is deprecated, just deprecate the initialize and open
1907
1784
methods on the format class. Do not deprecate the object, as the
1908
1785
object will be created every system load.
1910
:cvar colocated_branches: Whether this formats supports colocated branches.
1913
1788
_default_format = None
1931
1806
_lock_file_name = 'branch-lock'
1933
colocated_branches = False
1934
"""Whether co-located branches are supported for this control dir format.
1937
1808
# _lock_class must be set in subclasses to the lock type, typ.
1938
1809
# TransportLock or LockDir
2737
2609
def convert(self, to_convert, pb):
2738
2610
"""See Converter.convert()."""
2739
2611
self.bzrdir = to_convert
2741
warnings.warn("pb parameter to convert() is deprecated")
2742
self.pb = ui.ui_factory.nested_progress_bar()
2744
ui.ui_factory.note('starting upgrade from format 4 to 5')
2745
if isinstance(self.bzrdir.transport, local.LocalTransport):
2746
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
2747
self._convert_to_weaves()
2748
return BzrDir.open(self.bzrdir.user_url)
2613
ui.ui_factory.note('starting upgrade from format 4 to 5')
2614
if isinstance(self.bzrdir.transport, local.LocalTransport):
2615
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
2616
self._convert_to_weaves()
2617
return BzrDir.open(self.bzrdir.root_transport.base)
2752
2619
def _convert_to_weaves(self):
2753
2620
ui.ui_factory.note('note: upgrade may be faster if all store files are ungzipped first')
2873
2740
self.revisions[rev_id] = rev
2875
2742
def _load_old_inventory(self, rev_id):
2876
f = self.branch.repository.inventory_store.get(rev_id)
2878
old_inv_xml = f.read()
2743
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
2881
2744
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
2882
2745
inv.revision_id = rev_id
2883
2746
rev = self.revisions[rev_id]
2961
2824
ie.revision = previous_ie.revision
2963
2826
if ie.has_text():
2964
f = self.branch.repository._text_store.get(ie.text_id)
2966
file_lines = f.readlines()
2827
text = self.branch.repository._text_store.get(ie.text_id)
2828
file_lines = text.readlines()
2969
2829
w.add_lines(rev_id, previous_revisions, file_lines)
2970
2830
self.text_count += 1
3001
2861
def convert(self, to_convert, pb):
3002
2862
"""See Converter.convert()."""
3003
2863
self.bzrdir = to_convert
3004
pb = ui.ui_factory.nested_progress_bar()
3006
ui.ui_factory.note('starting upgrade from format 5 to 6')
3007
self._convert_to_prefixed()
3008
return BzrDir.open(self.bzrdir.user_url)
2865
ui.ui_factory.note('starting upgrade from format 5 to 6')
2866
self._convert_to_prefixed()
2867
return BzrDir.open(self.bzrdir.root_transport.base)
3012
2869
def _convert_to_prefixed(self):
3013
2870
from bzrlib.store import TransportStore
3046
2903
from bzrlib.repofmt.weaverepo import RepositoryFormat7
3047
2904
from bzrlib.branch import BzrBranchFormat5
3048
2905
self.bzrdir = to_convert
3049
self.pb = ui.ui_factory.nested_progress_bar()
3051
2908
self.total = 20 # the steps we know about
3052
2909
self.garbage_inventories = []
3132
2989
'branch-format',
3133
2990
BzrDirMetaFormat1().get_format_string(),
3134
2991
mode=self.file_mode)
3136
return BzrDir.open(self.bzrdir.user_url)
2992
return BzrDir.open(self.bzrdir.root_transport.base)
3138
2994
def make_lock(self, name):
3139
2995
"""Make a lock for the new control dir name."""
3174
3030
def convert(self, to_convert, pb):
3175
3031
"""See Converter.convert()."""
3176
3032
self.bzrdir = to_convert
3177
self.pb = ui.ui_factory.nested_progress_bar()
3180
3036
self.step('checking repository format')
3188
3044
ui.ui_factory.note('starting repository conversion')
3189
3045
converter = CopyConverter(self.target_format.repository_format)
3190
3046
converter.convert(repo, pb)
3191
for branch in self.bzrdir.list_branches():
3048
branch = self.bzrdir.open_branch()
3049
except errors.NotBranchError:
3192
3052
# TODO: conversions of Branch and Tree should be done by
3193
3053
# InterXFormat lookups/some sort of registry.
3194
3054
# Avoid circular imports
3252
3111
# XXX: It's a bit ugly that the network name is here, because we'd
3253
3112
# like to believe that format objects are stateless or at least
3254
3113
# immutable, However, we do at least avoid mutating the name after
3255
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
3114
# it's returned. See <https://bugs.edge.launchpad.net/bzr/+bug/504102>
3256
3115
self._network_name = None
3258
3117
def __repr__(self):
3721
3580
stack_on = urlutils.rebase_url(self._stack_on,
3722
3581
self._stack_on_pwd,
3582
branch.bzrdir.root_transport.base)
3724
3583
except errors.InvalidRebaseURLs:
3725
3584
stack_on = self._get_full_stack_on()