~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/reconfigure.py

  • Committer: Robert Collins
  • Date: 2007-11-09 17:50:31 UTC
  • mto: This revision was merged to the branch mainline in revision 2988.
  • Revision ID: robertc@robertcollins.net-20071109175031-agaiy6530rvbprmb
Change (without backwards compatibility) the
iter_lines_added_or_present_in_versions VersionedFile API to yield the
text version that each line is being returned from. This is useful for
reconcile in determining what inventories reference what texts.
(Robert Collins)

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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Reconfigure a bzrdir into a new tree/branch/repository layout"""
 
18
 
 
19
from bzrlib import (
 
20
    branch,
 
21
    errors,
 
22
    )
 
23
 
 
24
class Reconfigure(object):
 
25
 
 
26
    def __init__(self, bzrdir, new_bound_location=None):
 
27
        self.bzrdir = bzrdir
 
28
        self.new_bound_location = new_bound_location
 
29
        try:
 
30
            self.repository = self.bzrdir.find_repository()
 
31
        except errors.NoRepositoryPresent:
 
32
            self.repository = None
 
33
        try:
 
34
            branch = self.bzrdir.open_branch()
 
35
            if branch.bzrdir.root_transport.base == bzrdir.root_transport.base:
 
36
                self.local_branch = branch
 
37
                self.referenced_branch = None
 
38
            else:
 
39
                self.local_branch = None
 
40
                self.referenced_branch = branch
 
41
        except errors.NotBranchError:
 
42
            self.local_branch = None
 
43
            self.referenced_branch = None
 
44
        try:
 
45
            self.tree = bzrdir.open_workingtree()
 
46
        except errors.NoWorkingTree:
 
47
            self.tree = None
 
48
        self._unbind = False
 
49
        self._bind = False
 
50
        self._destroy_reference = False
 
51
        self._create_branch = False
 
52
        self._destroy_tree = False
 
53
        self._create_tree = False
 
54
        self._create_repository = False
 
55
 
 
56
    @staticmethod
 
57
    def to_branch(bzrdir):
 
58
        """Return a Reconfiguration to convert this bzrdir into a branch
 
59
 
 
60
        :param bzrdir: The bzrdir to reconfigure
 
61
        :raise errors.AlreadyBranch: if bzrdir is already a branch
 
62
        :raise errors.ReconfigurationNotSupported: if bzrdir does not contain
 
63
            a branch or branch reference
 
64
        """
 
65
        reconfiguration = Reconfigure(bzrdir)
 
66
        reconfiguration._plan_changes(want_tree=False, want_branch=True,
 
67
                                      want_bound=False)
 
68
        if not reconfiguration.changes_planned():
 
69
            raise errors.AlreadyBranch(bzrdir)
 
70
        return reconfiguration
 
71
 
 
72
    @staticmethod
 
73
    def to_tree(bzrdir):
 
74
        """Return a Reconfiguration to convert this bzrdir into a tree
 
75
 
 
76
        :param bzrdir: The bzrdir to reconfigure
 
77
        :raise errors.AlreadyTree: if bzrdir is already a tree
 
78
        :raise errors.ReconfigurationNotSupported: if bzrdir does not contain
 
79
            a branch or branch reference
 
80
        """
 
81
        reconfiguration = Reconfigure(bzrdir)
 
82
        reconfiguration._plan_changes(want_tree=True, want_branch=True,
 
83
                                      want_bound=False)
 
84
        if not reconfiguration.changes_planned():
 
85
            raise errors.AlreadyTree(bzrdir)
 
86
        return reconfiguration
 
87
 
 
88
    @staticmethod
 
89
    def to_checkout(bzrdir, bound_location=None):
 
90
        """Return a Reconfiguration to convert this bzrdir into a checkout
 
91
 
 
92
        :param bzrdir: The bzrdir to reconfigure
 
93
        :param bound_location: The location the checkout should be bound to.
 
94
        :raise errors.AlreadyCheckout: if bzrdir is already a checkout
 
95
        :raise errors.ReconfigurationNotSupported: if bzrdir does not contain
 
96
            a branch or branch reference
 
97
        """
 
98
        reconfiguration = Reconfigure(bzrdir, bound_location)
 
99
        reconfiguration._plan_changes(want_tree=True, want_branch=True,
 
100
                                      want_bound=True)
 
101
        if not reconfiguration.changes_planned():
 
102
            raise errors.AlreadyCheckout(bzrdir)
 
103
        return reconfiguration
 
104
 
 
105
    def _plan_changes(self, want_tree, want_branch, want_bound):
 
106
        """Determine which changes are needed to assume the configuration"""
 
107
        if self.repository is None:
 
108
            self._create_repository = True
 
109
        if self.local_branch is None:
 
110
            if want_branch is True:
 
111
                if self.referenced_branch is not None:
 
112
                    self._destroy_reference = True
 
113
                    self._create_branch = True
 
114
                    if want_bound:
 
115
                        self._bind = True
 
116
                else:
 
117
                    raise errors.ReconfigurationNotSupported(self.bzrdir)
 
118
        else:
 
119
            if want_bound:
 
120
                if self.local_branch.get_bound_location() is None:
 
121
                    self._bind = True
 
122
            else:
 
123
                if self.local_branch.get_bound_location() is not None:
 
124
                    self._unbind = True
 
125
        if not want_tree and self.tree is not None:
 
126
            self._destroy_tree = True
 
127
        if want_tree and self.tree is None:
 
128
            self._create_tree = True
 
129
 
 
130
    def changes_planned(self):
 
131
        """Return True if changes are planned, False otherwise"""
 
132
        return (self._unbind or self._bind or self._destroy_tree
 
133
                or self._create_tree or self._destroy_reference
 
134
                or self._create_branch or self._create_repository)
 
135
 
 
136
    def _check(self):
 
137
        """Raise if reconfiguration would destroy local changes"""
 
138
        if self._destroy_tree:
 
139
            changes = self.tree.changes_from(self.tree.basis_tree())
 
140
            if changes.has_changed():
 
141
                raise errors.UncommittedChanges(self.tree)
 
142
 
 
143
    def _select_bind_location(self):
 
144
        """Select a location to bind to.
 
145
 
 
146
        Preference is:
 
147
        1. user specified location
 
148
        2. branch reference location (it's a kind of bind location)
 
149
        3. previous bind location (it was a good choice once)
 
150
        4. push location (it's writeable, so committable)
 
151
        5. parent location (it's pullable, so update-from-able)
 
152
        """
 
153
        if self.new_bound_location is not None:
 
154
            return self.new_bound_location
 
155
        if self.local_branch is not None:
 
156
            old_bound = self.local_branch.get_old_bound_location()
 
157
            if old_bound is not None:
 
158
                return old_bound
 
159
            push_location = self.local_branch.get_push_location()
 
160
            if push_location is not None:
 
161
                return push_location
 
162
            parent = self.local_branch.get_parent()
 
163
            if parent is not None:
 
164
                return parent
 
165
        elif self.referenced_branch is not None:
 
166
            return self.referenced_branch.base
 
167
        raise errors.NoBindLocation(self.bzrdir)
 
168
 
 
169
    def apply(self, force=False):
 
170
        """Apply the reconfiguration
 
171
 
 
172
        :param force: If true, the reconfiguration is applied even if it will
 
173
            destroy local changes.
 
174
        :raise errors.UncommittedChanges: if the local tree is to be destroyed
 
175
            but contains uncommitted changes.
 
176
        :raise errors.NoBindLocation: if no bind location was specified and
 
177
            none could be autodetected.
 
178
        """
 
179
        if not force:
 
180
            self._check()
 
181
        if self._create_repository:
 
182
            repo = self.bzrdir.create_repository()
 
183
        else:
 
184
            repo = self.repository
 
185
        if self._create_branch:
 
186
            repo.fetch(self.referenced_branch.repository,
 
187
                       self.referenced_branch.last_revision())
 
188
        if self._destroy_reference:
 
189
            reference_info = self.referenced_branch.last_revision_info()
 
190
            self.bzrdir.destroy_branch()
 
191
        if self._create_branch:
 
192
            local_branch = self.bzrdir.create_branch()
 
193
            local_branch.set_last_revision_info(*reference_info)
 
194
        else:
 
195
            local_branch = self.local_branch
 
196
        if self._destroy_tree:
 
197
            self.bzrdir.destroy_workingtree()
 
198
        if self._create_tree:
 
199
            self.bzrdir.create_workingtree()
 
200
        if self._unbind:
 
201
            self.local_branch.unbind()
 
202
        if self._bind:
 
203
            bind_location = self._select_bind_location()
 
204
            local_branch.bind(branch.Branch.open(bind_location))