14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""ControlDir logic. The BzrDir is the basic control directory class.
17
"""ControlDir is the basic control directory class.
19
19
The ControlDir class is the base for the control directory used
20
by all bzr and foreign formats. For the ".bzr" implementation,
20
by all bzr and foreign formats. For the ".bzr" implementation,
21
21
see bzrlib.bzrdir.BzrDir.
24
from bzrlib.lazy_import import lazy_import
25
lazy_import(globals(), """
29
revision as _mod_revision,
32
from bzrlib.push import (
35
from bzrlib.trace import (
38
from bzrlib.transport import (
45
class ControlDir(object):
46
"""A control directory."""
48
def can_convert_format(self):
49
"""Return true if this bzrdir is one whose format we can convert from."""
52
def list_branches(self):
53
"""Return a sequence of all branches local to this control directory.
57
return [self.open_branch()]
58
except (errors.NotBranchError, errors.NoRepositoryPresent):
61
def is_control_filename(self, filename):
62
"""True if filename is the name of a path which is reserved for bzrdir's.
64
:param filename: A filename within the root transport of this bzrdir.
66
This is true IF and ONLY IF the filename is part of the namespace reserved
67
for bzr control dirs. Currently this is the '.bzr' directory in the root
68
of the root_transport. it is expected that plugins will need to extend
69
this in the future - for instance to make bzr talk with svn working
72
raise NotImplementedError(self.is_control_filename)
74
def needs_format_conversion(self, format=None):
75
"""Return true if this bzrdir needs convert_format run on it.
77
For instance, if the repository format is out of date but the
78
branch and working tree are not, this should return True.
80
:param format: Optional parameter indicating a specific desired
81
format we plan to arrive at.
83
raise NotImplementedError(self.needs_format_conversion)
85
def destroy_repository(self):
86
"""Destroy the repository in this BzrDir"""
87
raise NotImplementedError(self.destroy_repository)
89
def create_branch(self, name=None):
90
"""Create a branch in this BzrDir.
92
:param name: Name of the colocated branch to create, None for
95
The bzrdir's format will control what branch format is created.
96
For more control see BranchFormatXX.create(a_bzrdir).
98
raise NotImplementedError(self.create_branch)
100
def destroy_branch(self, name=None):
101
"""Destroy a branch in this BzrDir.
103
:param name: Name of the branch to destroy, None for the default
106
raise NotImplementedError(self.destroy_branch)
108
def create_workingtree(self, revision_id=None, from_branch=None,
109
accelerator_tree=None, hardlink=False):
110
"""Create a working tree at this BzrDir.
112
:param revision_id: create it as of this revision id.
113
:param from_branch: override bzrdir branch (for lightweight checkouts)
114
:param accelerator_tree: A tree which can be used for retrieving file
115
contents more quickly than the revision tree, i.e. a workingtree.
116
The revision tree will be used for cases where accelerator_tree's
117
content is different.
119
raise NotImplementedError(self.create_workingtree)
121
def destroy_workingtree(self):
122
"""Destroy the working tree at this BzrDir.
124
Formats that do not support this may raise UnsupportedOperation.
126
raise NotImplementedError(self.destroy_workingtree)
128
def destroy_workingtree_metadata(self):
129
"""Destroy the control files for the working tree at this BzrDir.
131
The contents of working tree files are not affected.
132
Formats that do not support this may raise UnsupportedOperation.
134
raise NotImplementedError(self.destroy_workingtree_metadata)
136
def get_branch_reference(self, name=None):
137
"""Return the referenced URL for the branch in this bzrdir.
139
:param name: Optional colocated branch name
140
:raises NotBranchError: If there is no Branch.
141
:raises NoColocatedBranchSupport: If a branch name was specified
142
but colocated branches are not supported.
143
:return: The URL the branch in this bzrdir references if it is a
144
reference branch, or None for regular branches.
147
raise errors.NoColocatedBranchSupport(self)
150
def get_branch_transport(self, branch_format, name=None):
151
"""Get the transport for use by branch format in this BzrDir.
153
Note that bzr dirs that do not support format strings will raise
154
IncompatibleFormat if the branch format they are given has
155
a format string, and vice versa.
157
If branch_format is None, the transport is returned with no
158
checking. If it is not None, then the returned transport is
159
guaranteed to point to an existing directory ready for use.
161
raise NotImplementedError(self.get_branch_transport)
163
def get_repository_transport(self, repository_format):
164
"""Get the transport for use by repository format in this BzrDir.
166
Note that bzr dirs that do not support format strings will raise
167
IncompatibleFormat if the repository format they are given has
168
a format string, and vice versa.
170
If repository_format is None, the transport is returned with no
171
checking. If it is not None, then the returned transport is
172
guaranteed to point to an existing directory ready for use.
174
raise NotImplementedError(self.get_repository_transport)
176
def get_workingtree_transport(self, tree_format):
177
"""Get the transport for use by workingtree format in this BzrDir.
179
Note that bzr dirs that do not support format strings will raise
180
IncompatibleFormat if the workingtree format they are given has a
181
format string, and vice versa.
183
If workingtree_format is None, the transport is returned with no
184
checking. If it is not None, then the returned transport is
185
guaranteed to point to an existing directory ready for use.
187
raise NotImplementedError(self.get_workingtree_transport)
189
def open_branch(self, name=None, unsupported=False,
190
ignore_fallbacks=False):
191
"""Open the branch object at this BzrDir if one is present.
193
If unsupported is True, then no longer supported branch formats can
196
TODO: static convenience version of this?
198
raise NotImplementedError(self.open_branch)
200
def open_repository(self, _unsupported=False):
201
"""Open the repository object at this BzrDir if one is present.
203
This will not follow the Branch object pointer - it's strictly a direct
204
open facility. Most client code should use open_branch().repository to
207
:param _unsupported: a private parameter, not part of the api.
208
TODO: static convenience version of this?
210
raise NotImplementedError(self.open_repository)
212
def find_repository(self):
213
"""Find the repository that should be used.
215
This does not require a branch as we use it to find the repo for
216
new branches as well as to hook existing branches up to their
219
raise NotImplementedError(self.find_repository)
221
def open_workingtree(self, _unsupported=False,
222
recommend_upgrade=True, from_branch=None):
223
"""Open the workingtree object at this BzrDir if one is present.
225
:param recommend_upgrade: Optional keyword parameter, when True (the
226
default), emit through the ui module a recommendation that the user
227
upgrade the working tree when the workingtree being opened is old
228
(but still fully supported).
229
:param from_branch: override bzrdir branch (for lightweight checkouts)
231
raise NotImplementedError(self.open_workingtree)
233
def has_branch(self, name=None):
234
"""Tell if this bzrdir contains a branch.
236
Note: if you're going to open the branch, you should just go ahead
237
and try, and not ask permission first. (This method just opens the
238
branch and discards it, and that's somewhat expensive.)
241
self.open_branch(name)
243
except errors.NotBranchError:
246
def has_workingtree(self):
247
"""Tell if this bzrdir contains a working tree.
249
This will still raise an exception if the bzrdir has a workingtree that
250
is remote & inaccessible.
252
Note: if you're going to open the working tree, you should just go ahead
253
and try, and not ask permission first. (This method just opens the
254
workingtree and discards it, and that's somewhat expensive.)
257
self.open_workingtree(recommend_upgrade=False)
259
except errors.NoWorkingTree:
262
def cloning_metadir(self, require_stacking=False):
263
"""Produce a metadir suitable for cloning or sprouting with.
265
These operations may produce workingtrees (yes, even though they're
266
"cloning" something that doesn't have a tree), so a viable workingtree
267
format must be selected.
269
:require_stacking: If True, non-stackable formats will be upgraded
270
to similar stackable formats.
271
:returns: a BzrDirFormat with all component formats either set
272
appropriately or set to None if that component should not be
275
raise NotImplementedError(self.cloning_metadir)
277
def checkout_metadir(self):
278
"""Produce a metadir suitable for checkouts of this controldir."""
279
return self.cloning_metadir()
281
def sprout(self, url, revision_id=None, force_new_repo=False,
282
recurse='down', possible_transports=None,
283
accelerator_tree=None, hardlink=False, stacked=False,
284
source_branch=None, create_tree_if_local=True):
285
"""Create a copy of this bzrdir prepared for use as a new line of
288
If url's last component does not exist, it will be created.
290
Attributes related to the identity of the source branch like
291
branch nickname will be cleaned, a working tree is created
292
whether one existed before or not; and a local branch is always
295
if revision_id is not None, then the clone operation may tune
296
itself to download less data.
297
:param accelerator_tree: A tree which can be used for retrieving file
298
contents more quickly than the revision tree, i.e. a workingtree.
299
The revision tree will be used for cases where accelerator_tree's
300
content is different.
301
:param hardlink: If true, hard-link files from accelerator_tree,
303
:param stacked: If true, create a stacked branch referring to the
304
location of this control directory.
305
:param create_tree_if_local: If true, a working-tree will be created
306
when working locally.
308
target_transport = get_transport(url, possible_transports)
309
target_transport.ensure_base()
310
cloning_format = self.cloning_metadir(stacked)
311
# Create/update the result branch
312
result = cloning_format.initialize_on_transport(target_transport)
313
# if a stacked branch wasn't requested, we don't create one
314
# even if the origin was stacked
315
stacked_branch_url = None
316
if source_branch is not None:
318
stacked_branch_url = self.root_transport.base
319
source_repository = source_branch.repository
322
source_branch = self.open_branch()
323
source_repository = source_branch.repository
325
stacked_branch_url = self.root_transport.base
326
except errors.NotBranchError:
329
source_repository = self.open_repository()
330
except errors.NoRepositoryPresent:
331
source_repository = None
332
repository_policy = result.determine_repository_policy(
333
force_new_repo, stacked_branch_url, require_stacking=stacked)
334
result_repo, is_new_repo = repository_policy.acquire_repository()
335
is_stacked = stacked or (len(result_repo._fallback_repositories) != 0)
336
if is_new_repo and revision_id is not None and not is_stacked:
337
fetch_spec = graph.PendingAncestryResult(
338
[revision_id], source_repository)
341
if source_repository is not None:
342
# Fetch while stacked to prevent unstacked fetch from
344
if fetch_spec is None:
345
result_repo.fetch(source_repository, revision_id=revision_id)
347
result_repo.fetch(source_repository, fetch_spec=fetch_spec)
349
if source_branch is None:
350
# this is for sprouting a bzrdir without a branch; is that
352
# Not especially, but it's part of the contract.
353
result_branch = result.create_branch()
355
result_branch = source_branch.sprout(result,
356
revision_id=revision_id, repository_policy=repository_policy)
357
mutter("created new branch %r" % (result_branch,))
359
# Create/update the result working tree
360
if (create_tree_if_local and
361
isinstance(target_transport, local.LocalTransport) and
362
(result_repo is None or result_repo.make_working_trees())):
363
wt = result.create_workingtree(accelerator_tree=accelerator_tree,
367
if wt.path2id('') is None:
369
wt.set_root_id(self.open_workingtree.get_root_id())
370
except errors.NoWorkingTree:
376
if recurse == 'down':
378
basis = wt.basis_tree()
380
subtrees = basis.iter_references()
381
elif result_branch is not None:
382
basis = result_branch.basis_tree()
384
subtrees = basis.iter_references()
385
elif source_branch is not None:
386
basis = source_branch.basis_tree()
388
subtrees = basis.iter_references()
393
for path, file_id in subtrees:
394
target = urlutils.join(url, urlutils.escape(path))
395
sublocation = source_branch.reference_parent(file_id, path)
396
sublocation.bzrdir.sprout(target,
397
basis.get_reference_revision(file_id, path),
398
force_new_repo=force_new_repo, recurse=recurse,
401
if basis is not None:
405
def push_branch(self, source, revision_id=None, overwrite=False,
406
remember=False, create_prefix=False):
407
"""Push the source branch into this BzrDir."""
409
# If we can open a branch, use its direct repository, otherwise see
410
# if there is a repository without a branch.
412
br_to = self.open_branch()
413
except errors.NotBranchError:
414
# Didn't find a branch, can we find a repository?
415
repository_to = self.find_repository()
417
# Found a branch, so we must have found a repository
418
repository_to = br_to.repository
420
push_result = PushResult()
421
push_result.source_branch = source
423
# We have a repository but no branch, copy the revisions, and then
425
repository_to.fetch(source.repository, revision_id=revision_id)
426
br_to = source.clone(self, revision_id=revision_id)
427
if source.get_push_location() is None or remember:
428
source.set_push_location(br_to.base)
429
push_result.stacked_on = None
430
push_result.branch_push_result = None
431
push_result.old_revno = None
432
push_result.old_revid = _mod_revision.NULL_REVISION
433
push_result.target_branch = br_to
434
push_result.master_branch = None
435
push_result.workingtree_updated = False
437
# We have successfully opened the branch, remember if necessary:
438
if source.get_push_location() is None or remember:
439
source.set_push_location(br_to.base)
441
tree_to = self.open_workingtree()
442
except errors.NotLocalUrl:
443
push_result.branch_push_result = source.push(br_to,
444
overwrite, stop_revision=revision_id)
445
push_result.workingtree_updated = False
446
except errors.NoWorkingTree:
447
push_result.branch_push_result = source.push(br_to,
448
overwrite, stop_revision=revision_id)
449
push_result.workingtree_updated = None # Not applicable
453
push_result.branch_push_result = source.push(
454
tree_to.branch, overwrite, stop_revision=revision_id)
458
push_result.workingtree_updated = True
459
push_result.old_revno = push_result.branch_push_result.old_revno
460
push_result.old_revid = push_result.branch_push_result.old_revid
461
push_result.target_branch = \
462
push_result.branch_push_result.target_branch