~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/upgrade.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-09-03 22:30:56 UTC
  • mfrom: (3644.2.13 index_builder_cleanup)
  • Revision ID: pqm@pqm.ubuntu.com-20080903223056-b108iytb38xkznci
(jam) Streamline BTreeBuilder.add_node et al to make btree creation
        faster.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""bzr upgrade logic."""
18
18
 
19
 
from __future__ import absolute_import
20
 
 
21
 
from bzrlib import (
22
 
    errors,
23
 
    trace,
24
 
    ui,
25
 
    urlutils,
26
 
    )
27
 
from bzrlib.controldir import (
28
 
    ControlDir,
29
 
    format_registry,
30
 
    )
31
 
from bzrlib.i18n import gettext
 
19
# change upgrade from .bzr to create a '.bzr-new', then do a bait and switch.
 
20
 
 
21
 
 
22
from bzrlib.bzrdir import ConvertBzrDir4To5, ConvertBzrDir5To6, BzrDir, BzrDirFormat4, BzrDirFormat5, BzrDirFormat
 
23
import bzrlib.errors as errors
32
24
from bzrlib.remote import RemoteBzrDir
 
25
from bzrlib.transport import get_transport
 
26
import bzrlib.ui as ui
33
27
 
34
28
 
35
29
class Convert(object):
36
30
 
37
 
    def __init__(self, url=None, format=None, control_dir=None):
38
 
        """Convert a Bazaar control directory to a given format.
39
 
 
40
 
        Either the url or control_dir parameter must be given.
41
 
 
42
 
        :param url: the URL of the control directory or None if the
43
 
          control_dir is explicitly given instead
44
 
        :param format: the format to convert to or None for the default
45
 
        :param control_dir: the control directory or None if it is
46
 
          specified via the URL parameter instead
47
 
        """
 
31
    def __init__(self, url, format):
48
32
        self.format = format
49
 
        # XXX: Change to cleanup
50
 
        warning_id = 'cross_format_fetch'
51
 
        saved_warning = warning_id in ui.ui_factory.suppressed_warnings
52
 
        if url is None and control_dir is None:
53
 
            raise AssertionError(
54
 
                "either the url or control_dir parameter must be set.")
55
 
        if control_dir is not None:
56
 
            self.bzrdir = control_dir
57
 
        else:
58
 
            self.bzrdir = ControlDir.open_unsupported(url)
 
33
        self.bzrdir = BzrDir.open_unsupported(url)
59
34
        if isinstance(self.bzrdir, RemoteBzrDir):
60
35
            self.bzrdir._ensure_real()
61
36
            self.bzrdir = self.bzrdir._real_bzrdir
62
37
        if self.bzrdir.root_transport.is_readonly():
63
38
            raise errors.UpgradeReadonly
64
39
        self.transport = self.bzrdir.root_transport
65
 
        ui.ui_factory.suppressed_warnings.add(warning_id)
 
40
        self.pb = ui.ui_factory.nested_progress_bar()
66
41
        try:
67
42
            self.convert()
68
43
        finally:
69
 
            if not saved_warning:
70
 
                ui.ui_factory.suppressed_warnings.remove(warning_id)
 
44
            self.pb.finished()
71
45
 
72
46
    def convert(self):
73
47
        try:
74
48
            branch = self.bzrdir.open_branch()
75
 
            if branch.user_url != self.bzrdir.user_url:
76
 
                ui.ui_factory.note(gettext(
77
 
                    'This is a checkout. The branch (%s) needs to be upgraded'
78
 
                    ' separately.') % (urlutils.unescape_for_display(
79
 
                        branch.user_url, 'utf-8')))
 
49
            if branch.bzrdir.root_transport.base != \
 
50
                self.bzrdir.root_transport.base:
 
51
                self.pb.note("This is a checkout. The branch (%s) needs to be "
 
52
                             "upgraded separately.",
 
53
                             branch.bzrdir.root_transport.base)
80
54
            del branch
81
55
        except (errors.NotBranchError, errors.IncompatibleRepositories):
82
 
            # might not be a format we can open without upgrading; see e.g.
 
56
            # might not be a format we can open without upgrading; see e.g. 
83
57
            # https://bugs.launchpad.net/bzr/+bug/253891
84
58
            pass
85
 
        if self.format is None:
86
 
            try:
87
 
                rich_root = self.bzrdir.find_repository()._format.rich_root_data
88
 
            except errors.NoRepositoryPresent:
89
 
                rich_root = False # assume no rich roots
90
 
            if rich_root:
91
 
                format_name = "default-rich-root"
92
 
            else:
93
 
                format_name = "default"
94
 
            format = format_registry.make_bzrdir(format_name)
95
 
        else:
96
 
            format = self.format
97
 
        if not self.bzrdir.needs_format_conversion(format):
 
59
        if not self.bzrdir.needs_format_conversion(self.format):
98
60
            raise errors.UpToDateFormat(self.bzrdir._format)
99
61
        if not self.bzrdir.can_convert_format():
100
 
            raise errors.BzrError(gettext("cannot upgrade from bzrdir format %s") %
 
62
            raise errors.BzrError("cannot upgrade from bzrdir format %s" %
101
63
                           self.bzrdir._format)
102
 
        self.bzrdir.check_conversion_target(format)
103
 
        ui.ui_factory.note(gettext('starting upgrade of %s') % 
104
 
            urlutils.unescape_for_display(self.transport.base, 'utf-8'))
105
 
 
106
 
        self.backup_oldpath, self.backup_newpath = self.bzrdir.backup_bzrdir()
107
 
        while self.bzrdir.needs_format_conversion(format):
108
 
            converter = self.bzrdir._format.get_converter(format)
109
 
            self.bzrdir = converter.convert(self.bzrdir, None)
110
 
        ui.ui_factory.note(gettext('finished'))
111
 
 
112
 
    def clean_up(self):
113
 
        """Clean-up after a conversion.
114
 
 
115
 
        This removes the backup.bzr directory.
116
 
        """
117
 
        transport = self.transport
118
 
        backup_relpath = transport.relpath(self.backup_newpath)
119
 
        child_pb = ui.ui_factory.nested_progress_bar()
120
 
        child_pb.update(gettext('Deleting backup.bzr'))
121
 
        try:
122
 
            transport.delete_tree(backup_relpath)
123
 
        finally:
124
 
            child_pb.finished()
125
 
 
126
 
 
127
 
def upgrade(url, format=None, clean_up=False, dry_run=False):
128
 
    """Upgrade locations to format.
129
 
 
130
 
    This routine wraps the smart_upgrade() routine with a nicer UI.
131
 
    In particular, it ensures all URLs can be opened before starting
132
 
    and reports a summary at the end if more than one upgrade was attempted.
133
 
    This routine is useful for command line tools. Other bzrlib clients
134
 
    probably ought to use smart_upgrade() instead.
135
 
 
136
 
    :param url: a URL of the locations to upgrade.
137
 
    :param format: the format to convert to or None for the best default
138
 
    :param clean-up: if True, the backup.bzr directory is removed if the
139
 
      upgrade succeeded for a given repo/branch/tree
140
 
    :param dry_run: show what would happen but don't actually do any upgrades
141
 
    :return: the list of exceptions encountered
142
 
    """
143
 
    control_dirs = [ControlDir.open_unsupported(url)]
144
 
    attempted, succeeded, exceptions = smart_upgrade(control_dirs,
145
 
        format, clean_up=clean_up, dry_run=dry_run)
146
 
    if len(attempted) > 1:
147
 
        attempted_count = len(attempted)
148
 
        succeeded_count = len(succeeded)
149
 
        failed_count = attempted_count - succeeded_count
150
 
        ui.ui_factory.note(
151
 
            gettext('\nSUMMARY: {0} upgrades attempted, {1} succeeded,'\
152
 
                    ' {2} failed').format(
153
 
                     attempted_count, succeeded_count, failed_count))
154
 
    return exceptions
155
 
 
156
 
 
157
 
def smart_upgrade(control_dirs, format, clean_up=False,
158
 
    dry_run=False):
159
 
    """Convert control directories to a new format intelligently.
160
 
 
161
 
    If the control directory is a shared repository, dependent branches
162
 
    are also converted provided the repository converted successfully.
163
 
    If the conversion of a branch fails, remaining branches are still tried.
164
 
 
165
 
    :param control_dirs: the BzrDirs to upgrade
166
 
    :param format: the format to convert to or None for the best default
167
 
    :param clean_up: if True, the backup.bzr directory is removed if the
168
 
      upgrade succeeded for a given repo/branch/tree
169
 
    :param dry_run: show what would happen but don't actually do any upgrades
170
 
    :return: attempted-control-dirs, succeeded-control-dirs, exceptions
171
 
    """
172
 
    all_attempted = []
173
 
    all_succeeded = []
174
 
    all_exceptions = []
175
 
    for control_dir in control_dirs:
176
 
        attempted, succeeded, exceptions = _smart_upgrade_one(control_dir,
177
 
            format, clean_up=clean_up, dry_run=dry_run)
178
 
        all_attempted.extend(attempted)
179
 
        all_succeeded.extend(succeeded)
180
 
        all_exceptions.extend(exceptions)
181
 
    return all_attempted, all_succeeded, all_exceptions
182
 
 
183
 
 
184
 
def _smart_upgrade_one(control_dir, format, clean_up=False,
185
 
    dry_run=False):
186
 
    """Convert a control directory to a new format intelligently.
187
 
 
188
 
    See smart_upgrade for parameter details.
189
 
    """
190
 
    # If the URL is a shared repository, find the dependent branches
191
 
    dependents = None
192
 
    try:
193
 
        repo = control_dir.open_repository()
194
 
    except errors.NoRepositoryPresent:
195
 
        # A branch or checkout using a shared repository higher up
196
 
        pass
197
 
    else:
198
 
        # The URL is a repository. If it successfully upgrades,
199
 
        # then upgrade the dependent branches as well.
200
 
        if repo.is_shared():
201
 
            dependents = repo.find_branches(using=True)
202
 
 
203
 
    # Do the conversions
204
 
    attempted = [control_dir]
205
 
    succeeded, exceptions = _convert_items([control_dir], format, clean_up,
206
 
                                           dry_run)
207
 
    if succeeded and dependents:
208
 
        ui.ui_factory.note(gettext('Found %d dependent branches - upgrading ...')
209
 
                           % (len(dependents),))
210
 
        # Convert dependent branches
211
 
        branch_cdirs = [b.bzrdir for b in dependents]
212
 
        successes, problems = _convert_items(branch_cdirs, format, clean_up,
213
 
            dry_run, label="branch")
214
 
        attempted.extend(branch_cdirs)
215
 
        succeeded.extend(successes)
216
 
        exceptions.extend(problems)
217
 
 
218
 
    # Return the result
219
 
    return attempted, succeeded, exceptions
220
 
 
221
 
# FIXME: There are several problems below:
222
 
# - RemoteRepository doesn't support _unsupported (really ?)
223
 
# - raising AssertionError is rude and may not be necessary
224
 
# - no tests
225
 
# - the only caller uses only the label
226
 
def _get_object_and_label(control_dir):
227
 
    """Return the primary object and type label for a control directory.
228
 
 
229
 
    :return: object, label where:
230
 
      * object is a Branch, Repository or WorkingTree and
231
 
      * label is one of:
232
 
        * branch            - a branch
233
 
        * repository        - a repository
234
 
        * tree              - a lightweight checkout
235
 
    """
236
 
    try:
237
 
        try:
238
 
            br = control_dir.open_branch(unsupported=True,
239
 
                                         ignore_fallbacks=True)
240
 
        except NotImplementedError:
241
 
            # RemoteRepository doesn't support the unsupported parameter
242
 
            br = control_dir.open_branch(ignore_fallbacks=True)
243
 
    except errors.NotBranchError:
244
 
        pass
245
 
    else:
246
 
        return br, "branch"
247
 
    try:
248
 
        repo = control_dir.open_repository()
249
 
    except errors.NoRepositoryPresent:
250
 
        pass
251
 
    else:
252
 
        return repo, "repository"
253
 
    try:
254
 
        wt = control_dir.open_workingtree()
255
 
    except (errors.NoWorkingTree, errors.NotLocalUrl):
256
 
        pass
257
 
    else:
258
 
        return wt, "tree"
259
 
    raise AssertionError("unknown type of control directory %s", control_dir)
260
 
 
261
 
 
262
 
def _convert_items(items, format, clean_up, dry_run, label=None):
263
 
    """Convert a sequence of control directories to the given format.
264
 
 
265
 
    :param items: the control directories to upgrade
266
 
    :param format: the format to convert to or None for the best default
267
 
    :param clean-up: if True, the backup.bzr directory is removed if the
268
 
      upgrade succeeded for a given repo/branch/tree
269
 
    :param dry_run: show what would happen but don't actually do any upgrades
270
 
    :param label: the label for these items or None to calculate one
271
 
    :return: items successfully upgraded, exceptions
272
 
    """
273
 
    succeeded = []
274
 
    exceptions = []
275
 
    child_pb = ui.ui_factory.nested_progress_bar()
276
 
    child_pb.update(gettext('Upgrading bzrdirs'), 0, len(items))
277
 
    for i, control_dir in enumerate(items):
278
 
        # Do the conversion
279
 
        location = control_dir.root_transport.base
280
 
        bzr_object, bzr_label = _get_object_and_label(control_dir)
281
 
        type_label = label or bzr_label
282
 
        child_pb.update(gettext("Upgrading %s") % (type_label), i+1, len(items))
283
 
        ui.ui_factory.note(gettext('Upgrading {0} {1} ...').format(type_label, 
284
 
            urlutils.unescape_for_display(location, 'utf-8'),))
285
 
        try:
286
 
            if not dry_run:
287
 
                cv = Convert(control_dir=control_dir, format=format)
288
 
        except errors.UpToDateFormat, ex:
289
 
            ui.ui_factory.note(str(ex))
290
 
            succeeded.append(control_dir)
291
 
            continue
292
 
        except Exception, ex:
293
 
            trace.warning('conversion error: %s' % ex)
294
 
            exceptions.append(ex)
295
 
            continue
296
 
 
297
 
        # Do any required post processing
298
 
        succeeded.append(control_dir)
299
 
        if clean_up:
300
 
            try:
301
 
                ui.ui_factory.note(gettext('Removing backup ...'))
302
 
                if not dry_run:
303
 
                    cv.clean_up()
304
 
            except Exception, ex:
305
 
                trace.warning(gettext('failed to clean-up {0}: {1}') % (location, ex))
306
 
                exceptions.append(ex)
307
 
 
308
 
    child_pb.finished()
309
 
 
310
 
    # Return the result
311
 
    return succeeded, exceptions
 
64
        if self.format is None:
 
65
            target_format = BzrDirFormat.get_default_format()
 
66
        else:
 
67
            target_format = self.format
 
68
        self.bzrdir.check_conversion_target(target_format)
 
69
        self.pb.note('starting upgrade of %s', self.transport.base)
 
70
        self._backup_control_dir()
 
71
        while self.bzrdir.needs_format_conversion(self.format):
 
72
            converter = self.bzrdir._format.get_converter(self.format)
 
73
            self.bzrdir = converter.convert(self.bzrdir, self.pb)
 
74
        self.pb.note("finished")
 
75
 
 
76
    def _backup_control_dir(self):
 
77
        self.pb.note('making backup of tree history')
 
78
        self.transport.copy_tree('.bzr', 'backup.bzr')
 
79
        self.pb.note('%s.bzr has been backed up to %sbackup.bzr',
 
80
             self.transport.base,
 
81
             self.transport.base)
 
82
        self.pb.note('if conversion fails, you can move this directory back to .bzr')
 
83
        self.pb.note('if it succeeds, you can remove this directory if you wish')
 
84
 
 
85
def upgrade(url, format=None):
 
86
    """Upgrade to format, or the default bzrdir format if not supplied."""
 
87
    Convert(url, format)