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