~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/shelf_ui.py

  • Committer: Ian Clatworthy
  • Date: 2008-12-15 04:37:10 UTC
  • mfrom: (3892.1.6 bzr.help-formats)
  • mto: This revision was merged to the branch mainline in revision 3905.
  • Revision ID: ian.clatworthy@canonical.com-20081215043710-ybhxvqjeir13k5ht
Improved help on storage formats (Ian Clatworthy)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 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
 
18
18
from cStringIO import StringIO
27
27
    errors,
28
28
    osutils,
29
29
    patches,
30
 
    patiencediff,
31
30
    shelf,
32
31
    textfile,
33
32
    trace,
34
33
    ui,
35
34
    workingtree,
36
35
)
37
 
from bzrlib.i18n import gettext
38
 
 
39
 
class UseEditor(Exception):
40
 
    """Use an editor instead of selecting hunks."""
41
 
 
42
 
 
43
 
class ShelfReporter(object):
44
 
 
45
 
    vocab = {'add file': gettext('Shelve adding file "%(path)s"?'),
46
 
             'binary': gettext('Shelve binary changes?'),
47
 
             'change kind': gettext('Shelve changing "%s" from %(other)s'
48
 
             ' to %(this)s?'),
49
 
             'delete file': gettext('Shelve removing file "%(path)s"?'),
50
 
             'final': gettext('Shelve %d change(s)?'),
51
 
             'hunk': gettext('Shelve?'),
52
 
             'modify target': gettext('Shelve changing target of'
53
 
             ' "%(path)s" from "%(other)s" to "%(this)s"?'),
54
 
             'rename': gettext('Shelve renaming "%(other)s" =>'
55
 
                        ' "%(this)s"?')
56
 
             }
57
 
 
58
 
    invert_diff = False
59
 
 
60
 
    def __init__(self):
61
 
        self.delta_reporter = delta._ChangeReporter()
62
 
 
63
 
    def no_changes(self):
64
 
        """Report that no changes were selected to apply."""
65
 
        trace.warning('No changes to shelve.')
66
 
 
67
 
    def shelved_id(self, shelf_id):
68
 
        """Report the id changes were shelved to."""
69
 
        trace.note(gettext('Changes shelved with id "%d".') % shelf_id)
70
 
 
71
 
    def changes_destroyed(self):
72
 
        """Report that changes were made without shelving."""
73
 
        trace.note(gettext('Selected changes destroyed.'))
74
 
 
75
 
    def selected_changes(self, transform):
76
 
        """Report the changes that were selected."""
77
 
        trace.note(gettext("Selected changes:"))
78
 
        changes = transform.iter_changes()
79
 
        delta.report_changes(changes, self.delta_reporter)
80
 
 
81
 
    def prompt_change(self, change):
82
 
        """Determine the prompt for a change to apply."""
83
 
        if change[0] == 'rename':
84
 
            vals = {'this': change[3], 'other': change[2]}
85
 
        elif change[0] == 'change kind':
86
 
            vals = {'path': change[4], 'other': change[2], 'this': change[3]}
87
 
        elif change[0] == 'modify target':
88
 
            vals = {'path': change[2], 'other': change[3], 'this': change[4]}
89
 
        else:
90
 
            vals = {'path': change[3]}
91
 
        prompt = self.vocab[change[0]] % vals
92
 
        return prompt
93
 
 
94
 
 
95
 
class ApplyReporter(ShelfReporter):
96
 
 
97
 
    vocab = {'add file': gettext('Delete file "%(path)s"?'),
98
 
             'binary': gettext('Apply binary changes?'),
99
 
             'change kind': gettext('Change "%(path)s" from %(this)s'
100
 
             ' to %(other)s?'),
101
 
             'delete file': gettext('Add file "%(path)s"?'),
102
 
             'final': gettext('Apply %d change(s)?'),
103
 
             'hunk': gettext('Apply change?'),
104
 
             'modify target': gettext('Change target of'
105
 
             ' "%(path)s" from "%(this)s" to "%(other)s"?'),
106
 
             'rename': gettext('Rename "%(this)s" => "%(other)s"?'),
107
 
             }
108
 
 
109
 
    invert_diff = True
110
 
 
111
 
    def changes_destroyed(self):
112
 
        pass
113
36
 
114
37
 
115
38
class Shelver(object):
116
39
    """Interactively shelve the changes in a working tree."""
117
40
 
118
41
    def __init__(self, work_tree, target_tree, diff_writer=None, auto=False,
119
 
                 auto_apply=False, file_list=None, message=None,
120
 
                 destroy=False, manager=None, reporter=None):
 
42
                 auto_apply=False, file_list=None, message=None):
121
43
        """Constructor.
122
44
 
123
45
        :param work_tree: The working tree to shelve changes from.
127
49
        :param auto_apply: If True, shelve changes with no final prompt.
128
50
        :param file_list: If supplied, only files in this list may be shelved.
129
51
        :param message: The message to associate with the shelved changes.
130
 
        :param destroy: Change the working tree without storing the shelved
131
 
            changes.
132
 
        :param manager: The shelf manager to use.
133
 
        :param reporter: Object for reporting changes to user.
134
52
        """
135
53
        self.work_tree = work_tree
136
54
        self.target_tree = target_tree
137
55
        self.diff_writer = diff_writer
138
56
        if self.diff_writer is None:
139
57
            self.diff_writer = sys.stdout
140
 
        if manager is None:
141
 
            manager = work_tree.get_shelf_manager()
142
 
        self.manager = manager
 
58
        self.manager = work_tree.get_shelf_manager()
143
59
        self.auto = auto
144
60
        self.auto_apply = auto_apply
145
61
        self.file_list = file_list
146
62
        self.message = message
147
 
        self.destroy = destroy
148
 
        if reporter is None:
149
 
            reporter = ShelfReporter()
150
 
        self.reporter = reporter
151
 
        config = self.work_tree.branch.get_config()
152
 
        self.change_editor = config.get_change_editor(target_tree, work_tree)
153
 
        self.work_tree.lock_tree_write()
154
63
 
155
64
    @classmethod
156
65
    def from_args(klass, diff_writer, revision=None, all=False, file_list=None,
157
 
                  message=None, directory=None, destroy=False):
 
66
                  message=None, directory='.'):
158
67
        """Create a shelver from commandline arguments.
159
68
 
160
 
        The returned shelver wil have a work_tree that is locked and should
161
 
        be unlocked.
162
 
 
163
69
        :param revision: RevisionSpec of the revision to compare to.
164
70
        :param all: If True, shelve all changes without prompting.
165
71
        :param file_list: If supplied, only files in this list may be  shelved.
166
72
        :param message: The message to associate with the shelved changes.
167
73
        :param directory: The directory containing the working tree.
168
 
        :param destroy: Change the working tree without storing the shelved
169
 
            changes.
170
74
        """
171
 
        if directory is None:
172
 
            directory = u'.'
173
 
        elif file_list:
174
 
            file_list = [osutils.pathjoin(directory, f) for f in file_list]
175
75
        tree, path = workingtree.WorkingTree.open_containing(directory)
176
 
        # Ensure that tree is locked for the lifetime of target_tree, as
177
 
        # target tree may be reading from the same dirstate.
178
 
        tree.lock_tree_write()
179
 
        try:
180
 
            target_tree = builtins._get_one_revision_tree('shelf2', revision,
181
 
                tree.branch, tree)
182
 
            files = tree.safe_relpath_files(file_list)
183
 
            return klass(tree, target_tree, diff_writer, all, all, files,
184
 
                         message, destroy)
185
 
        finally:
186
 
            tree.unlock()
 
76
        target_tree = builtins._get_one_revision_tree('shelf2', revision,
 
77
            tree.branch, tree)
 
78
        files = builtins.safe_relpath_files(tree, file_list)
 
79
        return klass(tree, target_tree, diff_writer, all, all, files, message)
187
80
 
188
81
    def run(self):
189
82
        """Interactively shelve the changes."""
198
91
                        changes_shelved += self.handle_modify_text(creator,
199
92
                                                                   change[1])
200
93
                    except errors.BinaryFile:
201
 
                        if self.prompt_bool(self.reporter.vocab['binary']):
 
94
                        if self.prompt_bool('Shelve binary changes?'):
202
95
                            changes_shelved += 1
203
96
                            creator.shelve_content_change(change[1])
204
 
                else:
205
 
                    if self.prompt_bool(self.reporter.prompt_change(change)):
206
 
                        creator.shelve_change(change)
 
97
                if change[0] == 'add file':
 
98
                    if self.prompt_bool('Shelve adding file "%s"?'
 
99
                                        % change[3]):
 
100
                        creator.shelve_creation(change[1])
 
101
                        changes_shelved += 1
 
102
                if change[0] == 'delete file':
 
103
                    if self.prompt_bool('Shelve removing file "%s"?'
 
104
                                        % change[3]):
 
105
                        creator.shelve_deletion(change[1])
 
106
                        changes_shelved += 1
 
107
                if change[0] == 'change kind':
 
108
                    if self.prompt_bool('Shelve changing "%s" from %s to %s? '
 
109
                                        % (change[4], change[2], change[3])):
 
110
                        creator.shelve_content_change(change[1])
 
111
                        changes_shelved += 1
 
112
                if change[0] == 'rename':
 
113
                    if self.prompt_bool('Shelve renaming "%s" => "%s"?' %
 
114
                                   change[2:]):
 
115
                        creator.shelve_rename(change[1])
207
116
                        changes_shelved += 1
208
117
            if changes_shelved > 0:
209
 
                self.reporter.selected_changes(creator.work_transform)
 
118
                trace.note("Selected changes:")
 
119
                changes = creator.work_transform.iter_changes()
 
120
                reporter = delta._ChangeReporter()
 
121
                delta.report_changes(changes, reporter)
210
122
                if (self.auto_apply or self.prompt_bool(
211
 
                    self.reporter.vocab['final'] % changes_shelved)):
212
 
                    if self.destroy:
213
 
                        creator.transform()
214
 
                        self.reporter.changes_destroyed()
215
 
                    else:
216
 
                        shelf_id = self.manager.shelve_changes(creator,
217
 
                                                               self.message)
218
 
                        self.reporter.shelved_id(shelf_id)
 
123
                    'Shelve %d change(s)?' % changes_shelved)):
 
124
                    shelf_id = self.manager.shelve_changes(creator,
 
125
                                                           self.message)
 
126
                    trace.note('Changes shelved with id "%d".' % shelf_id)
219
127
            else:
220
 
                self.reporter.no_changes()
 
128
                trace.warning('No changes to shelve.')
221
129
        finally:
222
130
            shutil.rmtree(self.tempdir)
223
131
            creator.finalize()
224
132
 
225
 
    def finalize(self):
226
 
        if self.change_editor is not None:
227
 
            self.change_editor.finish()
228
 
        self.work_tree.unlock()
229
 
 
230
 
 
231
 
    def get_parsed_patch(self, file_id, invert=False):
 
133
    def get_parsed_patch(self, file_id):
232
134
        """Return a parsed version of a file's patch.
233
135
 
234
136
        :param file_id: The id of the file to generate a patch for.
235
 
        :param invert: If True, provide an inverted patch (insertions displayed
236
 
            as removals, removals displayed as insertions).
237
137
        :return: A patches.Patch.
238
138
        """
 
139
        old_path = self.target_tree.id2path(file_id)
 
140
        new_path = self.work_tree.id2path(file_id)
239
141
        diff_file = StringIO()
240
 
        if invert:
241
 
            old_tree = self.work_tree
242
 
            new_tree = self.target_tree
243
 
        else:
244
 
            old_tree = self.target_tree
245
 
            new_tree = self.work_tree
246
 
        old_path = old_tree.id2path(file_id)
247
 
        new_path = new_tree.id2path(file_id)
248
 
        text_differ = diff.DiffText(old_tree, new_tree, diff_file,
249
 
            path_encoding=osutils.get_terminal_encoding())
 
142
        text_differ = diff.DiffText(self.target_tree, self.work_tree,
 
143
                                    diff_file)
250
144
        patch = text_differ.diff(file_id, old_path, new_path, 'file', 'file')
251
145
        diff_file.seek(0)
252
146
        return patches.parse_patch(diff_file)
257
151
        :param message: The message to prompt a user with.
258
152
        :return: A character.
259
153
        """
260
 
        if not sys.stdin.isatty():
261
 
            # Since there is no controlling terminal we will hang when trying
262
 
            # to prompt the user, better abort now.  See
263
 
            # https://code.launchpad.net/~bialix/bzr/shelve-no-tty/+merge/14905
264
 
            # for more context.
265
 
            raise errors.BzrError(gettext("You need a controlling terminal."))
266
154
        sys.stdout.write(message)
267
155
        char = osutils.getchar()
268
156
        sys.stdout.write("\r" + ' ' * len(message) + '\r')
269
157
        sys.stdout.flush()
270
158
        return char
271
159
 
272
 
    def prompt_bool(self, question, long=False, allow_editor=False):
 
160
    def prompt_bool(self, question):
273
161
        """Prompt the user with a yes/no question.
274
162
 
275
163
        This may be overridden by self.auto.  It may also *set* self.auto.  It
279
167
        """
280
168
        if self.auto:
281
169
            return True
282
 
        editor_string = ''
283
 
        if long:
284
 
            if allow_editor:
285
 
                editor_string = '(E)dit manually, '
286
 
            prompt = ' [(y)es, (N)o, %s(f)inish, or (q)uit]' % editor_string
287
 
        else:
288
 
            if allow_editor:
289
 
                editor_string = 'e'
290
 
            prompt = ' [yN%sfq?]' % editor_string
291
 
        char = self.prompt(question + prompt)
 
170
        char = self.prompt(question + ' [yNfq]')
292
171
        if char == 'y':
293
172
            return True
294
 
        elif char == 'e' and allow_editor:
295
 
            raise UseEditor
296
173
        elif char == 'f':
297
174
            self.auto = True
298
175
            return True
299
 
        elif char == '?':
300
 
            return self.prompt_bool(question, long=True)
301
176
        if char == 'q':
302
177
            raise errors.UserAbort()
303
178
        else:
304
179
            return False
305
180
 
306
181
    def handle_modify_text(self, creator, file_id):
307
 
        """Handle modified text, by using hunk selection or file editing.
308
 
 
309
 
        :param creator: A ShelfCreator.
310
 
        :param file_id: The id of the file that was modified.
311
 
        :return: The number of changes.
312
 
        """
313
 
        work_tree_lines = self.work_tree.get_file_lines(file_id)
314
 
        try:
315
 
            lines, change_count = self._select_hunks(creator, file_id,
316
 
                                                     work_tree_lines)
317
 
        except UseEditor:
318
 
            lines, change_count = self._edit_file(file_id, work_tree_lines)
319
 
        if change_count != 0:
320
 
            creator.shelve_lines(file_id, lines)
321
 
        return change_count
322
 
 
323
 
    def _select_hunks(self, creator, file_id, work_tree_lines):
324
182
        """Provide diff hunk selection for modified text.
325
183
 
326
 
        If self.reporter.invert_diff is True, the diff is inverted so that
327
 
        insertions are displayed as removals and vice versa.
328
 
 
329
184
        :param creator: a ShelfCreator
330
185
        :param file_id: The id of the file to shelve.
331
 
        :param work_tree_lines: Line contents of the file in the working tree.
332
186
        :return: number of shelved hunks.
333
187
        """
334
 
        if self.reporter.invert_diff:
335
 
            target_lines = work_tree_lines
336
 
        else:
337
 
            target_lines = self.target_tree.get_file_lines(file_id)
338
 
        textfile.check_text_lines(work_tree_lines)
 
188
        target_lines = self.target_tree.get_file_lines(file_id)
 
189
        textfile.check_text_lines(self.work_tree.get_file_lines(file_id))
339
190
        textfile.check_text_lines(target_lines)
340
 
        parsed = self.get_parsed_patch(file_id, self.reporter.invert_diff)
 
191
        parsed = self.get_parsed_patch(file_id)
341
192
        final_hunks = []
342
193
        if not self.auto:
343
194
            offset = 0
344
195
            self.diff_writer.write(parsed.get_header())
345
196
            for hunk in parsed.hunks:
346
197
                self.diff_writer.write(str(hunk))
347
 
                selected = self.prompt_bool(self.reporter.vocab['hunk'],
348
 
                                            allow_editor=(self.change_editor
349
 
                                                          is not None))
350
 
                if not self.reporter.invert_diff:
351
 
                    selected = (not selected)
352
 
                if selected:
 
198
                if not self.prompt_bool('Shelve?'):
353
199
                    hunk.mod_pos += offset
354
200
                    final_hunks.append(hunk)
355
201
                else:
356
202
                    offset -= (hunk.mod_range - hunk.orig_range)
357
203
        sys.stdout.flush()
358
 
        if self.reporter.invert_diff:
359
 
            change_count = len(final_hunks)
360
 
        else:
361
 
            change_count = len(parsed.hunks) - len(final_hunks)
362
 
        patched = patches.iter_patched_from_hunks(target_lines,
363
 
                                                  final_hunks)
364
 
        lines = list(patched)
365
 
        return lines, change_count
366
 
 
367
 
    def _edit_file(self, file_id, work_tree_lines):
368
 
        """
369
 
        :param file_id: id of the file to edit.
370
 
        :param work_tree_lines: Line contents of the file in the working tree.
371
 
        :return: (lines, change_region_count), where lines is the new line
372
 
            content of the file, and change_region_count is the number of
373
 
            changed regions.
374
 
        """
375
 
        lines = osutils.split_lines(self.change_editor.edit_file(file_id))
376
 
        return lines, self._count_changed_regions(work_tree_lines, lines)
377
 
 
378
 
    @staticmethod
379
 
    def _count_changed_regions(old_lines, new_lines):
380
 
        matcher = patiencediff.PatienceSequenceMatcher(None, old_lines,
381
 
                                                       new_lines)
382
 
        blocks = matcher.get_matching_blocks()
383
 
        return len(blocks) - 2
 
204
        if len(parsed.hunks) == len(final_hunks):
 
205
            return 0
 
206
        patched = patches.iter_patched_from_hunks(target_lines, final_hunks)
 
207
        creator.shelve_lines(file_id, list(patched))
 
208
        return len(parsed.hunks) - len(final_hunks)
384
209
 
385
210
 
386
211
class Unshelver(object):
387
212
    """Unshelve changes into a working tree."""
388
213
 
389
214
    @classmethod
390
 
    def from_args(klass, shelf_id=None, action='apply', directory='.',
391
 
                  write_diff_to=None):
 
215
    def from_args(klass, shelf_id=None, action='apply', directory='.'):
392
216
        """Create an unshelver from commandline arguments.
393
217
 
394
 
        The returned shelver will have a tree that is locked and should
395
 
        be unlocked.
396
 
 
397
218
        :param shelf_id: Integer id of the shelf, as a string.
398
219
        :param action: action to perform.  May be 'apply', 'dry-run',
399
 
            'delete', 'preview'.
 
220
            'delete'.
400
221
        :param directory: The directory to unshelve changes into.
401
 
        :param write_diff_to: See Unshelver.__init__().
402
222
        """
403
223
        tree, path = workingtree.WorkingTree.open_containing(directory)
404
 
        tree.lock_tree_write()
405
 
        try:
406
 
            manager = tree.get_shelf_manager()
407
 
            if shelf_id is not None:
408
 
                try:
409
 
                    shelf_id = int(shelf_id)
410
 
                except ValueError:
411
 
                    raise errors.InvalidShelfId(shelf_id)
412
 
            else:
413
 
                shelf_id = manager.last_shelf()
414
 
                if shelf_id is None:
415
 
                    raise errors.BzrCommandError(gettext('No changes are shelved.'))
416
 
            apply_changes = True
417
 
            delete_shelf = True
418
 
            read_shelf = True
419
 
            show_diff = False
420
 
            if action == 'dry-run':
421
 
                apply_changes = False
422
 
                delete_shelf = False
423
 
            elif action == 'preview':
424
 
                apply_changes = False
425
 
                delete_shelf = False
426
 
                show_diff = True
427
 
            elif action == 'delete-only':
428
 
                apply_changes = False
429
 
                read_shelf = False
430
 
            elif action == 'keep':
431
 
                apply_changes = True
432
 
                delete_shelf = False
433
 
        except:
434
 
            tree.unlock()
435
 
            raise
 
224
        manager = tree.get_shelf_manager()
 
225
        if shelf_id is not None:
 
226
            shelf_id = int(shelf_id)
 
227
        else:
 
228
            shelf_id = manager.last_shelf()
 
229
            if shelf_id is None:
 
230
                raise errors.BzrCommandError('No changes are shelved.')
 
231
            trace.note('Unshelving changes with id "%d".' % shelf_id)
 
232
        apply_changes = True
 
233
        delete_shelf = True
 
234
        read_shelf = True
 
235
        if action == 'dry-run':
 
236
            apply_changes = False
 
237
            delete_shelf = False
 
238
        if action == 'delete-only':
 
239
            apply_changes = False
 
240
            read_shelf = False
436
241
        return klass(tree, manager, shelf_id, apply_changes, delete_shelf,
437
 
                     read_shelf, show_diff, write_diff_to)
 
242
                     read_shelf)
438
243
 
439
244
    def __init__(self, tree, manager, shelf_id, apply_changes=True,
440
 
                 delete_shelf=True, read_shelf=True, show_diff=False,
441
 
                 write_diff_to=None):
 
245
                 delete_shelf=True, read_shelf=True):
442
246
        """Constructor.
443
247
 
444
248
        :param tree: The working tree to unshelve into.
448
252
            working tree.
449
253
        :param delete_shelf: If True, delete the changes from the shelf.
450
254
        :param read_shelf: If True, read the changes from the shelf.
451
 
        :param show_diff: If True, show the diff that would result from
452
 
            unshelving the changes.
453
 
        :param write_diff_to: A file-like object where the diff will be
454
 
            written to. If None, ui.ui_factory.make_output_stream() will
455
 
            be used.
456
255
        """
457
256
        self.tree = tree
458
257
        manager = tree.get_shelf_manager()
461
260
        self.apply_changes = apply_changes
462
261
        self.delete_shelf = delete_shelf
463
262
        self.read_shelf = read_shelf
464
 
        self.show_diff = show_diff
465
 
        self.write_diff_to = write_diff_to
466
263
 
467
264
    def run(self):
468
265
        """Perform the unshelving operation."""
469
 
        self.tree.lock_tree_write()
 
266
        self.tree.lock_write()
470
267
        cleanups = [self.tree.unlock]
471
268
        try:
472
269
            if self.read_shelf:
473
 
                trace.note(gettext('Using changes with id "%d".') % self.shelf_id)
474
270
                unshelver = self.manager.get_unshelver(self.shelf_id)
475
271
                cleanups.append(unshelver.finalize)
476
272
                if unshelver.message is not None:
477
 
                    trace.note(gettext('Message: %s') % unshelver.message)
 
273
                    trace.note('Message: %s' % unshelver.message)
478
274
                change_reporter = delta._ChangeReporter()
479
 
                merger = unshelver.make_merger(None)
 
275
                merger = unshelver.make_merger()
480
276
                merger.change_reporter = change_reporter
481
277
                if self.apply_changes:
482
 
                    merger.do_merge()
483
 
                elif self.show_diff:
484
 
                    self.write_diff(merger)
 
278
                    pb = ui.ui_factory.nested_progress_bar()
 
279
                    try:
 
280
                        merger.do_merge()
 
281
                    finally:
 
282
                        pb.finished()
485
283
                else:
486
284
                    self.show_changes(merger)
487
285
            if self.delete_shelf:
488
286
                self.manager.delete_shelf(self.shelf_id)
489
 
                trace.note(gettext('Deleted changes with id "%d".') % self.shelf_id)
490
287
        finally:
491
288
            for cleanup in reversed(cleanups):
492
289
                cleanup()
493
290
 
494
 
    def write_diff(self, merger):
495
 
        """Write this operation's diff to self.write_diff_to."""
496
 
        tree_merger = merger.make_merger()
497
 
        tt = tree_merger.make_preview_transform()
498
 
        new_tree = tt.get_preview_tree()
499
 
        if self.write_diff_to is None:
500
 
            self.write_diff_to = ui.ui_factory.make_output_stream(encoding_type='exact')
501
 
        path_encoding = osutils.get_diff_header_encoding()
502
 
        diff.show_diff_trees(merger.this_tree, new_tree, self.write_diff_to,
503
 
            path_encoding=path_encoding)
504
 
        tt.finalize()
505
 
 
506
291
    def show_changes(self, merger):
507
292
        """Show the changes that this operation specifies."""
508
293
        tree_merger = merger.make_merger()