59
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)
62
68
class ControlComponent(object):
63
69
"""Abstract base class for control directory components.
124
130
def get_branches(self):
125
131
"""Get all branches in this control directory, as a dictionary.
127
133
:return: Dictionary mapping branch names to instances.
130
return { "": self.open_branch() }
136
return {"": self.open_branch()}
131
137
except (errors.NotBranchError, errors.NoRepositoryPresent):
134
140
def is_control_filename(self, filename):
135
141
"""True if filename is the name of a path which is reserved for
144
150
this in the future - for instance to make bzr talk with svn working
147
raise NotImplementedError(self.is_control_filename)
153
return self._format.is_control_filename(filename)
149
155
def needs_format_conversion(self, format=None):
150
156
"""Return true if this controldir needs convert_format run on it.
186
192
def destroy_branch(self, name=None):
187
193
"""Destroy a branch in this ControlDir.
189
:param name: Name of the branch to destroy, None for the
195
:param name: Name of the branch to destroy, None for the
190
196
user selected branch or "" for the active branch.
191
197
:raise NotBranchError: When the branch does not exist
193
199
raise NotImplementedError(self.destroy_branch)
195
201
def create_workingtree(self, revision_id=None, from_branch=None,
196
accelerator_tree=None, hardlink=False):
202
accelerator_tree=None, hardlink=False):
197
203
"""Create a working tree at this ControlDir.
199
205
:param revision_id: create it as of this revision id.
200
:param from_branch: override controldir branch
206
:param from_branch: override controldir branch
201
207
(for lightweight checkouts)
202
208
:param accelerator_tree: A tree which can be used for retrieving file
203
209
contents more quickly than the revision tree, i.e. a workingtree.
364
370
def sprout(self, url, revision_id=None, force_new_repo=False,
365
371
recurse='down', possible_transports=None,
366
372
accelerator_tree=None, hardlink=False, stacked=False,
367
source_branch=None, create_tree_if_local=True):
373
source_branch=None, create_tree_if_local=True,
368
375
"""Create a copy of this controldir prepared for use as a new line of
391
398
raise NotImplementedError(self.sprout)
393
def push_branch(self, source, revision_id=None, overwrite=False,
394
remember=False, create_prefix=False):
400
def push_branch(self, source, revision_id=None, overwrite=False,
401
remember=False, create_prefix=False, lossy=False,
395
403
"""Push the source branch into this ControlDir."""
397
405
# If we can open a branch, use its direct repository, otherwise see
416
424
revision_id = source.last_revision()
417
425
repository_to.fetch(source.repository, revision_id=revision_id)
418
br_to = source.clone(self, revision_id=revision_id)
426
br_to = source.sprout(
427
self, revision_id=revision_id, lossy=lossy,
428
tag_selector=tag_selector)
419
429
if source.get_push_location() is None or remember:
420
430
# FIXME: Should be done only if we succeed ? -- vila 2012-01-18
421
431
source.set_push_location(br_to.base)
435
445
tree_to = self.open_workingtree()
436
446
except errors.NotLocalUrl:
437
push_result.branch_push_result = source.push(br_to,
438
overwrite, stop_revision=revision_id)
447
push_result.branch_push_result = source.push(
448
br_to, overwrite, stop_revision=revision_id, lossy=lossy,
449
tag_selector=tag_selector)
439
450
push_result.workingtree_updated = False
440
451
except errors.NoWorkingTree:
441
push_result.branch_push_result = source.push(br_to,
442
overwrite, stop_revision=revision_id)
443
push_result.workingtree_updated = None # Not applicable
452
push_result.branch_push_result = source.push(
453
br_to, overwrite, stop_revision=revision_id, lossy=lossy,
454
tag_selector=tag_selector)
455
push_result.workingtree_updated = None # Not applicable
457
with tree_to.lock_write():
447
458
push_result.branch_push_result = source.push(
448
tree_to.branch, overwrite, stop_revision=revision_id)
459
tree_to.branch, overwrite, stop_revision=revision_id,
460
lossy=lossy, tag_selector=tag_selector)
452
462
push_result.workingtree_updated = True
453
463
push_result.old_revno = push_result.branch_push_result.old_revno
454
464
push_result.old_revid = push_result.branch_push_result.old_revid
486
496
raise NotImplementedError(self.check_conversion_target)
488
498
def clone(self, url, revision_id=None, force_new_repo=False,
489
preserve_stacking=False):
499
preserve_stacking=False, tag_selector=None):
490
500
"""Clone this controldir and its contents to url verbatim.
492
502
:param url: The url create the clone at. If url's last component does
502
512
return self.clone_on_transport(_mod_transport.get_transport(url),
503
513
revision_id=revision_id,
504
514
force_new_repo=force_new_repo,
505
preserve_stacking=preserve_stacking)
515
preserve_stacking=preserve_stacking,
516
tag_selector=tag_selector)
507
518
def clone_on_transport(self, transport, revision_id=None,
508
force_new_repo=False, preserve_stacking=False, stacked_on=None,
509
create_prefix=False, use_existing_dir=True, no_tree=False):
519
force_new_repo=False, preserve_stacking=False, stacked_on=None,
520
create_prefix=False, use_existing_dir=True, no_tree=False,
510
522
"""Clone this controldir and its contents to transport verbatim.
512
524
:param transport: The transport for the location to produce the clone
556
568
controldir = klass.open_from_transport(current_transport)
557
except (errors.NotBranchError, errors.PermissionDenied):
569
except (errors.NotBranchError, errors.PermissionDenied,
570
errors.UnknownFormatError):
560
573
recurse, value = evaluate(controldir)
679
692
if not isinstance(t, local.LocalTransport):
680
693
raise errors.NotLocalUrl(base)
681
694
controldir = klass.create_branch_and_repo(base,
683
format=format).controldir
696
format=format).controldir
684
697
return controldir.create_workingtree()
698
711
t = _mod_transport.get_transport(base, possible_transports)
699
712
return klass.open_from_transport(t, probers=probers,
700
_unsupported=_unsupported)
713
_unsupported=_unsupported)
703
716
def open_from_transport(klass, transport, _unsupported=False,
712
725
# Keep initial base since 'transport' may be modified while following
713
726
# the redirections.
714
727
base = transport.base
715
729
def find_format(transport):
716
730
return transport, ControlDirFormat.find_format(transport,
719
733
def redirected(transport, e, redirection_notice):
720
734
redirected_transport = transport._redirected_to(e.source, e.target)
721
735
if redirected_transport is None:
722
736
raise errors.NotBranchError(base)
723
737
trace.note(gettext('{0} is{1} redirected to {2}').format(
724
transport.base, e.permanently, redirected_transport.base))
738
transport.base, e.permanently, redirected_transport.base))
725
739
return redirected_transport
766
780
result = klass.open_from_transport(a_transport)
767
781
return result, urlutils.unescape(a_transport.relpath(url))
768
except errors.NotBranchError as e:
782
except errors.NotBranchError:
770
784
except errors.PermissionDenied:
795
809
def open_containing_tree_or_branch(klass, location,
796
possible_transports=None):
810
possible_transports=None):
797
811
"""Return the branch and working tree contained by a location.
799
813
Returns (tree, branch, relpath).
803
817
relpath is the portion of the path that is contained by the branch.
805
819
controldir, relpath = klass.open_containing(location,
806
possible_transports=possible_transports)
820
possible_transports=possible_transports)
807
821
tree, branch = controldir._get_tree_branch()
808
822
return tree, branch, relpath
844
858
if klass is not ControlDir:
845
859
raise AssertionError("ControlDir.create always creates the"
846
"default format, not one of %r" % klass)
860
"default format, not one of %r" % klass)
847
861
t = _mod_transport.get_transport(base, possible_transports)
849
863
if format is None:
858
872
"""Create the default hooks."""
859
873
hooks.Hooks.__init__(self, "breezy.controldir", "ControlDir.hooks")
860
874
self.add_hook('pre_open',
861
"Invoked before attempting to open a ControlDir with the transport "
862
"that the open will use.", (1, 14))
875
"Invoked before attempting to open a ControlDir with the transport "
876
"that the open will use.", (1, 14))
863
877
self.add_hook('post_repo_init',
864
"Invoked after a repository has been initialized. "
865
"post_repo_init is called with a "
866
"breezy.controldir.RepoInitHookParams.",
878
"Invoked after a repository has been initialized. "
879
"post_repo_init is called with a "
880
"breezy.controldir.RepoInitHookParams.",
869
884
# install the default hooks
870
885
ControlDir.hooks = ControlDirHooks()
891
906
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
893
908
"""Give an error or warning on old formats.
895
910
:param allow_unsupported: If true, allow opening
949
964
registry._LazyObjectGetter(module_name, member_name))
951
966
def _get_extra(self):
952
"""Return all "extra" formats, not usable in meta directories."""
954
for getter in self._extra_formats:
967
"""Return getters for extra formats, not usable in meta directories."""
968
return [getter.get_obj for getter in self._extra_formats]
970
def _get_all_lazy(self):
971
"""Return getters for all formats, even those not usable in metadirs."""
972
result = [self._dict[name].get_obj for name in self.keys()]
973
result.extend(self._get_extra())
961
976
def _get_all(self):
962
"""Return all formats, even those not usable in metadirs.
977
"""Return all formats, even those not usable in metadirs."""
965
for name in self.keys():
979
for getter in self._get_all_lazy():
967
981
if callable(fmt):
969
983
result.append(fmt)
970
return result + self._get_extra()
972
986
def _get_all_modules(self):
973
987
"""Return a set of the modules providing objects."""
1019
1033
_default_format = None
1020
1034
"""The default format used for new control directories."""
1022
_server_probers = []
1023
"""The registered server format probers, e.g. RemoteBzrProber.
1025
This is a list of Prober-derived classes.
1029
1037
"""The registered format probers, e.g. BzrProber.
1079
1087
return self.is_supported()
1081
1089
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
1083
1091
"""Give an error or warning on old formats.
1085
1093
:param allow_unsupported: If true, allow opening
1100
1108
def same_model(self, target_format):
1101
1109
return (self.repository_format.rich_root_data ==
1102
target_format.rich_root_data)
1110
target_format.rich_root_data)
1105
1113
def register_prober(klass, prober):
1116
1124
klass._probers.remove(prober)
1119
def register_server_prober(klass, prober):
1120
"""Register a control format prober for client-server environments.
1122
These probers will be used before ones registered with
1123
register_prober. This gives implementations that decide to the
1124
chance to grab it before anything looks at the contents of the format
1127
klass._server_probers.append(prober)
1129
1126
def __str__(self):
1130
1127
# Trim the newline
1131
1128
return self.get_format_description().rstrip()
1134
1131
def all_probers(klass):
1135
return klass._server_probers + klass._probers
1132
return klass._probers
1138
1135
def known_formats(klass):
1139
1136
"""Return all the known formats.
1142
1139
for prober_kls in klass.all_probers():
1143
result.update(prober_kls.known_formats())
1140
result.extend(prober_kls.known_formats())
1147
1144
def find_format(klass, transport, probers=None):
1148
1145
"""Return the format present at transport."""
1149
1146
if probers is None:
1150
probers = klass.all_probers()
1148
klass.all_probers(),
1149
key=lambda prober: prober.priority(transport))
1151
1150
for prober_kls in probers:
1152
1151
prober = prober_kls()
1175
1174
raise NotImplementedError(self.initialize_on_transport)
1177
1176
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
1178
create_prefix=False, force_new_repo=False, stacked_on=None,
1179
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
1180
shared_repo=False, vfs_only=False):
1177
create_prefix=False, force_new_repo=False, stacked_on=None,
1178
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
1179
shared_repo=False, vfs_only=False):
1181
1180
"""Create this format on transport.
1183
1182
The directory to initialize will be created.
1239
1238
raise NotImplementedError(self.supports_transport)
1241
def is_control_filename(klass, filename):
1242
"""True if filename is the name of a path which is reserved for
1245
:param filename: A filename within the root transport of this
1248
This is true IF and ONLY IF the filename is part of the namespace reserved
1249
for bzr control dirs. Currently this is the '.bzr' directory in the root
1250
of the root_transport. it is expected that plugins will need to extend
1251
this in the future - for instance to make bzr talk with svn working
1254
raise NotImplementedError(self.is_control_filename)
1242
1257
class Prober(object):
1243
1258
"""Abstract class that can be used to detect a particular kind of
1251
1266
probers that detect .bzr/ directories and Bazaar smart servers,
1254
Probers should be registered using the register_server_prober or
1255
register_prober methods on ControlDirFormat.
1269
Probers should be registered using the register_prober methods on
1258
1273
def probe_transport(self, transport):
1277
1292
raise NotImplementedError(klass.known_formats)
1295
def priority(klass, transport):
1296
"""Priority of this prober.
1298
A lower value means the prober gets checked first.
1302
-10: This is a "server" prober
1304
10: This is a regular file-based prober
1305
100: This is a prober for an unsupported format
1280
1310
class ControlDirFormatInfo(object):
1296
1326
def __init__(self):
1297
1327
"""Create a ControlDirFormatRegistry."""
1298
self._aliases = set()
1299
1328
self._registration_order = list()
1300
1329
super(ControlDirFormatRegistry, self).__init__()
1303
"""Return a set of the format names which are aliases."""
1304
return frozenset(self._aliases)
1306
1331
def register(self, key, factory, help, native=True, deprecated=False,
1307
hidden=False, experimental=False, alias=False):
1332
hidden=False, experimental=False):
1308
1333
"""Register a ControlDirFormat factory.
1310
1335
The factory must be a callable that takes one parameter: the key.
1314
1339
supplied directly.
1316
1341
registry.Registry.register(self, key, factory, help,
1317
ControlDirFormatInfo(native, deprecated, hidden, experimental))
1319
self._aliases.add(key)
1342
ControlDirFormatInfo(native, deprecated, hidden, experimental))
1320
1343
self._registration_order.append(key)
1345
def register_alias(self, key, target, hidden=False):
1346
"""Register a format alias.
1348
:param key: Alias name
1349
:param target: Target format
1350
:param hidden: Whether the alias is hidden
1352
info = self.get_info(target)
1353
registry.Registry.register_alias(self, key, target,
1354
ControlDirFormatInfo(
1355
native=info.native, deprecated=info.deprecated,
1356
hidden=hidden, experimental=info.experimental))
1322
1358
def register_lazy(self, key, module_name, member_name, help, native=True,
1323
deprecated=False, hidden=False, experimental=False, alias=False):
1359
deprecated=False, hidden=False, experimental=False):
1324
1360
registry.Registry.register_lazy(self, key, module_name, member_name,
1325
help, ControlDirFormatInfo(native, deprecated, hidden, experimental))
1327
self._aliases.add(key)
1361
help, ControlDirFormatInfo(native, deprecated, hidden, experimental))
1328
1362
self._registration_order.append(key)
1330
1364
def set_default(self, key):
1333
1367
This method must be called once and only once.
1335
registry.Registry.register(self, 'default', self.get(key),
1336
self.get_help(key), info=self.get_info(key))
1337
self._aliases.add('default')
1369
self.register_alias('default', key)
1339
1371
def set_default_repository(self, key):
1340
1372
"""Set the FormatRegistry default and Repository default.
1368
1400
if info.native:
1369
1401
help = '(native) ' + help
1370
1402
return ':%s:\n%s\n\n' % (key,
1371
textwrap.fill(help, initial_indent=' ',
1372
subsequent_indent=' ',
1373
break_long_words=False))
1403
textwrap.fill(help, initial_indent=' ',
1404
subsequent_indent=' ',
1405
break_long_words=False))
1374
1406
if default_realkey is not None:
1375
1407
output += wrapped(default_realkey, '(default) %s' % default_help,
1376
1408
self.get_info('default'))
1443
1475
def __repr__(self):
1444
1476
if self.repository:
1445
1477
return "<%s for %s>" % (self.__class__.__name__,
1448
1480
return "<%s for %s>" % (self.__class__.__name__,
1452
1484
def is_control_filename(filename):
1453
1485
"""Check if filename is used for control directories."""
1454
# TODO(jelmer): Allow registration by other VCSes
1455
return filename == '.bzr'
1486
# TODO(jelmer): Instead, have a function that returns all control
1488
for key, format in format_registry.items():
1489
if format().is_control_filename(filename):
1458
1495
class RepositoryAcquisitionPolicy(object):
1462
1499
for a branch that is being created. The most basic policy decision is
1463
1500
whether to create a new repository or use an existing one.
1465
1503
def __init__(self, stack_on, stack_on_pwd, require_stacking):
1466
1504
"""Constructor.
1519
1557
stacked_dir = ControlDir.open(
1520
stack_on, possible_transports=possible_transports)
1558
stack_on, possible_transports=possible_transports)
1521
1559
except errors.JailBreak:
1522
1560
# We keep the stacking details, but we are in the server code so
1523
1561
# actually stacking is not needed.
1535
1573
self._require_stacking = True
1537
1575
def acquire_repository(self, make_working_trees=None, shared=False,
1538
possible_transports=None):
1576
possible_transports=None):
1539
1577
"""Acquire a repository for this controlrdir.
1541
1579
Implementations may create a new repository or use a pre-exising
1547
1585
:return: A repository, is_new_flag (True if the repository was
1550
raise NotImplementedError(RepositoryAcquisitionPolicy.acquire_repository)
1588
raise NotImplementedError(
1589
RepositoryAcquisitionPolicy.acquire_repository)
1553
1592
# Please register new formats after old formats so that formats