/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/upgrade.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2008-2011 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
16
16
 
17
17
"""bzr upgrade logic."""
18
18
 
 
19
from __future__ import absolute_import
19
20
 
20
 
from bzrlib.bzrdir import BzrDir, format_registry
21
 
import bzrlib.errors as errors
 
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
22
32
from bzrlib.remote import RemoteBzrDir
23
 
import bzrlib.ui as ui
24
33
 
25
34
 
26
35
class Convert(object):
27
36
 
28
 
    def __init__(self, url, format=None):
 
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
        """
29
48
        self.format = format
30
 
        self.bzrdir = BzrDir.open_unsupported(url)
31
49
        # XXX: Change to cleanup
32
50
        warning_id = 'cross_format_fetch'
33
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)
34
59
        if isinstance(self.bzrdir, RemoteBzrDir):
35
60
            self.bzrdir._ensure_real()
36
61
            self.bzrdir = self.bzrdir._real_bzrdir
48
73
        try:
49
74
            branch = self.bzrdir.open_branch()
50
75
            if branch.user_url != self.bzrdir.user_url:
51
 
                ui.ui_factory.note("This is a checkout. The branch (%s) needs to be "
52
 
                             "upgraded separately." %
53
 
                             branch.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')))
54
80
            del branch
55
81
        except (errors.NotBranchError, errors.IncompatibleRepositories):
56
82
            # might not be a format we can open without upgrading; see e.g.
71
97
        if not self.bzrdir.needs_format_conversion(format):
72
98
            raise errors.UpToDateFormat(self.bzrdir._format)
73
99
        if not self.bzrdir.can_convert_format():
74
 
            raise errors.BzrError("cannot upgrade from bzrdir format %s" %
 
100
            raise errors.BzrError(gettext("cannot upgrade from bzrdir format %s") %
75
101
                           self.bzrdir._format)
76
102
        self.bzrdir.check_conversion_target(format)
77
 
        ui.ui_factory.note('starting upgrade of %s' % self.transport.base)
 
103
        ui.ui_factory.note(gettext('starting upgrade of %s') % 
 
104
            urlutils.unescape_for_display(self.transport.base, 'utf-8'))
78
105
 
79
 
        self.bzrdir.backup_bzrdir()
 
106
        self.backup_oldpath, self.backup_newpath = self.bzrdir.backup_bzrdir()
80
107
        while self.bzrdir.needs_format_conversion(format):
81
108
            converter = self.bzrdir._format.get_converter(format)
82
109
            self.bzrdir = converter.convert(self.bzrdir, None)
83
 
        ui.ui_factory.note("finished")
84
 
 
85
 
 
86
 
def upgrade(url, format=None):
87
 
    """Upgrade to format, or the default bzrdir format if not supplied."""
88
 
    Convert(url, format)
 
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