38
from breezy.transport import local
39
from breezy.push import (
40
from brzlib.transport import local
41
from brzlib.push import (
43
from breezy.i18n import gettext
45
from brzlib.i18n import gettext
52
class MustHaveWorkingTree(errors.BzrError):
54
_fmt = "Branching '%(url)s'(%(format)s) must create a working tree."
56
def __init__(self, format, url):
57
errors.BzrError.__init__(self, format=format, url=url)
60
class BranchReferenceLoop(errors.BzrError):
62
_fmt = "Can not create branch reference that points at branch itself."
64
def __init__(self, branch):
65
errors.BzrError.__init__(self, branch=branch)
48
from brzlib import registry
68
51
class ControlComponent(object):
192
175
def destroy_branch(self, name=None):
193
176
"""Destroy a branch in this ControlDir.
195
:param name: Name of the branch to destroy, None for the
178
:param name: Name of the branch to destroy, None for the
196
179
user selected branch or "" for the active branch.
197
180
:raise NotBranchError: When the branch does not exist
199
182
raise NotImplementedError(self.destroy_branch)
201
184
def create_workingtree(self, revision_id=None, from_branch=None,
202
accelerator_tree=None, hardlink=False):
185
accelerator_tree=None, hardlink=False):
203
186
"""Create a working tree at this ControlDir.
205
188
:param revision_id: create it as of this revision id.
206
:param from_branch: override controldir branch
189
:param from_branch: override controldir branch
207
190
(for lightweight checkouts)
208
191
:param accelerator_tree: A tree which can be used for retrieving file
209
192
contents more quickly than the revision tree, i.e. a workingtree.
398
380
raise NotImplementedError(self.sprout)
400
def push_branch(self, source, revision_id=None, overwrite=False,
401
remember=False, create_prefix=False, lossy=False):
382
def push_branch(self, source, revision_id=None, overwrite=False,
383
remember=False, create_prefix=False):
402
384
"""Push the source branch into this ControlDir."""
404
386
# If we can open a branch, use its direct repository, otherwise see
442
424
tree_to = self.open_workingtree()
443
425
except errors.NotLocalUrl:
444
push_result.branch_push_result = source.push(
445
br_to, overwrite, stop_revision=revision_id, lossy=lossy)
426
push_result.branch_push_result = source.push(br_to,
427
overwrite, stop_revision=revision_id)
446
428
push_result.workingtree_updated = False
447
429
except errors.NoWorkingTree:
448
push_result.branch_push_result = source.push(
449
br_to, overwrite, stop_revision=revision_id, lossy=lossy)
450
push_result.workingtree_updated = None # Not applicable
430
push_result.branch_push_result = source.push(br_to,
431
overwrite, stop_revision=revision_id)
432
push_result.workingtree_updated = None # Not applicable
452
with tree_to.lock_write():
453
436
push_result.branch_push_result = source.push(
454
tree_to.branch, overwrite, stop_revision=revision_id,
437
tree_to.branch, overwrite, stop_revision=revision_id)
457
441
push_result.workingtree_updated = True
458
442
push_result.old_revno = push_result.branch_push_result.old_revno
459
443
push_result.old_revid = push_result.branch_push_result.old_revid
510
494
preserve_stacking=preserve_stacking)
512
496
def clone_on_transport(self, transport, revision_id=None,
513
force_new_repo=False, preserve_stacking=False, stacked_on=None,
514
create_prefix=False, use_existing_dir=True, no_tree=False):
497
force_new_repo=False, preserve_stacking=False, stacked_on=None,
498
create_prefix=False, use_existing_dir=True, no_tree=False):
515
499
"""Clone this controldir and its contents to transport verbatim.
517
501
:param transport: The transport for the location to produce the clone
718
701
# Keep initial base since 'transport' may be modified while following
719
702
# the redirections.
720
703
base = transport.base
722
704
def find_format(transport):
723
705
return transport, ControlDirFormat.find_format(transport,
726
708
def redirected(transport, e, redirection_notice):
727
709
redirected_transport = transport._redirected_to(e.source, e.target)
728
710
if redirected_transport is None:
729
711
raise errors.NotBranchError(base)
730
712
trace.note(gettext('{0} is{1} redirected to {2}').format(
731
transport.base, e.permanently, redirected_transport.base))
713
transport.base, e.permanently, redirected_transport.base))
732
714
return redirected_transport
773
755
result = klass.open_from_transport(a_transport)
774
756
return result, urlutils.unescape(a_transport.relpath(url))
775
except errors.NotBranchError:
757
except errors.NotBranchError, e:
777
759
except errors.PermissionDenied:
780
762
new_t = a_transport.clone('..')
781
except urlutils.InvalidURLJoin:
763
except errors.InvalidURLJoin:
782
764
# reached the root, whatever that may be
783
765
raise errors.NotBranchError(path=url)
784
766
if new_t.base == a_transport.base:
864
846
def __init__(self):
865
847
"""Create the default hooks."""
866
hooks.Hooks.__init__(self, "breezy.controldir", "ControlDir.hooks")
848
hooks.Hooks.__init__(self, "brzlib.controldir", "ControlDir.hooks")
867
849
self.add_hook('pre_open',
868
"Invoked before attempting to open a ControlDir with the transport "
869
"that the open will use.", (1, 14))
850
"Invoked before attempting to open a ControlDir with the transport "
851
"that the open will use.", (1, 14))
870
852
self.add_hook('post_repo_init',
871
"Invoked after a repository has been initialized. "
872
"post_repo_init is called with a "
873
"breezy.controldir.RepoInitHookParams.",
853
"Invoked after a repository has been initialized. "
854
"post_repo_init is called with a "
855
"brzlib.controldir.RepoInitHookParams.",
877
858
# install the default hooks
878
859
ControlDir.hooks = ControlDirHooks()
957
938
registry._LazyObjectGetter(module_name, member_name))
959
940
def _get_extra(self):
960
"""Return getters for extra formats, not usable in meta directories."""
961
return [getter.get_obj for getter in self._extra_formats]
963
def _get_all_lazy(self):
964
"""Return getters for all formats, even those not usable in metadirs."""
965
result = [self._dict[name].get_obj for name in self.keys()]
966
result.extend(self._get_extra())
941
"""Return all "extra" formats, not usable in meta directories."""
943
for getter in self._extra_formats:
969
950
def _get_all(self):
970
"""Return all formats, even those not usable in metadirs."""
951
"""Return all formats, even those not usable in metadirs.
972
for getter in self._get_all_lazy():
954
for name in self.keys():
974
956
if callable(fmt):
976
958
result.append(fmt)
959
return result + self._get_extra()
979
961
def _get_all_modules(self):
980
962
"""Return a set of the modules providing objects."""
1117
1113
klass._probers.remove(prober)
1116
def register_server_prober(klass, prober):
1117
"""Register a control format prober for client-server environments.
1119
These probers will be used before ones registered with
1120
register_prober. This gives implementations that decide to the
1121
chance to grab it before anything looks at the contents of the format
1124
klass._server_probers.append(prober)
1119
1126
def __str__(self):
1120
1127
# Trim the newline
1121
1128
return self.get_format_description().rstrip()
1124
1131
def all_probers(klass):
1125
return klass._probers
1132
return klass._server_probers + klass._probers
1128
1135
def known_formats(klass):
1129
1136
"""Return all the known formats.
1132
1139
for prober_kls in klass.all_probers():
1133
result.extend(prober_kls.known_formats())
1140
result.update(prober_kls.known_formats())
1137
1144
def find_format(klass, transport, probers=None):
1138
1145
"""Return the format present at transport."""
1139
1146
if probers is None:
1141
klass.all_probers(),
1142
key=lambda prober: prober.priority(transport))
1147
probers = klass.all_probers()
1143
1148
for prober_kls in probers:
1144
1149
prober = prober_kls()
1167
1172
raise NotImplementedError(self.initialize_on_transport)
1169
1174
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
1170
create_prefix=False, force_new_repo=False, stacked_on=None,
1171
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
1172
shared_repo=False, vfs_only=False):
1175
create_prefix=False, force_new_repo=False, stacked_on=None,
1176
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
1177
shared_repo=False, vfs_only=False):
1173
1178
"""Create this format on transport.
1175
1180
The directory to initialize will be created.
1319
1293
def __init__(self):
1320
1294
"""Create a ControlDirFormatRegistry."""
1295
self._aliases = set()
1321
1296
self._registration_order = list()
1322
1297
super(ControlDirFormatRegistry, self).__init__()
1300
"""Return a set of the format names which are aliases."""
1301
return frozenset(self._aliases)
1324
1303
def register(self, key, factory, help, native=True, deprecated=False,
1325
hidden=False, experimental=False):
1304
hidden=False, experimental=False, alias=False):
1326
1305
"""Register a ControlDirFormat factory.
1328
1307
The factory must be a callable that takes one parameter: the key.
1332
1311
supplied directly.
1334
1313
registry.Registry.register(self, key, factory, help,
1335
ControlDirFormatInfo(native, deprecated, hidden, experimental))
1314
ControlDirFormatInfo(native, deprecated, hidden, experimental))
1316
self._aliases.add(key)
1336
1317
self._registration_order.append(key)
1338
def register_alias(self, key, target, hidden=False):
1339
"""Register a format alias.
1341
:param key: Alias name
1342
:param target: Target format
1343
:param hidden: Whether the alias is hidden
1345
info = self.get_info(target)
1346
registry.Registry.register_alias(self, key, target,
1347
ControlDirFormatInfo(
1348
native=info.native, deprecated=info.deprecated,
1349
hidden=hidden, experimental=info.experimental))
1351
1319
def register_lazy(self, key, module_name, member_name, help, native=True,
1352
deprecated=False, hidden=False, experimental=False):
1320
deprecated=False, hidden=False, experimental=False, alias=False):
1353
1321
registry.Registry.register_lazy(self, key, module_name, member_name,
1354
help, ControlDirFormatInfo(native, deprecated, hidden, experimental))
1322
help, ControlDirFormatInfo(native, deprecated, hidden, experimental))
1324
self._aliases.add(key)
1355
1325
self._registration_order.append(key)
1357
1327
def set_default(self, key):
1468
1440
def __repr__(self):
1469
1441
if self.repository:
1470
1442
return "<%s for %s>" % (self.__class__.__name__,
1473
1445
return "<%s for %s>" % (self.__class__.__name__,
1477
def is_control_filename(filename):
1478
"""Check if filename is used for control directories."""
1479
# TODO(jelmer): Instead, have a function that returns all control
1481
for key, format in format_registry.items():
1482
if format().is_control_filename(filename):
1488
class RepositoryAcquisitionPolicy(object):
1489
"""Abstract base class for repository acquisition policies.
1491
A repository acquisition policy decides how a ControlDir acquires a repository
1492
for a branch that is being created. The most basic policy decision is
1493
whether to create a new repository or use an existing one.
1496
def __init__(self, stack_on, stack_on_pwd, require_stacking):
1499
:param stack_on: A location to stack on
1500
:param stack_on_pwd: If stack_on is relative, the location it is
1502
:param require_stacking: If True, it is a failure to not stack.
1504
self._stack_on = stack_on
1505
self._stack_on_pwd = stack_on_pwd
1506
self._require_stacking = require_stacking
1508
def configure_branch(self, branch):
1509
"""Apply any configuration data from this policy to the branch.
1511
Default implementation sets repository stacking.
1513
if self._stack_on is None:
1515
if self._stack_on_pwd is None:
1516
stack_on = self._stack_on
1519
stack_on = urlutils.rebase_url(self._stack_on,
1522
except urlutils.InvalidRebaseURLs:
1523
stack_on = self._get_full_stack_on()
1525
branch.set_stacked_on_url(stack_on)
1526
except (_mod_branch.UnstackableBranchFormat,
1527
errors.UnstackableRepositoryFormat):
1528
if self._require_stacking:
1531
def requires_stacking(self):
1532
"""Return True if this policy requires stacking."""
1533
return self._stack_on is not None and self._require_stacking
1535
def _get_full_stack_on(self):
1536
"""Get a fully-qualified URL for the stack_on location."""
1537
if self._stack_on is None:
1539
if self._stack_on_pwd is None:
1540
return self._stack_on
1542
return urlutils.join(self._stack_on_pwd, self._stack_on)
1544
def _add_fallback(self, repository, possible_transports=None):
1545
"""Add a fallback to the supplied repository, if stacking is set."""
1546
stack_on = self._get_full_stack_on()
1547
if stack_on is None:
1550
stacked_dir = ControlDir.open(
1551
stack_on, possible_transports=possible_transports)
1552
except errors.JailBreak:
1553
# We keep the stacking details, but we are in the server code so
1554
# actually stacking is not needed.
1557
stacked_repo = stacked_dir.open_branch().repository
1558
except errors.NotBranchError:
1559
stacked_repo = stacked_dir.open_repository()
1561
repository.add_fallback_repository(stacked_repo)
1562
except errors.UnstackableRepositoryFormat:
1563
if self._require_stacking:
1566
self._require_stacking = True
1568
def acquire_repository(self, make_working_trees=None, shared=False,
1569
possible_transports=None):
1570
"""Acquire a repository for this controlrdir.
1572
Implementations may create a new repository or use a pre-exising
1575
:param make_working_trees: If creating a repository, set
1576
make_working_trees to this value (if non-None)
1577
:param shared: If creating a repository, make it shared if True
1578
:return: A repository, is_new_flag (True if the repository was
1581
raise NotImplementedError(
1582
RepositoryAcquisitionPolicy.acquire_repository)
1585
1449
# Please register new formats after old formats so that formats