1
# Copyright (C) 2008 Aaron Bentley <aaron@aaronbentley.com>
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.
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.
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
18
from cStringIO import StringIO
36
from bzrlib.plugins.bzrtools import colordiff
41
class Shelver(object):
43
def __init__(self, work_tree, target_tree, path, auto=False,
44
auto_apply=False, file_list=None, message=None):
45
self.work_tree = work_tree
46
self.target_tree = target_tree
48
self.diff_file = StringIO()
49
self.text_differ = diff.DiffText(self.target_tree, self.work_tree,
51
if colordiff is not None:
52
self.diff_writer = colordiff.DiffWriter(sys.stdout, False)
54
self.diff_writer = sys.stdout
55
self.manager = work_tree.get_shelf_manager()
57
self.auto_apply = auto_apply
58
self.file_list = file_list
59
self.message = message
62
def from_args(klass, revision=None, all=False, file_list=None,
64
tree, path = workingtree.WorkingTree.open_containing('.')
65
target_tree = builtins._get_one_revision_tree('shelf2', revision,
67
return klass(tree, target_tree, path, all, all, file_list, message)
70
creator = shelf.ShelfCreator(self.work_tree, self.target_tree,
72
self.tempdir = tempfile.mkdtemp()
75
for change in creator:
76
if change[0] == 'modify text':
78
changes_shelved += self.handle_modify_text(creator,
80
except errors.BinaryFile:
81
if self.prompt_bool('Shelve binary changes?'):
83
creator.shelve_content_change(change[1])
84
if change[0] == 'add file':
85
if self.prompt_bool('Shelve adding file "%s"?'
87
creator.shelve_creation(change[1])
89
if change[0] == 'delete file':
90
if self.prompt_bool('Shelve removing file "%s"? '
92
creator.shelve_deletion(change[1])
94
if change[0] == 'change kind':
95
if self.prompt_bool('Shelve changing "%s" from %s to %s? '
96
% (change[4], change[2], change[3])):
97
creator.shelve_content_change(change[1])
99
if change[0] == 'rename':
100
if self.prompt_bool('Shelve renaming %s => %s?' %
102
creator.shelve_rename(change[1])
104
if changes_shelved > 0:
105
trace.note("Selected changes:")
106
changes = creator.work_transform.iter_changes()
107
reporter = delta._ChangeReporter()
108
delta.report_changes(changes, reporter)
109
if (self.prompt_bool('Shelve %d change(s)?' %
110
changes_shelved, auto=self.auto_apply)):
111
shelf_id = self.manager.shelve_changes(creator,
113
trace.note('Changes shelved with id "%d".' % shelf_id)
115
trace.warning('No changes to shelve.')
117
shutil.rmtree(self.tempdir)
120
def get_parsed_patch(self, file_id):
121
old_path = self.work_tree.id2path(file_id)
122
new_path = self.target_tree.id2path(file_id)
124
patch = self.text_differ.diff(file_id, old_path, new_path, 'file',
126
self.diff_file.seek(0)
127
return patches.parse_patch(self.diff_file)
129
self.diff_file.truncate(0)
131
def prompt_bool(self, question, auto=None):
136
message = question + ' [yNfq]'
137
sys.stdout.write(message)
138
char = osutils.getchar()
139
sys.stdout.write("\r" + ' ' * len(message) + '\r')
151
def handle_modify_text(self, creator, file_id):
152
target_lines = self.target_tree.get_file_lines(file_id)
153
work_file = self.work_tree.get_file(file_id)
155
textfile.text_file(work_file)
158
textfile.check_text_lines(target_lines)
159
parsed = self.get_parsed_patch(file_id)
163
self.diff_writer.write(parsed.get_header())
164
for hunk in parsed.hunks:
165
self.diff_writer.write(str(hunk))
166
if not self.prompt_bool('Shelve?'):
167
hunk.mod_pos += offset
168
final_hunks.append(hunk)
170
offset -= (hunk.mod_range - hunk.orig_range)
172
if len(parsed.hunks) == len(final_hunks):
174
patched = patches.iter_patched_from_hunks(target_lines, final_hunks)
175
creator.shelve_lines(file_id, list(patched))
176
return len(parsed.hunks) - len(final_hunks)
179
class Unshelver(object):
182
def from_args(klass, shelf_id=None, action='apply'):
183
tree, path = workingtree.WorkingTree.open_containing('.')
184
manager = tree.get_shelf_manager()
185
if shelf_id is not None:
186
shelf_id = int(shelf_id)
188
shelf_id = manager.last_shelf()
190
raise errors.BzrCommandError('No changes are shelved.')
191
trace.note('Unshelving changes with id "%d".' % shelf_id)
195
if action == 'dry-run':
196
apply_changes = False
198
if action == 'delete-only':
199
apply_changes = False
201
return klass(tree, manager, shelf_id, apply_changes, delete_shelf,
204
def __init__(self, tree, manager, shelf_id, apply_changes, delete_shelf,
207
self.manager = manager
208
self.shelf_id = shelf_id
209
self.apply_changes = apply_changes
210
self.delete_shelf = delete_shelf
211
self.read_shelf = read_shelf
214
self.tree.lock_write()
215
cleanups = [self.tree.unlock]
218
unshelver = self.manager.get_unshelver(self.shelf_id)
219
cleanups.append(unshelver.finalize)
220
if unshelver.message is not None:
221
trace.note('Message: %s' % unshelver.message)
222
change_reporter = delta._ChangeReporter()
223
merger = unshelver.make_merger()
224
merger.change_reporter = change_reporter
225
if self.apply_changes:
226
pb = ui.ui_factory.nested_progress_bar()
232
self.show_changes(merger)
233
if self.delete_shelf:
234
self.manager.delete_shelf(self.shelf_id)
236
for cleanup in reversed(cleanups):
239
def show_changes(self, merger):
240
tree_merger = merger.make_merger()
241
# This implicitly shows the changes via the reporter, so we're done...
242
tt = tree_merger.make_preview_transform()