Merge lp:~jelmer/bzr/sprout-to-bzrdir into lp:bzr

Proposed by Jelmer Vernooij on 2011-03-23
Status: Merged
Approved by: Jelmer Vernooij on 2011-03-24
Approved revision: 5736
Merged at revision: 5738
Proposed branch: lp:~jelmer/bzr/sprout-to-bzrdir
Merge into: lp:bzr
Diff against target: 357 lines (+163/-130)
3 files modified
bzrlib/bzrdir.py (+158/-0)
bzrlib/controldir.py (+1/-130)
doc/en/release-notes/bzr-2.4.txt (+4/-0)
To merge this branch: bzr merge lp:~jelmer/bzr/sprout-to-bzrdir
Reviewer Review Type Date Requested Status
Andrew Bennetts 2011-03-23 Approve on 2011-03-23
Review via email: mp+54561@code.launchpad.net

Commit message

Move bzr-specific implementation of ControlDir.sprout to Bzrdir.

Description of the change

Move the implementation of ControlDir.sprout() to BzrDir.sprout(). Instead, simply raise NotImplementedError.

The current implementation is too BzrDir-specific, so it makes more sense to just have the foreign plugins provide their own implementation.

To post a comment you must log in.
Andrew Bennetts (spiv) wrote :

This seems like a reasonable change to me.

It'd be nice to slowly converge on having foreign formats and BzrDir do as much in common as possible, but as a first step acknowledging that sprout is currently quite BzrDir-specific is fair enough.

review: Approve
Andrew Bennetts (spiv) wrote :

(Don't forget to mention this in release-notes)

Jelmer Vernooij (jelmer) wrote :

sent to pqm by email

lp:~jelmer/bzr/sprout-to-bzrdir updated on 2011-03-24
5737. By Jelmer Vernooij on 2011-03-24

Update release notes.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/bzrdir.py'
2--- bzrlib/bzrdir.py 2011-03-22 12:11:20 +0000
3+++ bzrlib/bzrdir.py 2011-03-24 00:18:30 +0000
4@@ -33,9 +33,11 @@
5
6 import bzrlib
7 from bzrlib import (
8+ cleanup,
9 config,
10 controldir,
11 errors,
12+ fetch,
13 graph,
14 lockable_files,
15 lockdir,
16@@ -60,6 +62,7 @@
17 """)
18
19 from bzrlib.trace import (
20+ mutter,
21 note,
22 )
23
24@@ -387,6 +390,161 @@
25 policy = self.determine_repository_policy(force_new_repo)
26 return policy.acquire_repository()[0]
27
28+ def _find_source_repo(self, add_cleanup, source_branch):
29+ """Find the source branch and repo for a sprout operation.
30+
31+ This is helper intended for use by _sprout.
32+
33+ :returns: (source_branch, source_repository). Either or both may be
34+ None. If not None, they will be read-locked (and their unlock(s)
35+ scheduled via the add_cleanup param).
36+ """
37+ if source_branch is not None:
38+ add_cleanup(source_branch.lock_read().unlock)
39+ return source_branch, source_branch.repository
40+ try:
41+ source_branch = self.open_branch()
42+ source_repository = source_branch.repository
43+ except errors.NotBranchError:
44+ source_branch = None
45+ try:
46+ source_repository = self.open_repository()
47+ except errors.NoRepositoryPresent:
48+ source_repository = None
49+ else:
50+ add_cleanup(source_repository.lock_read().unlock)
51+ else:
52+ add_cleanup(source_branch.lock_read().unlock)
53+ return source_branch, source_repository
54+
55+ def sprout(self, url, revision_id=None, force_new_repo=False,
56+ recurse='down', possible_transports=None,
57+ accelerator_tree=None, hardlink=False, stacked=False,
58+ source_branch=None, create_tree_if_local=True):
59+ """Create a copy of this controldir prepared for use as a new line of
60+ development.
61+
62+ If url's last component does not exist, it will be created.
63+
64+ Attributes related to the identity of the source branch like
65+ branch nickname will be cleaned, a working tree is created
66+ whether one existed before or not; and a local branch is always
67+ created.
68+
69+ if revision_id is not None, then the clone operation may tune
70+ itself to download less data.
71+ :param accelerator_tree: A tree which can be used for retrieving file
72+ contents more quickly than the revision tree, i.e. a workingtree.
73+ The revision tree will be used for cases where accelerator_tree's
74+ content is different.
75+ :param hardlink: If true, hard-link files from accelerator_tree,
76+ where possible.
77+ :param stacked: If true, create a stacked branch referring to the
78+ location of this control directory.
79+ :param create_tree_if_local: If true, a working-tree will be created
80+ when working locally.
81+ """
82+ operation = cleanup.OperationWithCleanups(self._sprout)
83+ return operation.run(url, revision_id=revision_id,
84+ force_new_repo=force_new_repo, recurse=recurse,
85+ possible_transports=possible_transports,
86+ accelerator_tree=accelerator_tree, hardlink=hardlink,
87+ stacked=stacked, source_branch=source_branch,
88+ create_tree_if_local=create_tree_if_local)
89+
90+ def _sprout(self, op, url, revision_id=None, force_new_repo=False,
91+ recurse='down', possible_transports=None,
92+ accelerator_tree=None, hardlink=False, stacked=False,
93+ source_branch=None, create_tree_if_local=True):
94+ add_cleanup = op.add_cleanup
95+ fetch_spec_factory = fetch.FetchSpecFactory()
96+ if revision_id is not None:
97+ fetch_spec_factory.add_revision_ids([revision_id])
98+ fetch_spec_factory.source_branch_stop_revision_id = revision_id
99+ target_transport = _mod_transport.get_transport(url,
100+ possible_transports)
101+ target_transport.ensure_base()
102+ cloning_format = self.cloning_metadir(stacked)
103+ # Create/update the result branch
104+ result = cloning_format.initialize_on_transport(target_transport)
105+ source_branch, source_repository = self._find_source_repo(
106+ add_cleanup, source_branch)
107+ fetch_spec_factory.source_branch = source_branch
108+ # if a stacked branch wasn't requested, we don't create one
109+ # even if the origin was stacked
110+ if stacked and source_branch is not None:
111+ stacked_branch_url = self.root_transport.base
112+ else:
113+ stacked_branch_url = None
114+ repository_policy = result.determine_repository_policy(
115+ force_new_repo, stacked_branch_url, require_stacking=stacked)
116+ result_repo, is_new_repo = repository_policy.acquire_repository()
117+ add_cleanup(result_repo.lock_write().unlock)
118+ fetch_spec_factory.source_repo = source_repository
119+ fetch_spec_factory.target_repo = result_repo
120+ if stacked or (len(result_repo._fallback_repositories) != 0):
121+ target_repo_kind = fetch.TargetRepoKinds.STACKED
122+ elif is_new_repo:
123+ target_repo_kind = fetch.TargetRepoKinds.EMPTY
124+ else:
125+ target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
126+ fetch_spec_factory.target_repo_kind = target_repo_kind
127+ if source_repository is not None:
128+ fetch_spec = fetch_spec_factory.make_fetch_spec()
129+ result_repo.fetch(source_repository, fetch_spec=fetch_spec)
130+
131+ if source_branch is None:
132+ # this is for sprouting a controldir without a branch; is that
133+ # actually useful?
134+ # Not especially, but it's part of the contract.
135+ result_branch = result.create_branch()
136+ else:
137+ result_branch = source_branch.sprout(result,
138+ revision_id=revision_id, repository_policy=repository_policy,
139+ repository=result_repo)
140+ mutter("created new branch %r" % (result_branch,))
141+
142+ # Create/update the result working tree
143+ if (create_tree_if_local and
144+ isinstance(target_transport, local.LocalTransport) and
145+ (result_repo is None or result_repo.make_working_trees())):
146+ wt = result.create_workingtree(accelerator_tree=accelerator_tree,
147+ hardlink=hardlink, from_branch=result_branch)
148+ wt.lock_write()
149+ try:
150+ if wt.path2id('') is None:
151+ try:
152+ wt.set_root_id(self.open_workingtree.get_root_id())
153+ except errors.NoWorkingTree:
154+ pass
155+ finally:
156+ wt.unlock()
157+ else:
158+ wt = None
159+ if recurse == 'down':
160+ basis = None
161+ if wt is not None:
162+ basis = wt.basis_tree()
163+ elif result_branch is not None:
164+ basis = result_branch.basis_tree()
165+ elif source_branch is not None:
166+ basis = source_branch.basis_tree()
167+ if basis is not None:
168+ add_cleanup(basis.lock_read().unlock)
169+ subtrees = basis.iter_references()
170+ else:
171+ subtrees = []
172+ for path, file_id in subtrees:
173+ target = urlutils.join(url, urlutils.escape(path))
174+ sublocation = source_branch.reference_parent(file_id, path)
175+ sublocation.bzrdir.sprout(target,
176+ basis.get_reference_revision(file_id, path),
177+ force_new_repo=force_new_repo, recurse=recurse,
178+ stacked=stacked)
179+ return result
180+
181+
182+
183 @staticmethod
184 def create_branch_convenience(base, force_new_repo=False,
185 force_new_tree=None, format=None,
186
187=== modified file 'bzrlib/controldir.py'
188--- bzrlib/controldir.py 2011-03-23 14:31:38 +0000
189+++ bzrlib/controldir.py 2011-03-24 00:18:30 +0000
190@@ -27,9 +27,7 @@
191 import textwrap
192
193 from bzrlib import (
194- cleanup,
195 errors,
196- fetch,
197 revision as _mod_revision,
198 transport as _mod_transport,
199 ui,
200@@ -38,9 +36,6 @@
201 from bzrlib.push import (
202 PushResult,
203 )
204-from bzrlib.trace import (
205- mutter,
206- )
207 from bzrlib.transport import (
208 local,
209 )
210@@ -336,131 +331,7 @@
211 :param create_tree_if_local: If true, a working-tree will be created
212 when working locally.
213 """
214- operation = cleanup.OperationWithCleanups(self._sprout)
215- return operation.run(url, revision_id=revision_id,
216- force_new_repo=force_new_repo, recurse=recurse,
217- possible_transports=possible_transports,
218- accelerator_tree=accelerator_tree, hardlink=hardlink,
219- stacked=stacked, source_branch=source_branch,
220- create_tree_if_local=create_tree_if_local)
221-
222- def _sprout(self, op, url, revision_id=None, force_new_repo=False,
223- recurse='down', possible_transports=None,
224- accelerator_tree=None, hardlink=False, stacked=False,
225- source_branch=None, create_tree_if_local=True):
226- add_cleanup = op.add_cleanup
227- fetch_spec_factory = fetch.FetchSpecFactory()
228- if revision_id is not None:
229- fetch_spec_factory.add_revision_ids([revision_id])
230- fetch_spec_factory.source_branch_stop_revision_id = revision_id
231- target_transport = _mod_transport.get_transport(url,
232- possible_transports)
233- target_transport.ensure_base()
234- cloning_format = self.cloning_metadir(stacked)
235- # Create/update the result branch
236- result = cloning_format.initialize_on_transport(target_transport)
237- source_branch, source_repository = self._find_source_repo(
238- add_cleanup, source_branch)
239- fetch_spec_factory.source_branch = source_branch
240- # if a stacked branch wasn't requested, we don't create one
241- # even if the origin was stacked
242- if stacked and source_branch is not None:
243- stacked_branch_url = self.root_transport.base
244- else:
245- stacked_branch_url = None
246- repository_policy = result.determine_repository_policy(
247- force_new_repo, stacked_branch_url, require_stacking=stacked)
248- result_repo, is_new_repo = repository_policy.acquire_repository()
249- add_cleanup(result_repo.lock_write().unlock)
250- fetch_spec_factory.source_repo = source_repository
251- fetch_spec_factory.target_repo = result_repo
252- if stacked or (len(result_repo._fallback_repositories) != 0):
253- target_repo_kind = fetch.TargetRepoKinds.STACKED
254- elif is_new_repo:
255- target_repo_kind = fetch.TargetRepoKinds.EMPTY
256- else:
257- target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
258- fetch_spec_factory.target_repo_kind = target_repo_kind
259- if source_repository is not None:
260- fetch_spec = fetch_spec_factory.make_fetch_spec()
261- result_repo.fetch(source_repository, fetch_spec=fetch_spec)
262-
263- if source_branch is None:
264- # this is for sprouting a controldir without a branch; is that
265- # actually useful?
266- # Not especially, but it's part of the contract.
267- result_branch = result.create_branch()
268- else:
269- result_branch = source_branch.sprout(result,
270- revision_id=revision_id, repository_policy=repository_policy,
271- repository=result_repo)
272- mutter("created new branch %r" % (result_branch,))
273-
274- # Create/update the result working tree
275- if (create_tree_if_local and
276- isinstance(target_transport, local.LocalTransport) and
277- (result_repo is None or result_repo.make_working_trees())):
278- wt = result.create_workingtree(accelerator_tree=accelerator_tree,
279- hardlink=hardlink, from_branch=result_branch)
280- wt.lock_write()
281- try:
282- if wt.path2id('') is None:
283- try:
284- wt.set_root_id(self.open_workingtree.get_root_id())
285- except errors.NoWorkingTree:
286- pass
287- finally:
288- wt.unlock()
289- else:
290- wt = None
291- if recurse == 'down':
292- basis = None
293- if wt is not None:
294- basis = wt.basis_tree()
295- elif result_branch is not None:
296- basis = result_branch.basis_tree()
297- elif source_branch is not None:
298- basis = source_branch.basis_tree()
299- if basis is not None:
300- add_cleanup(basis.lock_read().unlock)
301- subtrees = basis.iter_references()
302- else:
303- subtrees = []
304- for path, file_id in subtrees:
305- target = urlutils.join(url, urlutils.escape(path))
306- sublocation = source_branch.reference_parent(file_id, path)
307- sublocation.bzrdir.sprout(target,
308- basis.get_reference_revision(file_id, path),
309- force_new_repo=force_new_repo, recurse=recurse,
310- stacked=stacked)
311- return result
312-
313- def _find_source_repo(self, add_cleanup, source_branch):
314- """Find the source branch and repo for a sprout operation.
315-
316- This is helper intended for use by _sprout.
317-
318- :returns: (source_branch, source_repository). Either or both may be
319- None. If not None, they will be read-locked (and their unlock(s)
320- scheduled via the add_cleanup param).
321- """
322- if source_branch is not None:
323- add_cleanup(source_branch.lock_read().unlock)
324- return source_branch, source_branch.repository
325- try:
326- source_branch = self.open_branch()
327- source_repository = source_branch.repository
328- except errors.NotBranchError:
329- source_branch = None
330- try:
331- source_repository = self.open_repository()
332- except errors.NoRepositoryPresent:
333- source_repository = None
334- else:
335- add_cleanup(source_repository.lock_read().unlock)
336- else:
337- add_cleanup(source_branch.lock_read().unlock)
338- return source_branch, source_repository
339+ raise NotImplementedError(self.sprout)
340
341 def push_branch(self, source, revision_id=None, overwrite=False,
342 remember=False, create_prefix=False):
343
344=== modified file 'doc/en/release-notes/bzr-2.4.txt'
345--- doc/en/release-notes/bzr-2.4.txt 2011-03-23 12:56:01 +0000
346+++ doc/en/release-notes/bzr-2.4.txt 2011-03-24 00:18:30 +0000
347@@ -230,6 +230,10 @@
348 ``import_last_revision_info_and_tags`` method instead.
349 (Andrew Bennetts)
350
351+* Because it was too specific to BzrDir implementations,
352+ ``ControlDir.sprout`` no longer has a default implementation; it now
353+ raises ``NotImplementedError``. (Jelmer Vernooij, #717937)
354+
355 * ``ControlDirFormat.register_format`` has been removed. Instead,
356 ``Prober`` implementations should now implement a ``known_formats``
357 method. (Jelmer Vernooij)