~bzr-pqm/bzr/bzr.dev

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,
25
    delta,
26
    diff,
27
    errors,
0.16.79 by Aaron Bentley
Remove dependencies on bzrtools
28
    osutils,
0.16.25 by Aaron Bentley
Show selected changes before shelving
29
    patches,
0.16.74 by Aaron Bentley
Merge with shelf-manager
30
    shelf,
0.16.72 by Aaron Bentley
Allow shelving binary changes
31
    textfile,
0.16.54 by Aaron Bentley
Inform user about shelf ids.
32
    trace,
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
33
    ui,
0.16.102 by Aaron Bentley
Minor updates
34
    workingtree,
35
)
3835.2.6 by Aaron Bentley
Restore vila's colordiff change
36
37
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
38
class ShelfReporter(object):
39
40
    def __init__(self):
41
        self.delta_reporter = delta._ChangeReporter()
42
43
    def no_changes(self):
44
        trace.warning('No changes to shelve.')
45
46
    def shelved_id(self, shelf_id):
47
        trace.note('Changes shelved with id "%d".' % shelf_id)
48
49
    def selected_changes(self, transform):
50
        trace.note("Selected changes:")
51
        changes = transform.iter_changes()
52
        delta.report_changes(changes, self.delta_reporter)
53
54
0.16.1 by Aaron Bentley
Begin implementing UI
55
class Shelver(object):
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
56
    """Interactively shelve the changes in a working tree."""
0.16.1 by Aaron Bentley
Begin implementing UI
57
0.16.108 by Aaron Bentley
Shelf supports multiple diff writers.
58
    def __init__(self, work_tree, target_tree, diff_writer=None, auto=False,
4100.3.1 by Aaron Bentley
Implement shelve --destroy
59
                 auto_apply=False, file_list=None, message=None,
4465.1.3 by Aaron Bentley
Merge bzr.dev into vpipe
60
                 destroy=False, manager=None, reporter=None):
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
61
        """Constructor.
62
63
        :param work_tree: The working tree to shelve changes from.
64
        :param target_tree: The "unchanged" / old tree to compare the
65
            work_tree to.
66
        :param auto: If True, shelve each possible change.
67
        :param auto_apply: If True, shelve changes with no final prompt.
0.16.102 by Aaron Bentley
Minor updates
68
        :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.
69
        :param message: The message to associate with the shelved changes.
4100.3.2 by Aaron Bentley
Update docs
70
        :param destroy: Change the working tree without storing the shelved
71
            changes.
4465.1.2 by Aaron Bentley
Accept manager as a parameter to Shelver()
72
        :param manager: The shelf manager to use.
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
73
        """
0.16.1 by Aaron Bentley
Begin implementing UI
74
        self.work_tree = work_tree
75
        self.target_tree = target_tree
0.16.108 by Aaron Bentley
Shelf supports multiple diff writers.
76
        self.diff_writer = diff_writer
77
        if self.diff_writer is None:
0.16.79 by Aaron Bentley
Remove dependencies on bzrtools
78
            self.diff_writer = sys.stdout
4465.1.2 by Aaron Bentley
Accept manager as a parameter to Shelver()
79
        if manager is None:
80
            manager = work_tree.get_shelf_manager()
81
        self.manager = manager
0.16.15 by Aaron Bentley
Implement auto mode
82
        self.auto = auto
0.16.23 by Aaron Bentley
Improve prompting
83
        self.auto_apply = auto_apply
0.16.47 by Aaron Bentley
Support selecting files to shelve
84
        self.file_list = file_list
0.16.57 by Aaron Bentley
Expose messages in the UI
85
        self.message = message
4100.3.1 by Aaron Bentley
Implement shelve --destroy
86
        self.destroy = destroy
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
87
        if reporter is None:
88
            reporter = ShelfReporter()
89
        self.reporter = reporter
0.16.1 by Aaron Bentley
Begin implementing UI
90
91
    @classmethod
0.16.108 by Aaron Bentley
Shelf supports multiple diff writers.
92
    def from_args(klass, diff_writer, revision=None, all=False, file_list=None,
4100.3.1 by Aaron Bentley
Implement shelve --destroy
93
                  message=None, directory='.', destroy=False):
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
94
        """Create a shelver from commandline arguments.
95
96
        :param revision: RevisionSpec of the revision to compare to.
97
        :param all: If True, shelve all changes without prompting.
98
        :param file_list: If supplied, only files in this list may be  shelved.
99
        :param message: The message to associate with the shelved changes.
100
        :param directory: The directory containing the working tree.
4100.3.1 by Aaron Bentley
Implement shelve --destroy
101
        :param destroy: Change the working tree without storing the shelved
102
            changes.
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
103
        """
0.16.94 by Aaron Bentley
Add unshelve tests
104
        tree, path = workingtree.WorkingTree.open_containing(directory)
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
105
        target_tree = builtins._get_one_revision_tree('shelf2', revision,
106
            tree.branch, tree)
0.16.120 by Aaron Bentley
Use relative paths with shelve
107
        files = builtins.safe_relpath_files(tree, file_list)
4100.3.1 by Aaron Bentley
Implement shelve --destroy
108
        return klass(tree, target_tree, diff_writer, all, all, files, message,
109
                     destroy)
0.16.1 by Aaron Bentley
Begin implementing UI
110
111
    def run(self):
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
112
        """Interactively shelve the changes."""
0.16.47 by Aaron Bentley
Support selecting files to shelve
113
        creator = shelf.ShelfCreator(self.work_tree, self.target_tree,
114
                                     self.file_list)
0.16.5 by Aaron Bentley
Get text shelving working
115
        self.tempdir = tempfile.mkdtemp()
0.16.22 by Aaron Bentley
Only prompt when there are changes to shelve.
116
        changes_shelved = 0
0.16.1 by Aaron Bentley
Begin implementing UI
117
        try:
0.16.86 by Aaron Bentley
Switch to iter_shelvable
118
            for change in creator.iter_shelvable():
0.16.5 by Aaron Bentley
Get text shelving working
119
                if change[0] == 'modify text':
0.16.72 by Aaron Bentley
Allow shelving binary changes
120
                    try:
121
                        changes_shelved += self.handle_modify_text(creator,
122
                                                                   change[1])
123
                    except errors.BinaryFile:
124
                        if self.prompt_bool('Shelve binary changes?'):
125
                            changes_shelved += 1
126
                            creator.shelve_content_change(change[1])
0.16.16 by Aaron Bentley
Allow shelving renames and creation
127
                if change[0] == 'add file':
0.16.36 by Aaron Bentley
Better prompts on add/delete
128
                    if self.prompt_bool('Shelve adding file "%s"?'
129
                                        % change[3]):
0.16.16 by Aaron Bentley
Allow shelving renames and creation
130
                        creator.shelve_creation(change[1])
0.16.22 by Aaron Bentley
Only prompt when there are changes to shelve.
131
                        changes_shelved += 1
0.16.19 by Aaron Bentley
Implement shelving deletion
132
                if change[0] == 'delete file':
0.16.102 by Aaron Bentley
Minor updates
133
                    if self.prompt_bool('Shelve removing file "%s"?'
0.16.36 by Aaron Bentley
Better prompts on add/delete
134
                                        % change[3]):
0.16.19 by Aaron Bentley
Implement shelving deletion
135
                        creator.shelve_deletion(change[1])
0.16.22 by Aaron Bentley
Only prompt when there are changes to shelve.
136
                        changes_shelved += 1
0.16.72 by Aaron Bentley
Allow shelving binary changes
137
                if change[0] == 'change kind':
138
                    if self.prompt_bool('Shelve changing "%s" from %s to %s? '
139
                                        % (change[4], change[2], change[3])):
140
                        creator.shelve_content_change(change[1])
141
                        changes_shelved += 1
0.16.16 by Aaron Bentley
Allow shelving renames and creation
142
                if change[0] == 'rename':
0.16.102 by Aaron Bentley
Minor updates
143
                    if self.prompt_bool('Shelve renaming "%s" => "%s"?' %
0.16.23 by Aaron Bentley
Improve prompting
144
                                   change[2:]):
0.16.16 by Aaron Bentley
Allow shelving renames and creation
145
                        creator.shelve_rename(change[1])
0.16.22 by Aaron Bentley
Only prompt when there are changes to shelve.
146
                        changes_shelved += 1
4119.5.1 by James Westby
Shelve can now shelve changes to a symlink's target.
147
                if change[0] == 'modify target':
148
                    if self.prompt_bool('Shelve changing target of "%s" '
149
                            'from "%s" to "%s"?' % change[2:]):
150
                        creator.shelve_modify_target(change[1])
151
                        changes_shelved += 1
0.16.22 by Aaron Bentley
Only prompt when there are changes to shelve.
152
            if changes_shelved > 0:
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
153
                self.reporter.selected_changes(creator.work_transform)
0.16.98 by Aaron Bentley
Update docs and prompting
154
                if (self.auto_apply or self.prompt_bool(
155
                    'Shelve %d change(s)?' % changes_shelved)):
4100.3.1 by Aaron Bentley
Implement shelve --destroy
156
                    if self.destroy:
157
                        creator.transform()
4100.3.3 by Aaron Bentley
Add note when shelve --detroy completes.
158
                        trace.note('Selected changes destroyed.')
4100.3.1 by Aaron Bentley
Implement shelve --destroy
159
                    else:
160
                        shelf_id = self.manager.shelve_changes(creator,
161
                                                               self.message)
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
162
                        self.reporter.shelved_id(shelf_id)
0.16.22 by Aaron Bentley
Only prompt when there are changes to shelve.
163
            else:
4459.4.1 by Aaron Bentley
Provide control over switch and shelver messaging.
164
                self.reporter.no_changes()
0.16.1 by Aaron Bentley
Begin implementing UI
165
        finally:
0.16.5 by Aaron Bentley
Get text shelving working
166
            shutil.rmtree(self.tempdir)
0.16.1 by Aaron Bentley
Begin implementing UI
167
            creator.finalize()
168
169
    def get_parsed_patch(self, file_id):
0.16.98 by Aaron Bentley
Update docs and prompting
170
        """Return a parsed version of a file's patch.
171
172
        :param file_id: The id of the file to generate a patch for.
173
        :return: A patches.Patch.
174
        """
0.16.96 by Aaron Bentley
Fix shelving with renames
175
        old_path = self.target_tree.id2path(file_id)
176
        new_path = self.work_tree.id2path(file_id)
0.16.97 by Aaron Bentley
Turn diff_file and text_differ into instance variables.
177
        diff_file = StringIO()
178
        text_differ = diff.DiffText(self.target_tree, self.work_tree,
179
                                    diff_file)
180
        patch = text_differ.diff(file_id, old_path, new_path, 'file', 'file')
181
        diff_file.seek(0)
182
        return patches.parse_patch(diff_file)
0.16.1 by Aaron Bentley
Begin implementing UI
183
0.16.89 by Aaron Bentley
Add tests for Shelver
184
    def prompt(self, message):
0.16.98 by Aaron Bentley
Update docs and prompting
185
        """Prompt the user for a character.
186
187
        :param message: The message to prompt a user with.
188
        :return: A character.
189
        """
0.16.89 by Aaron Bentley
Add tests for Shelver
190
        sys.stdout.write(message)
191
        char = osutils.getchar()
192
        sys.stdout.write("\r" + ' ' * len(message) + '\r')
193
        sys.stdout.flush()
194
        return char
195
3990.4.3 by Daniel Watkins
Added help option to shelve prompt.
196
    def prompt_bool(self, question, long=False):
0.16.98 by Aaron Bentley
Update docs and prompting
197
        """Prompt the user with a yes/no question.
198
0.16.102 by Aaron Bentley
Minor updates
199
        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
200
        may also raise UserAbort.
0.16.98 by Aaron Bentley
Update docs and prompting
201
        :param question: The question to ask the user.
202
        :return: True or False
203
        """
204
        if self.auto:
0.16.23 by Aaron Bentley
Improve prompting
205
            return True
3990.4.3 by Daniel Watkins
Added help option to shelve prompt.
206
        if long:
207
            prompt = ' [(y)es, (N)o, (f)inish, or (q)uit]'
208
        else:
209
            prompt = ' [yNfq?]'
210
        char = self.prompt(question + prompt)
0.16.23 by Aaron Bentley
Improve prompting
211
        if char == 'y':
212
            return True
213
        elif char == 'f':
214
            self.auto = True
215
            return True
3990.4.3 by Daniel Watkins
Added help option to shelve prompt.
216
        elif char == '?':
217
            return self.prompt_bool(question, long=True)
0.16.24 by Aaron Bentley
Regularize prompts
218
        if char == 'q':
0.16.103 by Aaron Bentley
raise UserAbort instead of doing sys.exit
219
            raise errors.UserAbort()
0.16.23 by Aaron Bentley
Improve prompting
220
        else:
221
            return False
0.16.1 by Aaron Bentley
Begin implementing UI
222
0.16.5 by Aaron Bentley
Get text shelving working
223
    def handle_modify_text(self, creator, file_id):
0.16.98 by Aaron Bentley
Update docs and prompting
224
        """Provide diff hunk selection for modified text.
225
226
        :param creator: a ShelfCreator
227
        :param file_id: The id of the file to shelve.
228
        :return: number of shelved hunks.
229
        """
0.16.72 by Aaron Bentley
Allow shelving binary changes
230
        target_lines = self.target_tree.get_file_lines(file_id)
0.16.102 by Aaron Bentley
Minor updates
231
        textfile.check_text_lines(self.work_tree.get_file_lines(file_id))
0.16.72 by Aaron Bentley
Allow shelving binary changes
232
        textfile.check_text_lines(target_lines)
0.16.1 by Aaron Bentley
Begin implementing UI
233
        parsed = self.get_parsed_patch(file_id)
0.16.43 by Aaron Bentley
Reduce API friction.
234
        final_hunks = []
0.16.15 by Aaron Bentley
Implement auto mode
235
        if not self.auto:
0.16.41 by Aaron Bentley
Implement shelving with internal patch
236
            offset = 0
0.16.61 by Aaron Bentley
Show file name when shelving
237
            self.diff_writer.write(parsed.get_header())
0.16.15 by Aaron Bentley
Implement auto mode
238
            for hunk in parsed.hunks:
239
                self.diff_writer.write(str(hunk))
0.16.24 by Aaron Bentley
Regularize prompts
240
                if not self.prompt_bool('Shelve?'):
0.16.41 by Aaron Bentley
Implement shelving with internal patch
241
                    hunk.mod_pos += offset
0.16.43 by Aaron Bentley
Reduce API friction.
242
                    final_hunks.append(hunk)
0.16.41 by Aaron Bentley
Implement shelving with internal patch
243
                else:
244
                    offset -= (hunk.mod_range - hunk.orig_range)
0.16.68 by Aaron Bentley
Avoid having escape codes affect the wrong text.
245
        sys.stdout.flush()
0.16.62 by Aaron Bentley
Make status nicer by not shelving lines for files not being changed
246
        if len(parsed.hunks) == len(final_hunks):
247
            return 0
0.16.43 by Aaron Bentley
Reduce API friction.
248
        patched = patches.iter_patched_from_hunks(target_lines, final_hunks)
0.16.45 by Aaron Bentley
switch to shelve_lines
249
        creator.shelve_lines(file_id, list(patched))
0.16.43 by Aaron Bentley
Reduce API friction.
250
        return len(parsed.hunks) - len(final_hunks)
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
251
252
253
class Unshelver(object):
0.16.98 by Aaron Bentley
Update docs and prompting
254
    """Unshelve changes into a working tree."""
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
255
256
    @classmethod
0.16.94 by Aaron Bentley
Add unshelve tests
257
    def from_args(klass, shelf_id=None, action='apply', directory='.'):
0.16.98 by Aaron Bentley
Update docs and prompting
258
        """Create an unshelver from commandline arguments.
259
260
        :param shelf_id: Integer id of the shelf, as a string.
261
        :param action: action to perform.  May be 'apply', 'dry-run',
262
            'delete'.
263
        :param directory: The directory to unshelve changes into.
264
        """
0.16.94 by Aaron Bentley
Add unshelve tests
265
        tree, path = workingtree.WorkingTree.open_containing(directory)
0.16.40 by Aaron Bentley
Update for ShelfManager API changes
266
        manager = tree.get_shelf_manager()
0.16.52 by Aaron Bentley
Allow user-specified shelves
267
        if shelf_id is not None:
3990.2.4 by Daniel Watkins
Don't stack trace with an invalid shelf id.
268
            try:
269
                shelf_id = int(shelf_id)
270
            except ValueError:
271
                raise errors.InvalidShelfId(shelf_id)
0.16.52 by Aaron Bentley
Allow user-specified shelves
272
        else:
273
            shelf_id = manager.last_shelf()
274
            if shelf_id is None:
275
                raise errors.BzrCommandError('No changes are shelved.')
0.16.54 by Aaron Bentley
Inform user about shelf ids.
276
            trace.note('Unshelving changes with id "%d".' % shelf_id)
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
277
        apply_changes = True
278
        delete_shelf = True
0.16.65 by Aaron Bentley
Implement unshelve --delete
279
        read_shelf = True
280
        if action == 'dry-run':
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
281
            apply_changes = False
282
            delete_shelf = False
0.16.65 by Aaron Bentley
Implement unshelve --delete
283
        if action == 'delete-only':
284
            apply_changes = False
285
            read_shelf = False
286
        return klass(tree, manager, shelf_id, apply_changes, delete_shelf,
287
                     read_shelf)
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
288
0.16.94 by Aaron Bentley
Add unshelve tests
289
    def __init__(self, tree, manager, shelf_id, apply_changes=True,
290
                 delete_shelf=True, read_shelf=True):
0.16.98 by Aaron Bentley
Update docs and prompting
291
        """Constructor.
292
293
        :param tree: The working tree to unshelve into.
294
        :param manager: The ShelveManager containing the shelved changes.
295
        :param shelf_id:
296
        :param apply_changes: If True, apply the shelved changes to the
297
            working tree.
298
        :param delete_shelf: If True, delete the changes from the shelf.
299
        :param read_shelf: If True, read the changes from the shelf.
300
        """
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
301
        self.tree = tree
0.16.98 by Aaron Bentley
Update docs and prompting
302
        manager = tree.get_shelf_manager()
0.16.13 by Aaron Bentley
Appy shelve-management updates to shelver
303
        self.manager = manager
304
        self.shelf_id = shelf_id
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
305
        self.apply_changes = apply_changes
306
        self.delete_shelf = delete_shelf
0.16.65 by Aaron Bentley
Implement unshelve --delete
307
        self.read_shelf = read_shelf
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
308
309
    def run(self):
0.16.98 by Aaron Bentley
Update docs and prompting
310
        """Perform the unshelving operation."""
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
311
        self.tree.lock_write()
0.16.37 by Aaron Bentley
Use cleanups list to reduce nested try blocks
312
        cleanups = [self.tree.unlock]
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
313
        try:
0.16.65 by Aaron Bentley
Implement unshelve --delete
314
            if self.read_shelf:
315
                unshelver = self.manager.get_unshelver(self.shelf_id)
316
                cleanups.append(unshelver.finalize)
317
                if unshelver.message is not None:
318
                    trace.note('Message: %s' % unshelver.message)
319
                change_reporter = delta._ChangeReporter()
1551.21.7 by Aaron Bentley
Fix progress warning
320
                task = ui.ui_factory.nested_progress_bar()
321
                try:
322
                    merger = unshelver.make_merger(task)
323
                    merger.change_reporter = change_reporter
324
                    if self.apply_changes:
0.16.65 by Aaron Bentley
Implement unshelve --delete
325
                        merger.do_merge()
1551.21.7 by Aaron Bentley
Fix progress warning
326
                    else:
327
                        self.show_changes(merger)
328
                finally:
329
                    task.finished()
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
330
            if self.delete_shelf:
331
                self.manager.delete_shelf(self.shelf_id)
0.16.8 by Aaron Bentley
Implement unshelve2, tidy shelve2
332
        finally:
0.16.37 by Aaron Bentley
Use cleanups list to reduce nested try blocks
333
            for cleanup in reversed(cleanups):
334
                cleanup()
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
335
336
    def show_changes(self, merger):
0.16.98 by Aaron Bentley
Update docs and prompting
337
        """Show the changes that this operation specifies."""
0.16.64 by Aaron Bentley
Implement dry-run option for Unshelve
338
        tree_merger = merger.make_merger()
339
        # This implicitly shows the changes via the reporter, so we're done...
340
        tt = tree_merger.make_preview_transform()
341
        tt.finalize()