~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/shelf_ui.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:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
17
18
 
18
19
from cStringIO import StringIO
19
20
import shutil
34
35
    ui,
35
36
    workingtree,
36
37
)
37
 
 
 
38
from bzrlib.i18n import gettext
38
39
 
39
40
class UseEditor(Exception):
40
41
    """Use an editor instead of selecting hunks."""
42
43
 
43
44
class ShelfReporter(object):
44
45
 
45
 
    vocab = {'add file': 'Shelve adding file "%(path)s"?',
46
 
             'binary': 'Shelve binary changes?',
47
 
             'change kind': 'Shelve changing "%s" from %(other)s'
48
 
             ' to %(this)s?',
49
 
             'delete file': 'Shelve removing file "%(path)s"?',
50
 
             'final': 'Shelve %d change(s)?',
51
 
             'hunk': 'Shelve?',
52
 
             'modify target': 'Shelve changing target of'
53
 
             ' "%(path)s" from "%(other)s" to "%(this)s"?',
54
 
             'rename': 'Shelve renaming "%(other)s" =>'
55
 
                        ' "%(this)s"?'
 
46
    vocab = {'add file': gettext('Shelve adding file "%(path)s"?'),
 
47
             'binary': gettext('Shelve binary changes?'),
 
48
             'change kind': gettext('Shelve changing "%s" from %(other)s'
 
49
             ' to %(this)s?'),
 
50
             'delete file': gettext('Shelve removing file "%(path)s"?'),
 
51
             'final': gettext('Shelve %d change(s)?'),
 
52
             'hunk': gettext('Shelve?'),
 
53
             'modify target': gettext('Shelve changing target of'
 
54
             ' "%(path)s" from "%(other)s" to "%(this)s"?'),
 
55
             'rename': gettext('Shelve renaming "%(other)s" =>'
 
56
                        ' "%(this)s"?')
56
57
             }
57
58
 
58
59
    invert_diff = False
66
67
 
67
68
    def shelved_id(self, shelf_id):
68
69
        """Report the id changes were shelved to."""
69
 
        trace.note('Changes shelved with id "%d".' % shelf_id)
 
70
        trace.note(gettext('Changes shelved with id "%d".') % shelf_id)
70
71
 
71
72
    def changes_destroyed(self):
72
73
        """Report that changes were made without shelving."""
73
 
        trace.note('Selected changes destroyed.')
 
74
        trace.note(gettext('Selected changes destroyed.'))
74
75
 
75
76
    def selected_changes(self, transform):
76
77
        """Report the changes that were selected."""
77
 
        trace.note("Selected changes:")
 
78
        trace.note(gettext("Selected changes:"))
78
79
        changes = transform.iter_changes()
79
80
        delta.report_changes(changes, self.delta_reporter)
80
81
 
94
95
 
95
96
class ApplyReporter(ShelfReporter):
96
97
 
97
 
    vocab = {'add file': 'Delete file "%(path)s"?',
98
 
             'binary': 'Apply binary changes?',
99
 
             'change kind': 'Change "%(path)s" from %(this)s'
100
 
             ' to %(other)s?',
101
 
             'delete file': 'Add file "%(path)s"?',
102
 
             'final': 'Apply %d change(s)?',
103
 
             'hunk': 'Apply change?',
104
 
             'modify target': 'Change target of'
105
 
             ' "%(path)s" from "%(this)s" to "%(other)s"?',
106
 
             'rename': 'Rename "%(this)s" => "%(other)s"?',
 
98
    vocab = {'add file': gettext('Delete file "%(path)s"?'),
 
99
             'binary': gettext('Apply binary changes?'),
 
100
             'change kind': gettext('Change "%(path)s" from %(this)s'
 
101
             ' to %(other)s?'),
 
102
             'delete file': gettext('Add file "%(path)s"?'),
 
103
             'final': gettext('Apply %d change(s)?'),
 
104
             'hunk': gettext('Apply change?'),
 
105
             'modify target': gettext('Change target of'
 
106
             ' "%(path)s" from "%(this)s" to "%(other)s"?'),
 
107
             'rename': gettext('Rename "%(this)s" => "%(other)s"?'),
107
108
             }
108
109
 
109
110
    invert_diff = True
154
155
 
155
156
    @classmethod
156
157
    def from_args(klass, diff_writer, revision=None, all=False, file_list=None,
157
 
                  message=None, directory='.', destroy=False):
 
158
                  message=None, directory=None, destroy=False):
158
159
        """Create a shelver from commandline arguments.
159
160
 
160
161
        The returned shelver wil have a work_tree that is locked and should
168
169
        :param destroy: Change the working tree without storing the shelved
169
170
            changes.
170
171
        """
 
172
        if directory is None:
 
173
            directory = u'.'
 
174
        elif file_list:
 
175
            file_list = [osutils.pathjoin(directory, f) for f in file_list]
171
176
        tree, path = workingtree.WorkingTree.open_containing(directory)
172
177
        # Ensure that tree is locked for the lifetime of target_tree, as
173
178
        # target tree may be reading from the same dirstate.
175
180
        try:
176
181
            target_tree = builtins._get_one_revision_tree('shelf2', revision,
177
182
                tree.branch, tree)
178
 
            files = builtins.safe_relpath_files(tree, file_list)
 
183
            files = tree.safe_relpath_files(file_list)
179
184
            return klass(tree, target_tree, diff_writer, all, all, files,
180
185
                         message, destroy)
181
186
        finally:
241
246
            new_tree = self.work_tree
242
247
        old_path = old_tree.id2path(file_id)
243
248
        new_path = new_tree.id2path(file_id)
244
 
        text_differ = diff.DiffText(old_tree, new_tree, diff_file)
 
249
        text_differ = diff.DiffText(old_tree, new_tree, diff_file,
 
250
            path_encoding=osutils.get_terminal_encoding())
245
251
        patch = text_differ.diff(file_id, old_path, new_path, 'file', 'file')
246
252
        diff_file.seek(0)
247
253
        return patches.parse_patch(diff_file)
248
254
 
249
 
    def prompt(self, message):
250
 
        """Prompt the user for a character.
251
 
 
252
 
        :param message: The message to prompt a user with.
253
 
        :return: A character.
254
 
        """
255
 
        if not sys.stdin.isatty():
256
 
            # Since there is no controlling terminal we will hang when trying
257
 
            # to prompt the user, better abort now.  See
258
 
            # https://code.launchpad.net/~bialix/bzr/shelve-no-tty/+merge/14905
259
 
            # for more context.
260
 
            raise errors.BzrError("You need a controlling terminal.")
261
 
        sys.stdout.write(message)
262
 
        char = osutils.getchar()
263
 
        sys.stdout.write("\r" + ' ' * len(message) + '\r')
264
 
        sys.stdout.flush()
265
 
        return char
266
 
 
267
 
    def prompt_bool(self, question, long=False, allow_editor=False):
 
255
    def prompt(self, message, choices, default):
 
256
        return ui.ui_factory.choose(message, choices, default=default)
 
257
 
 
258
    def prompt_bool(self, question, allow_editor=False):
268
259
        """Prompt the user with a yes/no question.
269
260
 
270
261
        This may be overridden by self.auto.  It may also *set* self.auto.  It
274
265
        """
275
266
        if self.auto:
276
267
            return True
277
 
        editor_string = ''
278
 
        if long:
279
 
            if allow_editor:
280
 
                editor_string = '(E)dit manually, '
281
 
            prompt = ' [(y)es, (N)o, %s(f)inish, or (q)uit]' % editor_string
 
268
        alternatives_chars = 'yn'
 
269
        alternatives = '&yes\n&No'
 
270
        if allow_editor:
 
271
            alternatives_chars += 'e'
 
272
            alternatives += '\n&edit manually'
 
273
        alternatives_chars += 'fq'
 
274
        alternatives += '\n&finish\n&quit'
 
275
        choice = self.prompt(question, alternatives, 1)
 
276
        if choice is None:
 
277
            # EOF.
 
278
            char = 'n'
282
279
        else:
283
 
            if allow_editor:
284
 
                editor_string = 'e'
285
 
            prompt = ' [yN%sfq?]' % editor_string
286
 
        char = self.prompt(question + prompt)
 
280
            char = alternatives_chars[choice]
287
281
        if char == 'y':
288
282
            return True
289
283
        elif char == 'e' and allow_editor:
291
285
        elif char == 'f':
292
286
            self.auto = True
293
287
            return True
294
 
        elif char == '?':
295
 
            return self.prompt_bool(question, long=True)
296
288
        if char == 'q':
297
289
            raise errors.UserAbort()
298
290
        else:
407
399
            else:
408
400
                shelf_id = manager.last_shelf()
409
401
                if shelf_id is None:
410
 
                    raise errors.BzrCommandError('No changes are shelved.')
 
402
                    raise errors.BzrCommandError(gettext('No changes are shelved.'))
411
403
            apply_changes = True
412
404
            delete_shelf = True
413
405
            read_shelf = True
465
457
        cleanups = [self.tree.unlock]
466
458
        try:
467
459
            if self.read_shelf:
468
 
                trace.note('Using changes with id "%d".' % self.shelf_id)
 
460
                trace.note(gettext('Using changes with id "%d".') % self.shelf_id)
469
461
                unshelver = self.manager.get_unshelver(self.shelf_id)
470
462
                cleanups.append(unshelver.finalize)
471
463
                if unshelver.message is not None:
472
 
                    trace.note('Message: %s' % unshelver.message)
 
464
                    trace.note(gettext('Message: %s') % unshelver.message)
473
465
                change_reporter = delta._ChangeReporter()
474
466
                merger = unshelver.make_merger(None)
475
467
                merger.change_reporter = change_reporter
481
473
                    self.show_changes(merger)
482
474
            if self.delete_shelf:
483
475
                self.manager.delete_shelf(self.shelf_id)
484
 
                trace.note('Deleted changes with id "%d".' % self.shelf_id)
 
476
                trace.note(gettext('Deleted changes with id "%d".') % self.shelf_id)
485
477
        finally:
486
478
            for cleanup in reversed(cleanups):
487
479
                cleanup()
492
484
        tt = tree_merger.make_preview_transform()
493
485
        new_tree = tt.get_preview_tree()
494
486
        if self.write_diff_to is None:
495
 
            self.write_diff_to = ui.ui_factory.make_output_stream()
496
 
        diff.show_diff_trees(merger.this_tree, new_tree, self.write_diff_to)
 
487
            self.write_diff_to = ui.ui_factory.make_output_stream(encoding_type='exact')
 
488
        path_encoding = osutils.get_diff_header_encoding()
 
489
        diff.show_diff_trees(merger.this_tree, new_tree, self.write_diff_to,
 
490
            path_encoding=path_encoding)
497
491
        tt.finalize()
498
492
 
499
493
    def show_changes(self, merger):