/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.16.101 by Aaron Bentley
Update GPL formatting and copyright
1
# Copyright (C) 2008 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
0.16.1 by Aaron Bentley
Begin implementing UI
16
17
18
from cStringIO import StringIO
0.16.5 by Aaron Bentley
Get text shelving working
19
import shutil
0.16.1 by Aaron Bentley
Begin implementing UI
20
import sys
0.16.5 by Aaron Bentley
Get text shelving working
21
import tempfile
0.16.1 by Aaron Bentley
Begin implementing UI
22
0.16.25 by Aaron Bentley
Show selected changes before shelving
23
from bzrlib import (
24
    builtins,
4603.1.7 by Aaron Bentley
Allow configuring change editor.
25
    commands,
0.16.25 by Aaron Bentley
Show selected changes before shelving
26
    delta,
27
    diff,
28
    errors,
0.16.79 by Aaron Bentley
Remove dependencies on bzrtools
29
    osutils,
0.16.25 by Aaron Bentley
Show selected changes before shelving
30
    patches,
4603.1.6 by Aaron Bentley
Provide a reasonable count of changes when file edited.
31
    patiencediff,
0.16.74 by Aaron Bentley
Merge with shelf-manager
32
    shelf,
0.16.72 by Aaron Bentley
Allow shelving binary changes
33
    textfile,
0.16.54 by Aaron Bentley
Inform user about shelf ids.
34
    trace,
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
35
    ui,
0.16.102 by Aaron Bentley
Minor updates
36
    workingtree,
37
)
3835.2.6 by Aaron Bentley
Restore vila's colordiff change
38
39
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
40
class UseEditor(Exception):
41
    """Use an editor instead of selecting hunks."""
42
43
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
44
class ShelfReporter(object):
4526.7.2 by Aaron Bentley
Update docs.
45
4526.7.1 by Aaron Bentley
Make vocabulary part of reporter.
46
    vocab = {'add file': 'Shelve adding file "%(path)s"?',
47
             'binary': 'Shelve binary changes?',
48
             'change kind': 'Shelve changing "%s" from %(other)s'
49
             ' to %(this)s?',
50
             'delete file': 'Shelve removing file "%(path)s"?',
51
             'final': 'Shelve %d change(s)?',
52
             'hunk': 'Shelve?',
53
             'modify target': 'Shelve changing target of'
54
             ' "%(path)s" from "%(other)s" to "%(this)s"?',
55
             'rename': 'Shelve renaming "%(other)s" =>'
56
                        ' "%(this)s"?'
57
             }
58
59
    invert_diff = False
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
60
61
    def __init__(self):
62
        self.delta_reporter = delta._ChangeReporter()
63
64
    def no_changes(self):
4526.7.2 by Aaron Bentley
Update docs.
65
        """Report that no changes were selected to apply."""
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
66
        trace.warning('No changes to shelve.')
67
68
    def shelved_id(self, shelf_id):
4526.7.2 by Aaron Bentley
Update docs.
69
        """Report the id changes were shelved to."""
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
70
        trace.note('Changes shelved with id "%d".' % shelf_id)
71
4526.6.4 by Aaron Bentley
Remove changes_destroyed message.
72
    def changes_destroyed(self):
4526.7.2 by Aaron Bentley
Update docs.
73
        """Report that changes were made without shelving."""
4526.6.4 by Aaron Bentley
Remove changes_destroyed message.
74
        trace.note('Selected changes destroyed.')
75
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
76
    def selected_changes(self, transform):
4526.7.2 by Aaron Bentley
Update docs.
77
        """Report the changes that were selected."""
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
78
        trace.note("Selected changes:")
79
        changes = transform.iter_changes()
80
        delta.report_changes(changes, self.delta_reporter)
81
4526.7.1 by Aaron Bentley
Make vocabulary part of reporter.
82
    def prompt_change(self, change):
4526.7.2 by Aaron Bentley
Update docs.
83
        """Determine the prompt for a change to apply."""
4526.7.1 by Aaron Bentley
Make vocabulary part of reporter.
84
        if change[0] == 'rename':
85
            vals = {'this': change[3], 'other': change[2]}
86
        elif change[0] == 'change kind':
87
            vals = {'path': change[4], 'other': change[2], 'this': change[3]}
88
        elif change[0] == 'modify target':
89
            vals = {'path': change[2], 'other': change[3], 'this': change[4]}
90
        else:
91
            vals = {'path': change[3]}
92
        prompt = self.vocab[change[0]] % vals
93
        return prompt
94
95
96
class ApplyReporter(ShelfReporter):
97
98
    vocab = {'add file': 'Delete file "%(path)s"?',
99
             'binary': 'Apply binary changes?',
100
             'change kind': 'Change "%(path)s" from %(this)s'
101
             ' to %(other)s?',
102
             'delete file': 'Add file "%(path)s"?',
103
             'final': 'Apply %d change(s)?',
104
             'hunk': 'Apply change?',
105
             'modify target': 'Change target of'
106
             ' "%(path)s" from "%(this)s" to "%(other)s"?',
107
             'rename': 'Rename "%(this)s" => "%(other)s"?',
108
             }
109
110
    invert_diff = True
111
112
    def changes_destroyed(self):
113
        pass
114
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
115
0.16.1 by Aaron Bentley
Begin implementing UI
116
class Shelver(object):
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
117
    """Interactively shelve the changes in a working tree."""
0.16.1 by Aaron Bentley
Begin implementing UI
118
0.16.108 by Aaron Bentley
Shelf supports multiple diff writers.
119
    def __init__(self, work_tree, target_tree, diff_writer=None, auto=False,
4100.3.1 by Aaron Bentley
Implement shelve --destroy
120
                 auto_apply=False, file_list=None, message=None,
4603.1.9 by Aaron Bentley
Misc cleanup.
121
                 destroy=False, manager=None, reporter=None):
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
122
        """Constructor.
123
124
        :param work_tree: The working tree to shelve changes from.
125
        :param target_tree: The "unchanged" / old tree to compare the
126
            work_tree to.
127
        :param auto: If True, shelve each possible change.
128
        :param auto_apply: If True, shelve changes with no final prompt.
0.16.102 by Aaron Bentley
Minor updates
129
        :param file_list: If supplied, only files in this list may be shelved.
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
130
        :param message: The message to associate with the shelved changes.
4100.3.2 by Aaron Bentley
Update docs
131
        :param destroy: Change the working tree without storing the shelved
132
            changes.
4465.1.2 by Aaron Bentley
Accept manager as a parameter to Shelver()
133
        :param manager: The shelf manager to use.
4526.7.2 by Aaron Bentley
Update docs.
134
        :param reporter: Object for reporting changes to user.
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
135
        """
0.16.1 by Aaron Bentley
Begin implementing UI
136
        self.work_tree = work_tree
137
        self.target_tree = target_tree
0.16.108 by Aaron Bentley
Shelf supports multiple diff writers.
138
        self.diff_writer = diff_writer
139
        if self.diff_writer is None:
0.16.79 by Aaron Bentley
Remove dependencies on bzrtools
140
            self.diff_writer = sys.stdout
4465.1.2 by Aaron Bentley
Accept manager as a parameter to Shelver()
141
        if manager is None:
142
            manager = work_tree.get_shelf_manager()
143
        self.manager = manager
0.16.15 by Aaron Bentley
Implement auto mode
144
        self.auto = auto
0.16.23 by Aaron Bentley
Improve prompting
145
        self.auto_apply = auto_apply
0.16.47 by Aaron Bentley
Support selecting files to shelve
146
        self.file_list = file_list
0.16.57 by Aaron Bentley
Expose messages in the UI
147
        self.message = message
4100.3.1 by Aaron Bentley
Implement shelve --destroy
148
        self.destroy = destroy
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
149
        if reporter is None:
150
            reporter = ShelfReporter()
151
        self.reporter = reporter
4603.1.14 by Aaron Bentley
Make change editor mandatory
152
        config = self.work_tree.branch.get_config()
153
        self.change_editor = config.get_change_editor(target_tree, work_tree)
4603.2.1 by Benoît Pierre
Lock the work tree in Shelver only after everything else has been setup.
154
        self.work_tree.lock_tree_write()
0.16.1 by Aaron Bentley
Begin implementing UI
155
156
    @classmethod
0.16.108 by Aaron Bentley
Shelf supports multiple diff writers.
157
    def from_args(klass, diff_writer, revision=None, all=False, file_list=None,
4100.3.1 by Aaron Bentley
Implement shelve --destroy
158
                  message=None, directory='.', destroy=False):
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
159
        """Create a shelver from commandline arguments.
160
4595.13.2 by Alexander Belchenko
[cherrypick revno 4650 from bzr.dev] Fix shelve on windows. (Robert Collins, #305006)
161
        The returned shelver wil have a work_tree that is locked and should
162
        be unlocked.
163
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
164
        :param revision: RevisionSpec of the revision to compare to.
165
        :param all: If True, shelve all changes without prompting.
166
        :param file_list: If supplied, only files in this list may be  shelved.
167
        :param message: The message to associate with the shelved changes.
168
        :param directory: The directory containing the working tree.
4100.3.1 by Aaron Bentley
Implement shelve --destroy
169
        :param destroy: Change the working tree without storing the shelved
170
            changes.
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
171
        """
0.16.94 by Aaron Bentley
Add unshelve tests
172
        tree, path = workingtree.WorkingTree.open_containing(directory)
4595.13.2 by Alexander Belchenko
[cherrypick revno 4650 from bzr.dev] Fix shelve on windows. (Robert Collins, #305006)
173
        # Ensure that tree is locked for the lifetime of target_tree, as
174
        # target tree may be reading from the same dirstate.
175
        tree.lock_tree_write()
176
        try:
177
            target_tree = builtins._get_one_revision_tree('shelf2', revision,
178
                tree.branch, tree)
179
            files = builtins.safe_relpath_files(tree, file_list)
4603.1.17 by Aaron Bentley
Fix shelf_ui tests to finalize.
180
            return klass(tree, target_tree, diff_writer, all, all, files,
181
                         message, destroy)
182
        finally:
4595.13.2 by Alexander Belchenko
[cherrypick revno 4650 from bzr.dev] Fix shelve on windows. (Robert Collins, #305006)
183
            tree.unlock()
0.16.1 by Aaron Bentley
Begin implementing UI
184
185
    def run(self):
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
186
        """Interactively shelve the changes."""
0.16.47 by Aaron Bentley
Support selecting files to shelve
187
        creator = shelf.ShelfCreator(self.work_tree, self.target_tree,
188
                                     self.file_list)
0.16.5 by Aaron Bentley
Get text shelving working
189
        self.tempdir = tempfile.mkdtemp()
0.16.22 by Aaron Bentley
Only prompt when there are changes to shelve.
190
        changes_shelved = 0
0.16.1 by Aaron Bentley
Begin implementing UI
191
        try:
4603.1.7 by Aaron Bentley
Allow configuring change editor.
192
            for change in creator.iter_shelvable():
193
                if change[0] == 'modify text':
194
                    try:
4603.1.9 by Aaron Bentley
Misc cleanup.
195
                        changes_shelved += self.handle_modify_text(creator,
196
                                                                   change[1])
4603.1.7 by Aaron Bentley
Allow configuring change editor.
197
                    except errors.BinaryFile:
198
                        if self.prompt_bool(self.reporter.vocab['binary']):
0.16.72 by Aaron Bentley
Allow shelving binary changes
199
                            changes_shelved += 1
4603.1.7 by Aaron Bentley
Allow configuring change editor.
200
                            creator.shelve_content_change(change[1])
4526.6.1 by Aaron Bentley
Reverse the way changes are described by Shelver.
201
                else:
4603.1.7 by Aaron Bentley
Allow configuring change editor.
202
                    if self.prompt_bool(self.reporter.prompt_change(change)):
203
                        creator.shelve_change(change)
204
                        changes_shelved += 1
205
            if changes_shelved > 0:
206
                self.reporter.selected_changes(creator.work_transform)
207
                if (self.auto_apply or self.prompt_bool(
208
                    self.reporter.vocab['final'] % changes_shelved)):
209
                    if self.destroy:
210
                        creator.transform()
211
                        self.reporter.changes_destroyed()
212
                    else:
213
                        shelf_id = self.manager.shelve_changes(creator,
214
                                                               self.message)
215
                        self.reporter.shelved_id(shelf_id)
216
            else:
217
                self.reporter.no_changes()
0.16.1 by Aaron Bentley
Begin implementing UI
218
        finally:
0.16.5 by Aaron Bentley
Get text shelving working
219
            shutil.rmtree(self.tempdir)
0.16.1 by Aaron Bentley
Begin implementing UI
220
            creator.finalize()
221
4603.1.11 by Aaron Bentley
Implement shelver.finalize
222
    def finalize(self):
4603.1.17 by Aaron Bentley
Fix shelf_ui tests to finalize.
223
        if self.change_editor is not None:
224
            self.change_editor.finish()
4603.1.14 by Aaron Bentley
Make change editor mandatory
225
        self.work_tree.unlock()
226
4603.1.11 by Aaron Bentley
Implement shelver.finalize
227
4526.6.2 by Aaron Bentley
Fix display of diffs for apply mode
228
    def get_parsed_patch(self, file_id, invert=False):
0.16.98 by Aaron Bentley
Update docs and prompting
229
        """Return a parsed version of a file's patch.
230
231
        :param file_id: The id of the file to generate a patch for.
4526.7.2 by Aaron Bentley
Update docs.
232
        :param invert: If True, provide an inverted patch (insertions displayed
233
            as removals, removals displayed as insertions).
0.16.98 by Aaron Bentley
Update docs and prompting
234
        :return: A patches.Patch.
235
        """
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
236
        diff_file = StringIO()
4526.6.2 by Aaron Bentley
Fix display of diffs for apply mode
237
        if invert:
238
            old_tree = self.work_tree
239
            new_tree = self.target_tree
240
        else:
241
            old_tree = self.target_tree
242
            new_tree = self.work_tree
4526.7.1 by Aaron Bentley
Make vocabulary part of reporter.
243
        old_path = old_tree.id2path(file_id)
244
        new_path = new_tree.id2path(file_id)
4526.6.2 by Aaron Bentley
Fix display of diffs for apply mode
245
        text_differ = diff.DiffText(old_tree, new_tree, diff_file)
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
246
        patch = text_differ.diff(file_id, old_path, new_path, 'file', 'file')
247
        diff_file.seek(0)
248
        return patches.parse_patch(diff_file)
0.16.1 by Aaron Bentley
Begin implementing UI
249
0.16.89 by Aaron Bentley
Add tests for Shelver
250
    def prompt(self, message):
0.16.98 by Aaron Bentley
Update docs and prompting
251
        """Prompt the user for a character.
252
253
        :param message: The message to prompt a user with.
254
        :return: A character.
255
        """
0.16.89 by Aaron Bentley
Add tests for Shelver
256
        sys.stdout.write(message)
257
        char = osutils.getchar()
258
        sys.stdout.write("\r" + ' ' * len(message) + '\r')
259
        sys.stdout.flush()
260
        return char
261
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
262
    def prompt_bool(self, question, long=False, allow_editor=False):
0.16.98 by Aaron Bentley
Update docs and prompting
263
        """Prompt the user with a yes/no question.
264
0.16.102 by Aaron Bentley
Minor updates
265
        This may be overridden by self.auto.  It may also *set* self.auto.  It
0.16.103 by Aaron Bentley
raise UserAbort instead of doing sys.exit
266
        may also raise UserAbort.
0.16.98 by Aaron Bentley
Update docs and prompting
267
        :param question: The question to ask the user.
268
        :return: True or False
269
        """
270
        if self.auto:
0.16.23 by Aaron Bentley
Improve prompting
271
            return True
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
272
        editor_string = ''
3990.4.3 by Daniel Watkins
Added help option to shelve prompt.
273
        if long:
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
274
            if allow_editor:
275
                editor_string = '(E)dit manually, '
276
            prompt = ' [(y)es, (N)o, %s(f)inish, or (q)uit]' % editor_string
3990.4.3 by Daniel Watkins
Added help option to shelve prompt.
277
        else:
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
278
            if allow_editor:
279
                editor_string = 'e'
280
            prompt = ' [yN%sfq?]' % editor_string
3990.4.3 by Daniel Watkins
Added help option to shelve prompt.
281
        char = self.prompt(question + prompt)
0.16.23 by Aaron Bentley
Improve prompting
282
        if char == 'y':
283
            return True
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
284
        elif char == 'e' and allow_editor:
285
            raise UseEditor
0.16.23 by Aaron Bentley
Improve prompting
286
        elif char == 'f':
287
            self.auto = True
288
            return True
3990.4.3 by Daniel Watkins
Added help option to shelve prompt.
289
        elif char == '?':
290
            return self.prompt_bool(question, long=True)
0.16.24 by Aaron Bentley
Regularize prompts
291
        if char == 'q':
0.16.103 by Aaron Bentley
raise UserAbort instead of doing sys.exit
292
            raise errors.UserAbort()
0.16.23 by Aaron Bentley
Improve prompting
293
        else:
294
            return False
0.16.1 by Aaron Bentley
Begin implementing UI
295
4603.1.7 by Aaron Bentley
Allow configuring change editor.
296
    def handle_modify_text(self, creator, file_id):
4603.1.9 by Aaron Bentley
Misc cleanup.
297
        """Handle modified text, by using hunk selection or file editing.
298
299
        :param creator: A ShelfCreator.
300
        :param file_id: The id of the file that was modified.
301
        :return: The number of changes.
302
        """
303
        work_tree_lines = self.work_tree.get_file_lines(file_id)
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
304
        try:
4603.1.9 by Aaron Bentley
Misc cleanup.
305
            lines, change_count = self._select_hunks(creator, file_id,
306
                                                     work_tree_lines)
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
307
        except UseEditor:
4603.1.9 by Aaron Bentley
Misc cleanup.
308
            lines, change_count = self._edit_file(file_id, work_tree_lines)
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
309
        if change_count != 0:
310
            creator.shelve_lines(file_id, lines)
311
        return change_count
312
4603.1.9 by Aaron Bentley
Misc cleanup.
313
    def _select_hunks(self, creator, file_id, work_tree_lines):
0.16.98 by Aaron Bentley
Update docs and prompting
314
        """Provide diff hunk selection for modified text.
315
4526.7.2 by Aaron Bentley
Update docs.
316
        If self.reporter.invert_diff is True, the diff is inverted so that
317
        insertions are displayed as removals and vice versa.
318
0.16.98 by Aaron Bentley
Update docs and prompting
319
        :param creator: a ShelfCreator
320
        :param file_id: The id of the file to shelve.
4603.1.9 by Aaron Bentley
Misc cleanup.
321
        :param work_tree_lines: Line contents of the file in the working tree.
0.16.98 by Aaron Bentley
Update docs and prompting
322
        :return: number of shelved hunks.
323
        """
4526.7.1 by Aaron Bentley
Make vocabulary part of reporter.
324
        if self.reporter.invert_diff:
4603.1.9 by Aaron Bentley
Misc cleanup.
325
            target_lines = work_tree_lines
4526.6.2 by Aaron Bentley
Fix display of diffs for apply mode
326
        else:
327
            target_lines = self.target_tree.get_file_lines(file_id)
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
328
        textfile.check_text_lines(work_tree_lines)
0.16.72 by Aaron Bentley
Allow shelving binary changes
329
        textfile.check_text_lines(target_lines)
4526.7.1 by Aaron Bentley
Make vocabulary part of reporter.
330
        parsed = self.get_parsed_patch(file_id, self.reporter.invert_diff)
0.16.43 by Aaron Bentley
Reduce API friction.
331
        final_hunks = []
0.16.15 by Aaron Bentley
Implement auto mode
332
        if not self.auto:
0.16.41 by Aaron Bentley
Implement shelving with internal patch
333
            offset = 0
0.16.61 by Aaron Bentley
Show file name when shelving
334
            self.diff_writer.write(parsed.get_header())
0.16.15 by Aaron Bentley
Implement auto mode
335
            for hunk in parsed.hunks:
336
                self.diff_writer.write(str(hunk))
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
337
                selected = self.prompt_bool(self.reporter.vocab['hunk'],
4603.1.7 by Aaron Bentley
Allow configuring change editor.
338
                                            allow_editor=(self.change_editor
339
                                                          is not None))
4526.7.1 by Aaron Bentley
Make vocabulary part of reporter.
340
                if not self.reporter.invert_diff:
4526.6.2 by Aaron Bentley
Fix display of diffs for apply mode
341
                    selected = (not selected)
342
                if selected:
0.16.41 by Aaron Bentley
Implement shelving with internal patch
343
                    hunk.mod_pos += offset
0.16.43 by Aaron Bentley
Reduce API friction.
344
                    final_hunks.append(hunk)
0.16.41 by Aaron Bentley
Implement shelving with internal patch
345
                else:
346
                    offset -= (hunk.mod_range - hunk.orig_range)
0.16.68 by Aaron Bentley
Avoid having escape codes affect the wrong text.
347
        sys.stdout.flush()
4526.7.1 by Aaron Bentley
Make vocabulary part of reporter.
348
        if self.reporter.invert_diff:
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
349
            change_count = len(final_hunks)
350
        else:
351
            change_count = len(parsed.hunks) - len(final_hunks)
4603.1.2 by Aaron Bentley
Simplify unchanged case.
352
        patched = patches.iter_patched_from_hunks(target_lines,
353
                                                  final_hunks)
354
        lines = list(patched)
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
355
        return lines, change_count
356
4603.1.9 by Aaron Bentley
Misc cleanup.
357
    def _edit_file(self, file_id, work_tree_lines):
358
        """
359
        :param file_id: id of the file to edit.
360
        :param work_tree_lines: Line contents of the file in the working tree.
361
        :return: (lines, change_region_count), where lines is the new line
362
            content of the file, and change_region_count is the number of
363
            changed regions.
364
        """
4603.1.7 by Aaron Bentley
Allow configuring change editor.
365
        lines = osutils.split_lines(self.change_editor.edit_file(file_id))
4603.1.6 by Aaron Bentley
Provide a reasonable count of changes when file edited.
366
        return lines, self._count_changed_regions(work_tree_lines, lines)
367
368
    @staticmethod
369
    def _count_changed_regions(old_lines, new_lines):
370
        matcher = patiencediff.PatienceSequenceMatcher(None, old_lines,
371
                                                       new_lines)
372
        blocks = matcher.get_matching_blocks()
373
        return len(blocks) - 2
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
374
375
376
class Unshelver(object):
0.16.98 by Aaron Bentley
Update docs and prompting
377
    """Unshelve changes into a working tree."""
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
378
379
    @classmethod
0.16.94 by Aaron Bentley
Add unshelve tests
380
    def from_args(klass, shelf_id=None, action='apply', directory='.'):
0.16.98 by Aaron Bentley
Update docs and prompting
381
        """Create an unshelver from commandline arguments.
382
4595.13.2 by Alexander Belchenko
[cherrypick revno 4650 from bzr.dev] Fix shelve on windows. (Robert Collins, #305006)
383
        The returned shelver wil have a tree that is locked and should
384
        be unlocked.
385
0.16.98 by Aaron Bentley
Update docs and prompting
386
        :param shelf_id: Integer id of the shelf, as a string.
387
        :param action: action to perform.  May be 'apply', 'dry-run',
388
            'delete'.
389
        :param directory: The directory to unshelve changes into.
390
        """
0.16.94 by Aaron Bentley
Add unshelve tests
391
        tree, path = workingtree.WorkingTree.open_containing(directory)
4595.13.2 by Alexander Belchenko
[cherrypick revno 4650 from bzr.dev] Fix shelve on windows. (Robert Collins, #305006)
392
        tree.lock_tree_write()
393
        try:
394
            manager = tree.get_shelf_manager()
395
            if shelf_id is not None:
396
                try:
397
                    shelf_id = int(shelf_id)
398
                except ValueError:
399
                    raise errors.InvalidShelfId(shelf_id)
400
            else:
401
                shelf_id = manager.last_shelf()
402
                if shelf_id is None:
403
                    raise errors.BzrCommandError('No changes are shelved.')
404
                trace.note('Unshelving changes with id "%d".' % shelf_id)
405
            apply_changes = True
406
            delete_shelf = True
407
            read_shelf = True
408
            if action == 'dry-run':
409
                apply_changes = False
410
                delete_shelf = False
411
            if action == 'delete-only':
412
                apply_changes = False
413
                read_shelf = False
414
        except:
415
            tree.unlock()
416
            raise
0.16.65 by Aaron Bentley
Implement unshelve --delete
417
        return klass(tree, manager, shelf_id, apply_changes, delete_shelf,
418
                     read_shelf)
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
419
0.16.94 by Aaron Bentley
Add unshelve tests
420
    def __init__(self, tree, manager, shelf_id, apply_changes=True,
421
                 delete_shelf=True, read_shelf=True):
0.16.98 by Aaron Bentley
Update docs and prompting
422
        """Constructor.
423
424
        :param tree: The working tree to unshelve into.
425
        :param manager: The ShelveManager containing the shelved changes.
426
        :param shelf_id:
427
        :param apply_changes: If True, apply the shelved changes to the
428
            working tree.
429
        :param delete_shelf: If True, delete the changes from the shelf.
430
        :param read_shelf: If True, read the changes from the shelf.
431
        """
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
432
        self.tree = tree
0.16.98 by Aaron Bentley
Update docs and prompting
433
        manager = tree.get_shelf_manager()
0.16.13 by Aaron Bentley
Appy shelve-management updates to shelver
434
        self.manager = manager
435
        self.shelf_id = shelf_id
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
436
        self.apply_changes = apply_changes
437
        self.delete_shelf = delete_shelf
0.16.65 by Aaron Bentley
Implement unshelve --delete
438
        self.read_shelf = read_shelf
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
439
440
    def run(self):
0.16.98 by Aaron Bentley
Update docs and prompting
441
        """Perform the unshelving operation."""
4595.13.2 by Alexander Belchenko
[cherrypick revno 4650 from bzr.dev] Fix shelve on windows. (Robert Collins, #305006)
442
        self.tree.lock_tree_write()
0.16.37 by Aaron Bentley
Use cleanups list to reduce nested try blocks
443
        cleanups = [self.tree.unlock]
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
444
        try:
0.16.65 by Aaron Bentley
Implement unshelve --delete
445
            if self.read_shelf:
446
                unshelver = self.manager.get_unshelver(self.shelf_id)
447
                cleanups.append(unshelver.finalize)
448
                if unshelver.message is not None:
449
                    trace.note('Message: %s' % unshelver.message)
450
                change_reporter = delta._ChangeReporter()
1551.21.7 by Aaron Bentley
Fix progress warning
451
                task = ui.ui_factory.nested_progress_bar()
452
                try:
453
                    merger = unshelver.make_merger(task)
454
                    merger.change_reporter = change_reporter
455
                    if self.apply_changes:
0.16.65 by Aaron Bentley
Implement unshelve --delete
456
                        merger.do_merge()
1551.21.7 by Aaron Bentley
Fix progress warning
457
                    else:
458
                        self.show_changes(merger)
459
                finally:
460
                    task.finished()
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
461
            if self.delete_shelf:
462
                self.manager.delete_shelf(self.shelf_id)
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
463
        finally:
0.16.37 by Aaron Bentley
Use cleanups list to reduce nested try blocks
464
            for cleanup in reversed(cleanups):
465
                cleanup()
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
466
467
    def show_changes(self, merger):
0.16.98 by Aaron Bentley
Update docs and prompting
468
        """Show the changes that this operation specifies."""
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
469
        tree_merger = merger.make_merger()
470
        # This implicitly shows the changes via the reporter, so we're done...
471
        tt = tree_merger.make_preview_transform()
472
        tt.finalize()