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