/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: Canonical.com Patch Queue Manager
  • Date: 2009-07-20 08:56:45 UTC
  • mfrom: (4526.9.23 apply-inventory-delta)
  • Revision ID: pqm@pqm.ubuntu.com-20090720085645-54mtgybxua0yx6hw
(robertc) Add checks for inventory deltas which try to ensure that
        deltas that are not an exact fit are not applied. (Robert
        Collins, bug 397705, bug 367633)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2007 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
from bzrlib import (
 
20
    branch,
 
21
    bzrdir,
 
22
    errors,
 
23
    )
 
24
 
 
25
class Reconfigure(object):
 
26
 
 
27
    def __init__(self, bzrdir, new_bound_location=None):
 
28
        self.bzrdir = bzrdir
 
29
        self.new_bound_location = new_bound_location
 
30
        self.local_repository = None
 
31
        try:
 
32
            self.repository = self.bzrdir.find_repository()
 
33
        except errors.NoRepositoryPresent:
 
34
            self.repository = None
 
35
            self.local_repository = None
 
36
        else:
 
37
            if (self.repository.bzrdir.root_transport.base ==
 
38
                self.bzrdir.root_transport.base):
 
39
                self.local_repository = self.repository
 
40
            else:
 
41
                self.local_repository = None
 
42
        try:
 
43
            branch = self.bzrdir.open_branch()
 
44
            if branch.bzrdir.root_transport.base == bzrdir.root_transport.base:
 
45
                self.local_branch = branch
 
46
                self.referenced_branch = None
 
47
            else:
 
48
                self.local_branch = None
 
49
                self.referenced_branch = branch
 
50
        except errors.NotBranchError:
 
51
            self.local_branch = None
 
52
            self.referenced_branch = None
 
53
        try:
 
54
            self.tree = bzrdir.open_workingtree()
 
55
        except errors.NoWorkingTree:
 
56
            self.tree = None
 
57
        self._unbind = False
 
58
        self._bind = False
 
59
        self._destroy_reference = False
 
60
        self._create_reference = False
 
61
        self._destroy_branch = False
 
62
        self._create_branch = False
 
63
        self._destroy_tree = False
 
64
        self._create_tree = False
 
65
        self._create_repository = False
 
66
        self._destroy_repository = False
 
67
        self._repository_trees = None
 
68
 
 
69
    @staticmethod
 
70
    def to_branch(bzrdir):
 
71
        """Return a Reconfiguration to convert this bzrdir into a branch
 
72
 
 
73
        :param bzrdir: The bzrdir to reconfigure
 
74
        :raise errors.AlreadyBranch: if bzrdir is already a branch
 
75
        """
 
76
        reconfiguration = Reconfigure(bzrdir)
 
77
        reconfiguration._plan_changes(want_tree=False, want_branch=True,
 
78
                                      want_bound=False, want_reference=False)
 
79
        if not reconfiguration.changes_planned():
 
80
            raise errors.AlreadyBranch(bzrdir)
 
81
        return reconfiguration
 
82
 
 
83
    @staticmethod
 
84
    def to_tree(bzrdir):
 
85
        """Return a Reconfiguration to convert this bzrdir into a tree
 
86
 
 
87
        :param bzrdir: The bzrdir to reconfigure
 
88
        :raise errors.AlreadyTree: if bzrdir is already a tree
 
89
        """
 
90
        reconfiguration = Reconfigure(bzrdir)
 
91
        reconfiguration._plan_changes(want_tree=True, want_branch=True,
 
92
                                      want_bound=False, want_reference=False)
 
93
        if not reconfiguration.changes_planned():
 
94
            raise errors.AlreadyTree(bzrdir)
 
95
        return reconfiguration
 
96
 
 
97
    @staticmethod
 
98
    def to_checkout(bzrdir, bound_location=None):
 
99
        """Return a Reconfiguration to convert this bzrdir into a checkout
 
100
 
 
101
        :param bzrdir: The bzrdir to reconfigure
 
102
        :param bound_location: The location the checkout should be bound to.
 
103
        :raise errors.AlreadyCheckout: if bzrdir is already a checkout
 
104
        """
 
105
        reconfiguration = Reconfigure(bzrdir, bound_location)
 
106
        reconfiguration._plan_changes(want_tree=True, want_branch=True,
 
107
                                      want_bound=True, want_reference=False)
 
108
        if not reconfiguration.changes_planned():
 
109
            raise errors.AlreadyCheckout(bzrdir)
 
110
        return reconfiguration
 
111
 
 
112
    @classmethod
 
113
    def to_lightweight_checkout(klass, bzrdir, reference_location=None):
 
114
        """Make a Reconfiguration to convert bzrdir into a lightweight checkout
 
115
 
 
116
        :param bzrdir: The bzrdir to reconfigure
 
117
        :param bound_location: The location the checkout should be bound to.
 
118
        :raise errors.AlreadyLightweightCheckout: if bzrdir is already a
 
119
            lightweight checkout
 
120
        """
 
121
        reconfiguration = klass(bzrdir, reference_location)
 
122
        reconfiguration._plan_changes(want_tree=True, want_branch=False,
 
123
                                      want_bound=False, want_reference=True)
 
124
        if not reconfiguration.changes_planned():
 
125
            raise errors.AlreadyLightweightCheckout(bzrdir)
 
126
        return reconfiguration
 
127
 
 
128
    @classmethod
 
129
    def to_use_shared(klass, bzrdir):
 
130
        """Convert a standalone branch into a repository branch"""
 
131
        reconfiguration = klass(bzrdir)
 
132
        reconfiguration._set_use_shared(use_shared=True)
 
133
        if not reconfiguration.changes_planned():
 
134
            raise errors.AlreadyUsingShared(bzrdir)
 
135
        return reconfiguration
 
136
 
 
137
    @classmethod
 
138
    def to_standalone(klass, bzrdir):
 
139
        """Convert a repository branch into a standalone branch"""
 
140
        reconfiguration = klass(bzrdir)
 
141
        reconfiguration._set_use_shared(use_shared=False)
 
142
        if not reconfiguration.changes_planned():
 
143
            raise errors.AlreadyStandalone(bzrdir)
 
144
        return reconfiguration
 
145
 
 
146
    @classmethod
 
147
    def set_repository_trees(klass, bzrdir, with_trees):
 
148
        """Adjust a repository's working tree presence default"""
 
149
        reconfiguration = klass(bzrdir)
 
150
        if not reconfiguration.repository.is_shared():
 
151
            raise errors.ReconfigurationNotSupported(reconfiguration.bzrdir)
 
152
        if with_trees and reconfiguration.repository.make_working_trees():
 
153
            raise errors.AlreadyWithTrees(bzrdir)
 
154
        elif (not with_trees
 
155
              and not reconfiguration.repository.make_working_trees()):
 
156
            raise errors.AlreadyWithNoTrees(bzrdir)
 
157
        else:
 
158
            reconfiguration._repository_trees = with_trees
 
159
        return reconfiguration
 
160
 
 
161
    def _plan_changes(self, want_tree, want_branch, want_bound,
 
162
                      want_reference):
 
163
        """Determine which changes are needed to assume the configuration"""
 
164
        if not want_branch and not want_reference:
 
165
            raise errors.ReconfigurationNotSupported(self.bzrdir)
 
166
        if want_branch and want_reference:
 
167
            raise errors.ReconfigurationNotSupported(self.bzrdir)
 
168
        if self.repository is None:
 
169
            if not want_reference:
 
170
                self._create_repository = True
 
171
        else:
 
172
            if want_reference and (self.repository.bzrdir.root_transport.base
 
173
                                   == self.bzrdir.root_transport.base):
 
174
                if not self.repository.is_shared():
 
175
                    self._destroy_repository = True
 
176
        if self.referenced_branch is None:
 
177
            if want_reference:
 
178
                self._create_reference = True
 
179
                if self.local_branch is not None:
 
180
                    self._destroy_branch = True
 
181
        else:
 
182
            if not want_reference:
 
183
                self._destroy_reference = True
 
184
        if self.local_branch is None:
 
185
            if want_branch is True:
 
186
                self._create_branch = True
 
187
                if want_bound:
 
188
                    self._bind = True
 
189
        else:
 
190
            if want_bound:
 
191
                if self.local_branch.get_bound_location() is None:
 
192
                    self._bind = True
 
193
            else:
 
194
                if self.local_branch.get_bound_location() is not None:
 
195
                    self._unbind = True
 
196
        if not want_tree and self.tree is not None:
 
197
            self._destroy_tree = True
 
198
        if want_tree and self.tree is None:
 
199
            self._create_tree = True
 
200
 
 
201
    def _set_use_shared(self, use_shared=None):
 
202
        if use_shared is None:
 
203
            return
 
204
        if use_shared:
 
205
            if self.local_repository is not None:
 
206
                self._destroy_repository = True
 
207
        else:
 
208
            if self.local_repository is None:
 
209
                self._create_repository = True
 
210
 
 
211
    def changes_planned(self):
 
212
        """Return True if changes are planned, False otherwise"""
 
213
        return (self._unbind or self._bind or self._destroy_tree
 
214
                or self._create_tree or self._destroy_reference
 
215
                or self._create_branch or self._create_repository
 
216
                or self._create_reference or self._destroy_repository)
 
217
 
 
218
    def _check(self):
 
219
        """Raise if reconfiguration would destroy local changes"""
 
220
        if self._destroy_tree:
 
221
            # XXX: What about pending merges ? -- vila 20090629
 
222
            if self.tree.has_changes(self.tree.basis_tree()):
 
223
                raise errors.UncommittedChanges(self.tree)
 
224
        if self._create_reference and self.local_branch is not None:
 
225
            reference_branch = branch.Branch.open(self._select_bind_location())
 
226
            if (reference_branch.last_revision() !=
 
227
                self.local_branch.last_revision()):
 
228
                raise errors.UnsyncedBranches(self.bzrdir, reference_branch)
 
229
 
 
230
    def _select_bind_location(self):
 
231
        """Select a location to bind or create a reference to.
 
232
 
 
233
        Preference is:
 
234
        1. user specified location
 
235
        2. branch reference location (it's a kind of bind location)
 
236
        3. current bind location
 
237
        4. previous bind location (it was a good choice once)
 
238
        5. push location (it's writeable, so committable)
 
239
        6. parent location (it's pullable, so update-from-able)
 
240
        """
 
241
        if self.new_bound_location is not None:
 
242
            return self.new_bound_location
 
243
        if self.local_branch is not None:
 
244
            bound = self.local_branch.get_bound_location()
 
245
            if bound is not None:
 
246
                return bound
 
247
            old_bound = self.local_branch.get_old_bound_location()
 
248
            if old_bound is not None:
 
249
                return old_bound
 
250
            push_location = self.local_branch.get_push_location()
 
251
            if push_location is not None:
 
252
                return push_location
 
253
            parent = self.local_branch.get_parent()
 
254
            if parent is not None:
 
255
                return parent
 
256
        elif self.referenced_branch is not None:
 
257
            return self.referenced_branch.base
 
258
        raise errors.NoBindLocation(self.bzrdir)
 
259
 
 
260
    def apply(self, force=False):
 
261
        """Apply the reconfiguration
 
262
 
 
263
        :param force: If true, the reconfiguration is applied even if it will
 
264
            destroy local changes.
 
265
        :raise errors.UncommittedChanges: if the local tree is to be destroyed
 
266
            but contains uncommitted changes.
 
267
        :raise errors.NoBindLocation: if no bind location was specified and
 
268
            none could be autodetected.
 
269
        """
 
270
        if not force:
 
271
            self._check()
 
272
        if self._create_repository:
 
273
            if self.local_branch and not self._destroy_branch:
 
274
                old_repo = self.local_branch.repository
 
275
            elif self._create_branch and self.referenced_branch is not None:
 
276
                old_repo = self.referenced_branch.repository
 
277
            else:
 
278
                old_repo = None
 
279
            if old_repo is not None:
 
280
                repository_format = old_repo._format
 
281
            else:
 
282
                repository_format = None
 
283
            if repository_format is not None:
 
284
                repo = repository_format.initialize(self.bzrdir)
 
285
            else:
 
286
                repo = self.bzrdir.create_repository()
 
287
            if self.local_branch and not self._destroy_branch:
 
288
                repo.fetch(self.local_branch.repository,
 
289
                           self.local_branch.last_revision())
 
290
        else:
 
291
            repo = self.repository
 
292
        if self._create_branch and self.referenced_branch is not None:
 
293
            repo.fetch(self.referenced_branch.repository,
 
294
                       self.referenced_branch.last_revision())
 
295
        if self._create_reference:
 
296
            reference_branch = branch.Branch.open(self._select_bind_location())
 
297
        if self._destroy_repository:
 
298
            if self._create_reference:
 
299
                reference_branch.repository.fetch(self.repository)
 
300
            elif self.local_branch is not None and not self._destroy_branch:
 
301
                up = self.local_branch.bzrdir.root_transport.clone('..')
 
302
                up_bzrdir = bzrdir.BzrDir.open_containing_from_transport(up)[0]
 
303
                new_repo = up_bzrdir.find_repository()
 
304
                new_repo.fetch(self.repository)
 
305
        last_revision_info = None
 
306
        if self._destroy_reference:
 
307
            last_revision_info = self.referenced_branch.last_revision_info()
 
308
            self.bzrdir.destroy_branch()
 
309
        if self._destroy_branch:
 
310
            last_revision_info = self.local_branch.last_revision_info()
 
311
            if self._create_reference:
 
312
                self.local_branch.tags.merge_to(reference_branch.tags)
 
313
            self.bzrdir.destroy_branch()
 
314
        if self._create_branch:
 
315
            local_branch = self.bzrdir.create_branch()
 
316
            if last_revision_info is not None:
 
317
                local_branch.set_last_revision_info(*last_revision_info)
 
318
            if self._destroy_reference:
 
319
                self.referenced_branch.tags.merge_to(local_branch.tags)
 
320
                self.referenced_branch.update_references(local_branch)
 
321
        else:
 
322
            local_branch = self.local_branch
 
323
        if self._create_reference:
 
324
            format = branch.BranchReferenceFormat().initialize(self.bzrdir,
 
325
                reference_branch)
 
326
        if self._destroy_tree:
 
327
            self.bzrdir.destroy_workingtree()
 
328
        if self._create_tree:
 
329
            self.bzrdir.create_workingtree()
 
330
        if self._unbind:
 
331
            self.local_branch.unbind()
 
332
        if self._bind:
 
333
            bind_location = self._select_bind_location()
 
334
            local_branch.bind(branch.Branch.open(bind_location))
 
335
        if self._destroy_repository:
 
336
            self.bzrdir.destroy_repository()
 
337
        if self._repository_trees is not None:
 
338
            repo.set_make_working_trees(self._repository_trees)