~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/upgrade.py

  • Committer: Rory Yorke
  • Date: 2010-10-20 14:38:53 UTC
  • mto: This revision was merged to the branch mainline in revision 5519.
  • Revision ID: rory.yorke@gmail.com-20101020143853-9kfd2ldcjfroh8jw
Show missing files in bzr status (bug 134168).

"bzr status" will now show missing files, that is, those added with "bzr
add" and then removed by non bzr means (e.g., rm).

Blackbox tests were added for this case, and tests were also added to
test_delta, since the implementation change is in bzrlib.delta.

Might also affect bug 189709.

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, 2006, 2008, 2009, 2010 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
17
17
"""bzr upgrade logic."""
18
18
 
19
19
 
20
 
from bzrlib import (
21
 
    errors,
22
 
    trace,
23
 
    ui,
24
 
    )
25
 
from bzrlib.bzrdir import (
26
 
    BzrDir,
27
 
    format_registry,
28
 
    )
 
20
from bzrlib.bzrdir import BzrDir, format_registry
 
21
import bzrlib.errors as errors
29
22
from bzrlib.remote import RemoteBzrDir
 
23
import bzrlib.ui as ui
30
24
 
31
25
 
32
26
class Convert(object):
33
27
 
34
 
    def __init__(self, url=None, format=None, control_dir=None):
35
 
        """Convert a Bazaar control directory to a given format.
36
 
 
37
 
        Either the url or control_dir parameter must be given.
38
 
 
39
 
        :param url: the URL of the control directory or None if the
40
 
          control_dir is explicitly given instead
41
 
        :param format: the format to convert to or None for the default
42
 
        :param control_dir: the control directory or None if it is
43
 
          specified via the URL parameter instead
44
 
        """
 
28
    def __init__(self, url, format=None):
45
29
        self.format = format
 
30
        self.bzrdir = BzrDir.open_unsupported(url)
46
31
        # XXX: Change to cleanup
47
32
        warning_id = 'cross_format_fetch'
48
33
        saved_warning = warning_id in ui.ui_factory.suppressed_warnings
49
 
        if url is None and control_dir is None:
50
 
            raise AssertionError(
51
 
                "either the url or control_dir parameter must be set.")
52
 
        if control_dir is not None:
53
 
            self.bzrdir = control_dir
54
 
        else:
55
 
            self.bzrdir = BzrDir.open_unsupported(url)
56
34
        if isinstance(self.bzrdir, RemoteBzrDir):
57
35
            self.bzrdir._ensure_real()
58
36
            self.bzrdir = self.bzrdir._real_bzrdir
70
48
        try:
71
49
            branch = self.bzrdir.open_branch()
72
50
            if branch.user_url != self.bzrdir.user_url:
73
 
                ui.ui_factory.note(
74
 
                    'This is a checkout. The branch (%s) needs to be upgraded'
75
 
                    ' separately.' % (branch.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
54
            del branch
77
55
        except (errors.NotBranchError, errors.IncompatibleRepositories):
78
56
            # might not be a format we can open without upgrading; see e.g.
98
76
        self.bzrdir.check_conversion_target(format)
99
77
        ui.ui_factory.note('starting upgrade of %s' % self.transport.base)
100
78
 
101
 
        self.backup_oldpath, self.backup_newpath = self.bzrdir.backup_bzrdir()
 
79
        self.bzrdir.backup_bzrdir()
102
80
        while self.bzrdir.needs_format_conversion(format):
103
81
            converter = self.bzrdir._format.get_converter(format)
104
82
            self.bzrdir = converter.convert(self.bzrdir, None)
105
 
        ui.ui_factory.note('finished')
106
 
 
107
 
    def clean_up(self):
108
 
        """Clean-up after a conversion.
109
 
 
110
 
        This removes the backup.bzr directory.
111
 
        """
112
 
        transport = self.transport
113
 
        backup_relpath = transport.relpath(self.backup_newpath)
114
 
        child_pb = ui.ui_factory.nested_progress_bar()
115
 
        child_pb.update('Deleting backup.bzr')
116
 
        try:
117
 
            transport.delete_tree(backup_relpath)
118
 
        finally:
119
 
            child_pb.finished()
120
 
 
121
 
 
122
 
def upgrade(url, format=None, clean_up=False, dry_run=False):
123
 
    """Upgrade locations to format.
124
 
 
125
 
    This routine wraps the smart_upgrade() routine with a nicer UI.
126
 
    In particular, it ensures all URLs can be opened before starting
127
 
    and reports a summary at the end if more than one upgrade was attempted.
128
 
    This routine is useful for command line tools. Other bzrlib clients
129
 
    probably ought to use smart_upgrade() instead.
130
 
 
131
 
    :param url: a URL of the locations to upgrade.
132
 
    :param format: the format to convert to or None for the best default
133
 
    :param clean-up: if True, the backup.bzr directory is removed if the
134
 
      upgrade succeeded for a given repo/branch/tree
135
 
    :param dry_run: show what would happen but don't actually do any upgrades
136
 
    :return: the list of exceptions encountered
137
 
    """
138
 
    control_dirs = [BzrDir.open_unsupported(url)]
139
 
    attempted, succeeded, exceptions = smart_upgrade(control_dirs,
140
 
        format, clean_up=clean_up, dry_run=dry_run)
141
 
    if len(attempted) > 1:
142
 
        attempted_count = len(attempted)
143
 
        succeeded_count = len(succeeded)
144
 
        failed_count = attempted_count - succeeded_count
145
 
        ui.ui_factory.note(
146
 
            '\nSUMMARY: %d upgrades attempted, %d succeeded, %d failed'
147
 
            % (attempted_count, succeeded_count, failed_count))
148
 
    return exceptions
149
 
 
150
 
 
151
 
def smart_upgrade(control_dirs, format, clean_up=False,
152
 
    dry_run=False):
153
 
    """Convert control directories to a new format intelligently.
154
 
 
155
 
    If the control directory is a shared repository, dependent branches
156
 
    are also converted provided the repository converted successfully.
157
 
    If the conversion of a branch fails, remaining branches are still tried.
158
 
 
159
 
    :param control_dirs: the BzrDirs to upgrade
160
 
    :param format: the format to convert to or None for the best default
161
 
    :param clean_up: if True, the backup.bzr directory is removed if the
162
 
      upgrade succeeded for a given repo/branch/tree
163
 
    :param dry_run: show what would happen but don't actually do any upgrades
164
 
    :return: attempted-control-dirs, succeeded-control-dirs, exceptions
165
 
    """
166
 
    all_attempted = []
167
 
    all_succeeded = []
168
 
    all_exceptions = []
169
 
    for control_dir in control_dirs:
170
 
        attempted, succeeded, exceptions = _smart_upgrade_one(control_dir,
171
 
            format, clean_up=clean_up, dry_run=dry_run)
172
 
        all_attempted.extend(attempted)
173
 
        all_succeeded.extend(succeeded)
174
 
        all_exceptions.extend(exceptions)
175
 
    return all_attempted, all_succeeded, all_exceptions
176
 
 
177
 
 
178
 
def _smart_upgrade_one(control_dir, format, clean_up=False,
179
 
    dry_run=False):
180
 
    """Convert a control directory to a new format intelligently.
181
 
 
182
 
    See smart_upgrade for parameter details.
183
 
    """
184
 
    # If the URL is a shared repository, find the dependent branches
185
 
    dependents = None
186
 
    try:
187
 
        repo = control_dir.open_repository()
188
 
    except errors.NoRepositoryPresent:
189
 
        # A branch or checkout using a shared repository higher up
190
 
        pass
191
 
    else:
192
 
        # The URL is a repository. If it successfully upgrades,
193
 
        # then upgrade the dependent branches as well.
194
 
        if repo.is_shared():
195
 
            dependents = repo.find_branches(using=True)
196
 
 
197
 
    # Do the conversions
198
 
    attempted = [control_dir]
199
 
    succeeded, exceptions = _convert_items([control_dir], format, clean_up,
200
 
                                           dry_run)
201
 
    if succeeded and dependents:
202
 
        ui.ui_factory.note('Found %d dependent branches - upgrading ...'
203
 
                           % (len(dependents),))
204
 
        # Convert dependent branches
205
 
        branch_cdirs = [b.bzrdir for b in dependents]
206
 
        successes, problems = _convert_items(branch_cdirs, format, clean_up,
207
 
            dry_run, label="branch")
208
 
        attempted.extend(branch_cdirs)
209
 
        succeeded.extend(successes)
210
 
        exceptions.extend(problems)
211
 
 
212
 
    # Return the result
213
 
    return attempted, succeeded, exceptions
214
 
 
215
 
# FIXME: There are several problems below:
216
 
# - RemoteRepository doesn't support _unsupported (really ?)
217
 
# - raising AssertionError is rude and may not be necessary
218
 
# - no tests
219
 
# - the only caller uses only the label
220
 
def _get_object_and_label(control_dir):
221
 
    """Return the primary object and type label for a control directory.
222
 
 
223
 
    :return: object, label where
224
 
      object is a Branch, Repository or WorkingTree and
225
 
      label is one of:
226
 
        branch            - a branch
227
 
        repository        - a repository
228
 
        tree              - a lightweight checkout
229
 
    """
230
 
    try:
231
 
        try:
232
 
            br = control_dir.open_branch(unsupported=True,
233
 
                                         ignore_fallbacks=True)
234
 
        except NotImplementedError:
235
 
            # RemoteRepository doesn't support the unsupported parameter
236
 
            br = control_dir.open_branch(ignore_fallbacks=True)
237
 
    except errors.NotBranchError:
238
 
        pass
239
 
    else:
240
 
        return br, "branch"
241
 
    try:
242
 
        repo = control_dir.open_repository()
243
 
    except errors.NoRepositoryPresent:
244
 
        pass
245
 
    else:
246
 
        return repo, "repository"
247
 
    try:
248
 
        wt = control_dir.open_workingtree()
249
 
    except (errors.NoWorkingTree, errors.NotLocalUrl):
250
 
        pass
251
 
    else:
252
 
        return wt, "tree"
253
 
    raise AssertionError("unknown type of control directory %s", control_dir)
254
 
 
255
 
 
256
 
def _convert_items(items, format, clean_up, dry_run, label=None):
257
 
    """Convert a sequence of control directories to the given format.
258
 
 
259
 
    :param items: the control directories to upgrade
260
 
    :param format: the format to convert to or None for the best default
261
 
    :param clean-up: if True, the backup.bzr directory is removed if the
262
 
      upgrade succeeded for a given repo/branch/tree
263
 
    :param dry_run: show what would happen but don't actually do any upgrades
264
 
    :param label: the label for these items or None to calculate one
265
 
    :return: items successfully upgraded, exceptions
266
 
    """
267
 
    succeeded = []
268
 
    exceptions = []
269
 
    child_pb = ui.ui_factory.nested_progress_bar()
270
 
    child_pb.update('Upgrading bzrdirs', 0, len(items))
271
 
    for i, control_dir in enumerate(items):
272
 
        # Do the conversion
273
 
        location = control_dir.root_transport.base
274
 
        bzr_object, bzr_label = _get_object_and_label(control_dir)
275
 
        type_label = label or bzr_label
276
 
        child_pb.update("Upgrading %s" % (type_label), i+1, len(items))
277
 
        ui.ui_factory.note('Upgrading %s %s ...' % (type_label, location,))
278
 
        try:
279
 
            if not dry_run:
280
 
                cv = Convert(control_dir=control_dir, format=format)
281
 
        except Exception, ex:
282
 
            trace.warning('conversion error: %s' % ex)
283
 
            exceptions.append(ex)
284
 
            continue
285
 
 
286
 
        # Do any required post processing
287
 
        succeeded.append(control_dir)
288
 
        if clean_up:
289
 
            try:
290
 
                ui.ui_factory.note('Removing backup ...')
291
 
                if not dry_run:
292
 
                    cv.clean_up()
293
 
            except Exception, ex:
294
 
                trace.warning('failed to clean-up %s: %s' % (location, ex))
295
 
                exceptions.append(ex)
296
 
 
297
 
    child_pb.finished()
298
 
 
299
 
    # Return the result
300
 
    return succeeded, exceptions
 
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)