/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/reconfigure.py

  • Committer: Robert Collins
  • Date: 2010-07-04 06:22:11 UTC
  • mto: This revision was merged to the branch mainline in revision 5332.
  • Revision ID: robertc@robertcollins.net-20100704062211-tk9hw6bnsn5x47fm
``bzrlib.lsprof.profile`` will no longer silently generate bad threaded
profiles when concurrent profile requests are made. Instead the profile
requests will be serialised. Reentrant requests will now deadlock.
(Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2007-2010 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Reconfigure a bzrdir into a new tree/branch/repository layout.
 
18
 
 
19
Various types of reconfiguration operation are available either by
 
20
constructing a class or using a factory method on Reconfigure.
 
21
"""
 
22
 
 
23
 
 
24
from bzrlib import (
 
25
    branch,
 
26
    bzrdir,
 
27
    errors,
 
28
    trace,
 
29
    ui,
 
30
    urlutils,
 
31
    )
 
32
 
 
33
 
 
34
# TODO: common base class for all reconfigure operations, making no
 
35
# assumptions about what kind of change will be done.
 
36
 
 
37
 
 
38
class ReconfigureStackedOn(object):
 
39
    """Reconfigures a branch to be stacked on another branch."""
 
40
 
 
41
    def apply(self, bzrdir, stacked_on_url):
 
42
        branch = bzrdir.open_branch()
 
43
        # it may be a path relative to the cwd or a url; the branch wants
 
44
        # a path relative to itself...
 
45
        on_url = urlutils.relative_url(branch.base,
 
46
            urlutils.normalize_url(stacked_on_url))
 
47
        branch.lock_write()
 
48
        try:
 
49
            branch.set_stacked_on_url(on_url)
 
50
            if not trace.is_quiet():
 
51
                ui.ui_factory.note(
 
52
                    "%s is now stacked on %s\n"
 
53
                    % (branch.base, branch.get_stacked_on_url()))
 
54
        finally:
 
55
            branch.unlock()
 
56
 
 
57
 
 
58
class ReconfigureUnstacked(object):
 
59
 
 
60
    def apply(self, bzrdir):
 
61
        branch = bzrdir.open_branch()
 
62
        branch.lock_write()
 
63
        try:
 
64
            branch.set_stacked_on_url(None)
 
65
            if not trace.is_quiet():
 
66
                ui.ui_factory.note(
 
67
                    "%s is now not stacked\n"
 
68
                    % (branch.base,))
 
69
        finally:
 
70
            branch.unlock()
 
71
 
 
72
 
 
73
class Reconfigure(object):
 
74
 
 
75
    def __init__(self, bzrdir, new_bound_location=None):
 
76
        self.bzrdir = bzrdir
 
77
        self.new_bound_location = new_bound_location
 
78
        self.local_repository = None
 
79
        try:
 
80
            self.repository = self.bzrdir.find_repository()
 
81
        except errors.NoRepositoryPresent:
 
82
            self.repository = None
 
83
            self.local_repository = None
 
84
        else:
 
85
            if (self.repository.user_url == self.bzrdir.user_url):
 
86
                self.local_repository = self.repository
 
87
            else:
 
88
                self.local_repository = None
 
89
        try:
 
90
            branch = self.bzrdir.open_branch()
 
91
            if branch.user_url == bzrdir.user_url:
 
92
                self.local_branch = branch
 
93
                self.referenced_branch = None
 
94
            else:
 
95
                self.local_branch = None
 
96
                self.referenced_branch = branch
 
97
        except errors.NotBranchError:
 
98
            self.local_branch = None
 
99
            self.referenced_branch = None
 
100
        try:
 
101
            self.tree = bzrdir.open_workingtree()
 
102
        except errors.NoWorkingTree:
 
103
            self.tree = None
 
104
        self._unbind = False
 
105
        self._bind = False
 
106
        self._destroy_reference = False
 
107
        self._create_reference = False
 
108
        self._destroy_branch = False
 
109
        self._create_branch = False
 
110
        self._destroy_tree = False
 
111
        self._create_tree = False
 
112
        self._create_repository = False
 
113
        self._destroy_repository = False
 
114
        self._repository_trees = None
 
115
 
 
116
    @staticmethod
 
117
    def to_branch(bzrdir):
 
118
        """Return a Reconfiguration to convert this bzrdir into a branch
 
119
 
 
120
        :param bzrdir: The bzrdir to reconfigure
 
121
        :raise errors.AlreadyBranch: if bzrdir is already a branch
 
122
        """
 
123
        reconfiguration = Reconfigure(bzrdir)
 
124
        reconfiguration._plan_changes(want_tree=False, want_branch=True,
 
125
                                      want_bound=False, want_reference=False)
 
126
        if not reconfiguration.changes_planned():
 
127
            raise errors.AlreadyBranch(bzrdir)
 
128
        return reconfiguration
 
129
 
 
130
    @staticmethod
 
131
    def to_tree(bzrdir):
 
132
        """Return a Reconfiguration to convert this bzrdir into a tree
 
133
 
 
134
        :param bzrdir: The bzrdir to reconfigure
 
135
        :raise errors.AlreadyTree: if bzrdir is already a tree
 
136
        """
 
137
        reconfiguration = Reconfigure(bzrdir)
 
138
        reconfiguration._plan_changes(want_tree=True, want_branch=True,
 
139
                                      want_bound=False, want_reference=False)
 
140
        if not reconfiguration.changes_planned():
 
141
            raise errors.AlreadyTree(bzrdir)
 
142
        return reconfiguration
 
143
 
 
144
    @staticmethod
 
145
    def to_checkout(bzrdir, bound_location=None):
 
146
        """Return a Reconfiguration to convert this bzrdir into a checkout
 
147
 
 
148
        :param bzrdir: The bzrdir to reconfigure
 
149
        :param bound_location: The location the checkout should be bound to.
 
150
        :raise errors.AlreadyCheckout: if bzrdir is already a checkout
 
151
        """
 
152
        reconfiguration = Reconfigure(bzrdir, bound_location)
 
153
        reconfiguration._plan_changes(want_tree=True, want_branch=True,
 
154
                                      want_bound=True, want_reference=False)
 
155
        if not reconfiguration.changes_planned():
 
156
            raise errors.AlreadyCheckout(bzrdir)
 
157
        return reconfiguration
 
158
 
 
159
    @classmethod
 
160
    def to_lightweight_checkout(klass, bzrdir, reference_location=None):
 
161
        """Make a Reconfiguration to convert bzrdir into a lightweight checkout
 
162
 
 
163
        :param bzrdir: The bzrdir to reconfigure
 
164
        :param bound_location: The location the checkout should be bound to.
 
165
        :raise errors.AlreadyLightweightCheckout: if bzrdir is already a
 
166
            lightweight checkout
 
167
        """
 
168
        reconfiguration = klass(bzrdir, reference_location)
 
169
        reconfiguration._plan_changes(want_tree=True, want_branch=False,
 
170
                                      want_bound=False, want_reference=True)
 
171
        if not reconfiguration.changes_planned():
 
172
            raise errors.AlreadyLightweightCheckout(bzrdir)
 
173
        return reconfiguration
 
174
 
 
175
    @classmethod
 
176
    def to_use_shared(klass, bzrdir):
 
177
        """Convert a standalone branch into a repository branch"""
 
178
        reconfiguration = klass(bzrdir)
 
179
        reconfiguration._set_use_shared(use_shared=True)
 
180
        if not reconfiguration.changes_planned():
 
181
            raise errors.AlreadyUsingShared(bzrdir)
 
182
        return reconfiguration
 
183
 
 
184
    @classmethod
 
185
    def to_standalone(klass, bzrdir):
 
186
        """Convert a repository branch into a standalone branch"""
 
187
        reconfiguration = klass(bzrdir)
 
188
        reconfiguration._set_use_shared(use_shared=False)
 
189
        if not reconfiguration.changes_planned():
 
190
            raise errors.AlreadyStandalone(bzrdir)
 
191
        return reconfiguration
 
192
 
 
193
    @classmethod
 
194
    def set_repository_trees(klass, bzrdir, with_trees):
 
195
        """Adjust a repository's working tree presence default"""
 
196
        reconfiguration = klass(bzrdir)
 
197
        if not reconfiguration.repository.is_shared():
 
198
            raise errors.ReconfigurationNotSupported(reconfiguration.bzrdir)
 
199
        if with_trees and reconfiguration.repository.make_working_trees():
 
200
            raise errors.AlreadyWithTrees(bzrdir)
 
201
        elif (not with_trees
 
202
              and not reconfiguration.repository.make_working_trees()):
 
203
            raise errors.AlreadyWithNoTrees(bzrdir)
 
204
        else:
 
205
            reconfiguration._repository_trees = with_trees
 
206
        return reconfiguration
 
207
 
 
208
    def _plan_changes(self, want_tree, want_branch, want_bound,
 
209
                      want_reference):
 
210
        """Determine which changes are needed to assume the configuration"""
 
211
        if not want_branch and not want_reference:
 
212
            raise errors.ReconfigurationNotSupported(self.bzrdir)
 
213
        if want_branch and want_reference:
 
214
            raise errors.ReconfigurationNotSupported(self.bzrdir)
 
215
        if self.repository is None:
 
216
            if not want_reference:
 
217
                self._create_repository = True
 
218
        else:
 
219
            if want_reference and (
 
220
                self.repository.user_url == self.bzrdir.user_url):
 
221
                if not self.repository.is_shared():
 
222
                    self._destroy_repository = True
 
223
        if self.referenced_branch is None:
 
224
            if want_reference:
 
225
                self._create_reference = True
 
226
                if self.local_branch is not None:
 
227
                    self._destroy_branch = True
 
228
        else:
 
229
            if not want_reference:
 
230
                self._destroy_reference = True
 
231
        if self.local_branch is None:
 
232
            if want_branch is True:
 
233
                self._create_branch = True
 
234
                if want_bound:
 
235
                    self._bind = True
 
236
        else:
 
237
            if want_bound:
 
238
                if self.local_branch.get_bound_location() is None:
 
239
                    self._bind = True
 
240
            else:
 
241
                if self.local_branch.get_bound_location() is not None:
 
242
                    self._unbind = True
 
243
        if not want_tree and self.tree is not None:
 
244
            self._destroy_tree = True
 
245
        if want_tree and self.tree is None:
 
246
            self._create_tree = True
 
247
 
 
248
    def _set_use_shared(self, use_shared=None):
 
249
        if use_shared is None:
 
250
            return
 
251
        if use_shared:
 
252
            if self.local_repository is not None:
 
253
                self._destroy_repository = True
 
254
        else:
 
255
            if self.local_repository is None:
 
256
                self._create_repository = True
 
257
 
 
258
    def changes_planned(self):
 
259
        """Return True if changes are planned, False otherwise"""
 
260
        return (self._unbind or self._bind or self._destroy_tree
 
261
                or self._create_tree or self._destroy_reference
 
262
                or self._create_branch or self._create_repository
 
263
                or self._create_reference or self._destroy_repository)
 
264
 
 
265
    def _check(self):
 
266
        """Raise if reconfiguration would destroy local changes"""
 
267
        if self._destroy_tree and self.tree.has_changes():
 
268
                raise errors.UncommittedChanges(self.tree)
 
269
        if self._create_reference and self.local_branch is not None:
 
270
            reference_branch = branch.Branch.open(self._select_bind_location())
 
271
            if (reference_branch.last_revision() !=
 
272
                self.local_branch.last_revision()):
 
273
                raise errors.UnsyncedBranches(self.bzrdir, reference_branch)
 
274
 
 
275
    def _select_bind_location(self):
 
276
        """Select a location to bind or create a reference to.
 
277
 
 
278
        Preference is:
 
279
        1. user specified location
 
280
        2. branch reference location (it's a kind of bind location)
 
281
        3. current bind location
 
282
        4. previous bind location (it was a good choice once)
 
283
        5. push location (it's writeable, so committable)
 
284
        6. parent location (it's pullable, so update-from-able)
 
285
        """
 
286
        if self.new_bound_location is not None:
 
287
            return self.new_bound_location
 
288
        if self.local_branch is not None:
 
289
            bound = self.local_branch.get_bound_location()
 
290
            if bound is not None:
 
291
                return bound
 
292
            old_bound = self.local_branch.get_old_bound_location()
 
293
            if old_bound is not None:
 
294
                return old_bound
 
295
            push_location = self.local_branch.get_push_location()
 
296
            if push_location is not None:
 
297
                return push_location
 
298
            parent = self.local_branch.get_parent()
 
299
            if parent is not None:
 
300
                return parent
 
301
        elif self.referenced_branch is not None:
 
302
            return self.referenced_branch.base
 
303
        raise errors.NoBindLocation(self.bzrdir)
 
304
 
 
305
    def apply(self, force=False):
 
306
        """Apply the reconfiguration
 
307
 
 
308
        :param force: If true, the reconfiguration is applied even if it will
 
309
            destroy local changes.
 
310
        :raise errors.UncommittedChanges: if the local tree is to be destroyed
 
311
            but contains uncommitted changes.
 
312
        :raise errors.NoBindLocation: if no bind location was specified and
 
313
            none could be autodetected.
 
314
        """
 
315
        if not force:
 
316
            self._check()
 
317
        if self._create_repository:
 
318
            if self.local_branch and not self._destroy_branch:
 
319
                old_repo = self.local_branch.repository
 
320
            elif self._create_branch and self.referenced_branch is not None:
 
321
                old_repo = self.referenced_branch.repository
 
322
            else:
 
323
                old_repo = None
 
324
            if old_repo is not None:
 
325
                repository_format = old_repo._format
 
326
            else:
 
327
                repository_format = None
 
328
            if repository_format is not None:
 
329
                repo = repository_format.initialize(self.bzrdir)
 
330
            else:
 
331
                repo = self.bzrdir.create_repository()
 
332
            if self.local_branch and not self._destroy_branch:
 
333
                repo.fetch(self.local_branch.repository,
 
334
                           self.local_branch.last_revision())
 
335
        else:
 
336
            repo = self.repository
 
337
        if self._create_branch and self.referenced_branch is not None:
 
338
            repo.fetch(self.referenced_branch.repository,
 
339
                       self.referenced_branch.last_revision())
 
340
        if self._create_reference:
 
341
            reference_branch = branch.Branch.open(self._select_bind_location())
 
342
        if self._destroy_repository:
 
343
            if self._create_reference:
 
344
                reference_branch.repository.fetch(self.repository)
 
345
            elif self.local_branch is not None and not self._destroy_branch:
 
346
                up = self.local_branch.user_transport.clone('..')
 
347
                up_bzrdir = bzrdir.BzrDir.open_containing_from_transport(up)[0]
 
348
                new_repo = up_bzrdir.find_repository()
 
349
                new_repo.fetch(self.repository)
 
350
        last_revision_info = None
 
351
        if self._destroy_reference:
 
352
            last_revision_info = self.referenced_branch.last_revision_info()
 
353
            self.bzrdir.destroy_branch()
 
354
        if self._destroy_branch:
 
355
            last_revision_info = self.local_branch.last_revision_info()
 
356
            if self._create_reference:
 
357
                self.local_branch.tags.merge_to(reference_branch.tags)
 
358
            self.bzrdir.destroy_branch()
 
359
        if self._create_branch:
 
360
            local_branch = self.bzrdir.create_branch()
 
361
            if last_revision_info is not None:
 
362
                local_branch.set_last_revision_info(*last_revision_info)
 
363
            if self._destroy_reference:
 
364
                self.referenced_branch.tags.merge_to(local_branch.tags)
 
365
                self.referenced_branch.update_references(local_branch)
 
366
        else:
 
367
            local_branch = self.local_branch
 
368
        if self._create_reference:
 
369
            format = branch.BranchReferenceFormat().initialize(self.bzrdir,
 
370
                target_branch=reference_branch)
 
371
        if self._destroy_tree:
 
372
            self.bzrdir.destroy_workingtree()
 
373
        if self._create_tree:
 
374
            self.bzrdir.create_workingtree()
 
375
        if self._unbind:
 
376
            self.local_branch.unbind()
 
377
        if self._bind:
 
378
            bind_location = self._select_bind_location()
 
379
            local_branch.bind(branch.Branch.open(bind_location))
 
380
        if self._destroy_repository:
 
381
            self.bzrdir.destroy_repository()
 
382
        if self._repository_trees is not None:
 
383
            repo.set_make_working_trees(self._repository_trees)