40
from bzrlib.transport import local
41
from bzrlib.push import (
38
from breezy.transport import local
39
from breezy.push import (
45
from bzrlib.i18n import gettext
43
from breezy.i18n import gettext
48
from bzrlib import registry
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)
51
68
class ControlComponent(object):
108
125
"""Return a sequence of all branches local to this control directory.
111
return self.get_branches().values()
128
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):
113
142
def get_branches(self):
114
143
"""Get all branches in this control directory, as a dictionary.
116
145
:return: Dictionary mapping branch names to instances.
119
return { "": self.open_branch() }
148
return {"": self.open_branch()}
120
149
except (errors.NotBranchError, errors.NoRepositoryPresent):
123
152
def is_control_filename(self, filename):
124
153
"""True if filename is the name of a path which is reserved for
175
204
def destroy_branch(self, name=None):
176
205
"""Destroy a branch in this ControlDir.
178
:param name: Name of the branch to destroy, None for the
207
:param name: Name of the branch to destroy, None for the
179
208
user selected branch or "" for the active branch.
180
209
:raise NotBranchError: When the branch does not exist
182
211
raise NotImplementedError(self.destroy_branch)
184
213
def create_workingtree(self, revision_id=None, from_branch=None,
185
accelerator_tree=None, hardlink=False):
214
accelerator_tree=None, hardlink=False):
186
215
"""Create a working tree at this ControlDir.
188
217
:param revision_id: create it as of this revision id.
189
:param from_branch: override controldir branch
218
:param from_branch: override controldir branch
190
219
(for lightweight checkouts)
191
220
:param accelerator_tree: A tree which can be used for retrieving file
192
221
contents more quickly than the revision tree, i.e. a workingtree.
380
410
raise NotImplementedError(self.sprout)
382
def push_branch(self, source, revision_id=None, overwrite=False,
383
remember=False, create_prefix=False):
412
def push_branch(self, source, revision_id=None, overwrite=False,
413
remember=False, create_prefix=False, lossy=False,
384
415
"""Push the source branch into this ControlDir."""
386
417
# If we can open a branch, use its direct repository, otherwise see
424
457
tree_to = self.open_workingtree()
425
458
except errors.NotLocalUrl:
426
push_result.branch_push_result = source.push(br_to,
427
overwrite, stop_revision=revision_id)
459
push_result.branch_push_result = source.push(
460
br_to, overwrite, stop_revision=revision_id, lossy=lossy,
461
tag_selector=tag_selector)
428
462
push_result.workingtree_updated = False
429
463
except errors.NoWorkingTree:
430
push_result.branch_push_result = source.push(br_to,
431
overwrite, stop_revision=revision_id)
432
push_result.workingtree_updated = None # Not applicable
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
469
with tree_to.lock_write():
436
470
push_result.branch_push_result = source.push(
437
tree_to.branch, overwrite, stop_revision=revision_id)
471
tree_to.branch, overwrite, stop_revision=revision_id,
472
lossy=lossy, tag_selector=tag_selector)
441
474
push_result.workingtree_updated = True
442
475
push_result.old_revno = push_result.branch_push_result.old_revno
443
476
push_result.old_revid = push_result.branch_push_result.old_revid
491
524
return self.clone_on_transport(_mod_transport.get_transport(url),
492
525
revision_id=revision_id,
493
526
force_new_repo=force_new_repo,
494
preserve_stacking=preserve_stacking)
527
preserve_stacking=preserve_stacking,
528
tag_selector=tag_selector)
496
530
def clone_on_transport(self, transport, revision_id=None,
497
force_new_repo=False, preserve_stacking=False, stacked_on=None,
498
create_prefix=False, use_existing_dir=True, no_tree=False):
531
force_new_repo=False, preserve_stacking=False, stacked_on=None,
532
create_prefix=False, use_existing_dir=True, no_tree=False,
499
534
"""Clone this controldir and its contents to transport verbatim.
501
536
:param transport: The transport for the location to produce the clone
701
737
# Keep initial base since 'transport' may be modified while following
702
738
# the redirections.
703
739
base = transport.base
704
741
def find_format(transport):
705
742
return transport, ControlDirFormat.find_format(transport,
708
745
def redirected(transport, e, redirection_notice):
709
746
redirected_transport = transport._redirected_to(e.source, e.target)
710
747
if redirected_transport is None:
711
748
raise errors.NotBranchError(base)
712
749
trace.note(gettext('{0} is{1} redirected to {2}').format(
713
transport.base, e.permanently, redirected_transport.base))
750
transport.base, e.permanently, redirected_transport.base))
714
751
return redirected_transport
755
792
result = klass.open_from_transport(a_transport)
756
793
return result, urlutils.unescape(a_transport.relpath(url))
757
except errors.NotBranchError, e:
794
except errors.NotBranchError:
759
796
except errors.PermissionDenied:
762
799
new_t = a_transport.clone('..')
763
except errors.InvalidURLJoin:
800
except urlutils.InvalidURLJoin:
764
801
# reached the root, whatever that may be
765
802
raise errors.NotBranchError(path=url)
766
803
if new_t.base == a_transport.base:
846
883
def __init__(self):
847
884
"""Create the default hooks."""
848
hooks.Hooks.__init__(self, "bzrlib.controldir", "ControlDir.hooks")
885
hooks.Hooks.__init__(self, "breezy.controldir", "ControlDir.hooks")
849
886
self.add_hook('pre_open',
850
"Invoked before attempting to open a ControlDir with the transport "
851
"that the open will use.", (1, 14))
887
"Invoked before attempting to open a ControlDir with the transport "
888
"that the open will use.", (1, 14))
852
889
self.add_hook('post_repo_init',
853
"Invoked after a repository has been initialized. "
854
"post_repo_init is called with a "
855
"bzrlib.controldir.RepoInitHookParams.",
890
"Invoked after a repository has been initialized. "
891
"post_repo_init is called with a "
892
"breezy.controldir.RepoInitHookParams.",
858
896
# install the default hooks
859
897
ControlDir.hooks = ControlDirHooks()
938
976
registry._LazyObjectGetter(module_name, member_name))
940
978
def _get_extra(self):
941
"""Return all "extra" formats, not usable in meta directories."""
943
for getter in self._extra_formats:
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())
950
988
def _get_all(self):
951
"""Return all formats, even those not usable in metadirs.
989
"""Return all formats, even those not usable in metadirs."""
954
for name in self.keys():
991
for getter in self._get_all_lazy():
956
993
if callable(fmt):
958
995
result.append(fmt)
959
return result + self._get_extra()
961
998
def _get_all_modules(self):
962
999
"""Return a set of the modules providing objects."""
1113
1136
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)
1126
1138
def __str__(self):
1127
1139
# Trim the newline
1128
1140
return self.get_format_description().rstrip()
1131
1143
def all_probers(klass):
1132
return klass._server_probers + klass._probers
1144
return klass._probers
1135
1147
def known_formats(klass):
1136
1148
"""Return all the known formats.
1139
1151
for prober_kls in klass.all_probers():
1140
result.update(prober_kls.known_formats())
1152
result.extend(prober_kls.known_formats())
1144
1156
def find_format(klass, transport, probers=None):
1145
1157
"""Return the format present at transport."""
1146
1158
if probers is None:
1147
probers = klass.all_probers()
1160
klass.all_probers(),
1161
key=lambda prober: prober.priority(transport))
1148
1162
for prober_kls in probers:
1149
1163
prober = prober_kls()
1172
1186
raise NotImplementedError(self.initialize_on_transport)
1174
1188
def initialize_on_transport_ex(self, transport, use_existing_dir=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):
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):
1178
1192
"""Create this format on transport.
1180
1194
The directory to initialize will be created.
1293
1338
def __init__(self):
1294
1339
"""Create a ControlDirFormatRegistry."""
1295
self._aliases = set()
1296
1340
self._registration_order = list()
1297
1341
super(ControlDirFormatRegistry, self).__init__()
1300
"""Return a set of the format names which are aliases."""
1301
return frozenset(self._aliases)
1303
1343
def register(self, key, factory, help, native=True, deprecated=False,
1304
hidden=False, experimental=False, alias=False):
1344
hidden=False, experimental=False):
1305
1345
"""Register a ControlDirFormat factory.
1307
1347
The factory must be a callable that takes one parameter: the key.
1311
1351
supplied directly.
1313
1353
registry.Registry.register(self, key, factory, help,
1314
ControlDirFormatInfo(native, deprecated, hidden, experimental))
1316
self._aliases.add(key)
1354
ControlDirFormatInfo(native, deprecated, hidden, experimental))
1317
1355
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))
1319
1370
def register_lazy(self, key, module_name, member_name, help, native=True,
1320
deprecated=False, hidden=False, experimental=False, alias=False):
1371
deprecated=False, hidden=False, experimental=False):
1321
1372
registry.Registry.register_lazy(self, key, module_name, member_name,
1322
help, ControlDirFormatInfo(native, deprecated, hidden, experimental))
1324
self._aliases.add(key)
1373
help, ControlDirFormatInfo(native, deprecated, hidden, experimental))
1325
1374
self._registration_order.append(key)
1327
1376
def set_default(self, key):
1440
1487
def __repr__(self):
1441
1488
if self.repository:
1442
1489
return "<%s for %s>" % (self.__class__.__name__,
1445
1492
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)
1449
1604
# Please register new formats after old formats so that formats