43
45
from breezy.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 . import registry
68
51
class ControlComponent(object):
128
111
return list(self.get_branches().values())
130
def branch_names(self):
131
"""List all branch names in this control directory.
133
:return: List of branch names
136
self.get_branch_reference()
137
except (errors.NotBranchError, errors.NoRepositoryPresent):
142
113
def get_branches(self):
143
114
"""Get all branches in this control directory, as a dictionary.
145
116
:return: Dictionary mapping branch names to instances.
148
return {"": self.open_branch()}
119
return { "": self.open_branch() }
149
120
except (errors.NotBranchError, errors.NoRepositoryPresent):
152
123
def is_control_filename(self, filename):
153
124
"""True if filename is the name of a path which is reserved for
204
175
def destroy_branch(self, name=None):
205
176
"""Destroy a branch in this ControlDir.
207
:param name: Name of the branch to destroy, None for the
178
:param name: Name of the branch to destroy, None for the
208
179
user selected branch or "" for the active branch.
209
180
:raise NotBranchError: When the branch does not exist
211
182
raise NotImplementedError(self.destroy_branch)
213
184
def create_workingtree(self, revision_id=None, from_branch=None,
214
accelerator_tree=None, hardlink=False):
185
accelerator_tree=None, hardlink=False):
215
186
"""Create a working tree at this ControlDir.
217
188
:param revision_id: create it as of this revision id.
218
:param from_branch: override controldir branch
189
:param from_branch: override controldir branch
219
190
(for lightweight checkouts)
220
191
:param accelerator_tree: A tree which can be used for retrieving file
221
192
contents more quickly than the revision tree, i.e. a workingtree.
410
380
raise NotImplementedError(self.sprout)
412
def push_branch(self, source, revision_id=None, overwrite=False,
413
remember=False, create_prefix=False, lossy=False,
382
def push_branch(self, source, revision_id=None, overwrite=False,
383
remember=False, create_prefix=False):
415
384
"""Push the source branch into this ControlDir."""
417
386
# If we can open a branch, use its direct repository, otherwise see
457
424
tree_to = self.open_workingtree()
458
425
except errors.NotLocalUrl:
459
push_result.branch_push_result = source.push(
460
br_to, overwrite, stop_revision=revision_id, lossy=lossy,
461
tag_selector=tag_selector)
426
push_result.branch_push_result = source.push(br_to,
427
overwrite, stop_revision=revision_id)
462
428
push_result.workingtree_updated = False
463
429
except errors.NoWorkingTree:
464
push_result.branch_push_result = source.push(
465
br_to, overwrite, stop_revision=revision_id, lossy=lossy,
466
tag_selector=tag_selector)
467
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
469
with tree_to.lock_write():
470
436
push_result.branch_push_result = source.push(
471
tree_to.branch, overwrite, stop_revision=revision_id,
472
lossy=lossy, tag_selector=tag_selector)
437
tree_to.branch, overwrite, stop_revision=revision_id)
474
441
push_result.workingtree_updated = True
475
442
push_result.old_revno = push_result.branch_push_result.old_revno
476
443
push_result.old_revid = push_result.branch_push_result.old_revid
524
491
return self.clone_on_transport(_mod_transport.get_transport(url),
525
492
revision_id=revision_id,
526
493
force_new_repo=force_new_repo,
527
preserve_stacking=preserve_stacking,
528
tag_selector=tag_selector)
494
preserve_stacking=preserve_stacking)
530
496
def clone_on_transport(self, transport, revision_id=None,
531
force_new_repo=False, preserve_stacking=False, stacked_on=None,
532
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):
534
499
"""Clone this controldir and its contents to transport verbatim.
536
501
:param transport: The transport for the location to produce the clone
737
701
# Keep initial base since 'transport' may be modified while following
738
702
# the redirections.
739
703
base = transport.base
741
704
def find_format(transport):
742
705
return transport, ControlDirFormat.find_format(transport,
745
708
def redirected(transport, e, redirection_notice):
746
709
redirected_transport = transport._redirected_to(e.source, e.target)
747
710
if redirected_transport is None:
748
711
raise errors.NotBranchError(base)
749
712
trace.note(gettext('{0} is{1} redirected to {2}').format(
750
transport.base, e.permanently, redirected_transport.base))
713
transport.base, e.permanently, redirected_transport.base))
751
714
return redirected_transport
884
847
"""Create the default hooks."""
885
848
hooks.Hooks.__init__(self, "breezy.controldir", "ControlDir.hooks")
886
849
self.add_hook('pre_open',
887
"Invoked before attempting to open a ControlDir with the transport "
888
"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))
889
852
self.add_hook('post_repo_init',
890
"Invoked after a repository has been initialized. "
891
"post_repo_init is called with a "
892
"breezy.controldir.RepoInitHookParams.",
853
"Invoked after a repository has been initialized. "
854
"post_repo_init is called with a "
855
"breezy.controldir.RepoInitHookParams.",
896
858
# install the default hooks
897
859
ControlDir.hooks = ControlDirHooks()
976
938
registry._LazyObjectGetter(module_name, member_name))
978
940
def _get_extra(self):
979
"""Return getters for extra formats, not usable in meta directories."""
980
return [getter.get_obj for getter in self._extra_formats]
982
def _get_all_lazy(self):
983
"""Return getters for all formats, even those not usable in metadirs."""
984
result = [self._dict[name].get_obj for name in self.keys()]
985
result.extend(self._get_extra())
941
"""Return all "extra" formats, not usable in meta directories."""
943
for getter in self._extra_formats:
988
950
def _get_all(self):
989
"""Return all formats, even those not usable in metadirs."""
951
"""Return all formats, even those not usable in metadirs.
991
for getter in self._get_all_lazy():
954
for name in self.keys():
993
956
if callable(fmt):
995
958
result.append(fmt)
959
return result + self._get_extra()
998
961
def _get_all_modules(self):
999
962
"""Return a set of the modules providing objects."""
1136
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)
1138
1126
def __str__(self):
1139
1127
# Trim the newline
1140
1128
return self.get_format_description().rstrip()
1143
1131
def all_probers(klass):
1144
return klass._probers
1132
return klass._server_probers + klass._probers
1147
1135
def known_formats(klass):
1148
1136
"""Return all the known formats.
1151
1139
for prober_kls in klass.all_probers():
1152
result.extend(prober_kls.known_formats())
1140
result.update(prober_kls.known_formats())
1156
1144
def find_format(klass, transport, probers=None):
1157
1145
"""Return the format present at transport."""
1158
1146
if probers is None:
1160
klass.all_probers(),
1161
key=lambda prober: prober.priority(transport))
1147
probers = klass.all_probers()
1162
1148
for prober_kls in probers:
1163
1149
prober = prober_kls()
1186
1172
raise NotImplementedError(self.initialize_on_transport)
1188
1174
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
1189
create_prefix=False, force_new_repo=False, stacked_on=None,
1190
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
1191
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):
1192
1178
"""Create this format on transport.
1194
1180
The directory to initialize will be created.
1338
1293
def __init__(self):
1339
1294
"""Create a ControlDirFormatRegistry."""
1295
self._aliases = set()
1340
1296
self._registration_order = list()
1341
1297
super(ControlDirFormatRegistry, self).__init__()
1300
"""Return a set of the format names which are aliases."""
1301
return frozenset(self._aliases)
1343
1303
def register(self, key, factory, help, native=True, deprecated=False,
1344
hidden=False, experimental=False):
1304
hidden=False, experimental=False, alias=False):
1345
1305
"""Register a ControlDirFormat factory.
1347
1307
The factory must be a callable that takes one parameter: the key.
1351
1311
supplied directly.
1353
1313
registry.Registry.register(self, key, factory, help,
1354
ControlDirFormatInfo(native, deprecated, hidden, experimental))
1314
ControlDirFormatInfo(native, deprecated, hidden, experimental))
1316
self._aliases.add(key)
1355
1317
self._registration_order.append(key)
1357
def register_alias(self, key, target, hidden=False):
1358
"""Register a format alias.
1360
:param key: Alias name
1361
:param target: Target format
1362
:param hidden: Whether the alias is hidden
1364
info = self.get_info(target)
1365
registry.Registry.register_alias(self, key, target,
1366
ControlDirFormatInfo(
1367
native=info.native, deprecated=info.deprecated,
1368
hidden=hidden, experimental=info.experimental))
1370
1319
def register_lazy(self, key, module_name, member_name, help, native=True,
1371
deprecated=False, hidden=False, experimental=False):
1320
deprecated=False, hidden=False, experimental=False, alias=False):
1372
1321
registry.Registry.register_lazy(self, key, module_name, member_name,
1373
help, ControlDirFormatInfo(native, deprecated, hidden, experimental))
1322
help, ControlDirFormatInfo(native, deprecated, hidden, experimental))
1324
self._aliases.add(key)
1374
1325
self._registration_order.append(key)
1376
1327
def set_default(self, key):
1487
1440
def __repr__(self):
1488
1441
if self.repository:
1489
1442
return "<%s for %s>" % (self.__class__.__name__,
1492
1445
return "<%s for %s>" % (self.__class__.__name__,
1496
def is_control_filename(filename):
1497
"""Check if filename is used for control directories."""
1498
# TODO(jelmer): Instead, have a function that returns all control
1500
for key, format in format_registry.items():
1501
if format().is_control_filename(filename):
1507
class RepositoryAcquisitionPolicy(object):
1508
"""Abstract base class for repository acquisition policies.
1510
A repository acquisition policy decides how a ControlDir acquires a repository
1511
for a branch that is being created. The most basic policy decision is
1512
whether to create a new repository or use an existing one.
1515
def __init__(self, stack_on, stack_on_pwd, require_stacking):
1518
:param stack_on: A location to stack on
1519
:param stack_on_pwd: If stack_on is relative, the location it is
1521
:param require_stacking: If True, it is a failure to not stack.
1523
self._stack_on = stack_on
1524
self._stack_on_pwd = stack_on_pwd
1525
self._require_stacking = require_stacking
1527
def configure_branch(self, branch):
1528
"""Apply any configuration data from this policy to the branch.
1530
Default implementation sets repository stacking.
1532
if self._stack_on is None:
1534
if self._stack_on_pwd is None:
1535
stack_on = self._stack_on
1538
stack_on = urlutils.rebase_url(self._stack_on,
1541
except urlutils.InvalidRebaseURLs:
1542
stack_on = self._get_full_stack_on()
1544
branch.set_stacked_on_url(stack_on)
1545
except (_mod_branch.UnstackableBranchFormat,
1546
errors.UnstackableRepositoryFormat):
1547
if self._require_stacking:
1550
def requires_stacking(self):
1551
"""Return True if this policy requires stacking."""
1552
return self._stack_on is not None and self._require_stacking
1554
def _get_full_stack_on(self):
1555
"""Get a fully-qualified URL for the stack_on location."""
1556
if self._stack_on is None:
1558
if self._stack_on_pwd is None:
1559
return self._stack_on
1561
return urlutils.join(self._stack_on_pwd, self._stack_on)
1563
def _add_fallback(self, repository, possible_transports=None):
1564
"""Add a fallback to the supplied repository, if stacking is set."""
1565
stack_on = self._get_full_stack_on()
1566
if stack_on is None:
1569
stacked_dir = ControlDir.open(
1570
stack_on, possible_transports=possible_transports)
1571
except errors.JailBreak:
1572
# We keep the stacking details, but we are in the server code so
1573
# actually stacking is not needed.
1576
stacked_repo = stacked_dir.open_branch().repository
1577
except errors.NotBranchError:
1578
stacked_repo = stacked_dir.open_repository()
1580
repository.add_fallback_repository(stacked_repo)
1581
except errors.UnstackableRepositoryFormat:
1582
if self._require_stacking:
1585
self._require_stacking = True
1587
def acquire_repository(self, make_working_trees=None, shared=False,
1588
possible_transports=None):
1589
"""Acquire a repository for this controlrdir.
1591
Implementations may create a new repository or use a pre-exising
1594
:param make_working_trees: If creating a repository, set
1595
make_working_trees to this value (if non-None)
1596
:param shared: If creating a repository, make it shared if True
1597
:return: A repository, is_new_flag (True if the repository was
1600
raise NotImplementedError(
1601
RepositoryAcquisitionPolicy.acquire_repository)
1604
1449
# Please register new formats after old formats so that formats