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