6
4
from datetime import datetime
7
from errors import CommandError, PatchFailed
5
from errors import CommandError, PatchFailed, PatchInvokeError
8
6
from hunk_selector import ShelveHunkSelector, UnshelveHunkSelector
9
7
from patchsource import PatchSource, FilePatchSource
8
from bzrlib.osutils import rename
11
10
class Shelf(object):
12
11
MESSAGE_PREFIX = "# Shelved patch: "
57
56
def delete(self, patch):
58
57
path = self.__path_from_user(patch)
58
rename(path, '%s~' % path)
61
def display(self, patch):
62
path = self.__path_from_user(patch)
60
def display(self, patch=None):
62
path = self.last_patch()
64
path = self.__path_from_user(patch)
63
65
sys.stdout.write(open(path).read())
78
80
def __path_from_user(self, patch_id):
80
82
patch_index = int(patch_id)
83
except (TypeError, ValueError):
82
84
raise CommandError("Invalid patch name '%s'" % patch_id)
84
86
path = self.__path(patch_index)
131
133
return patch[len(self.MESSAGE_PREFIX):patch.index('\n')]
133
def unshelve(self, patch_source, all_hunks=False, force=False):
135
def unshelve(self, patch_source, patch_name=None, all=False, force=False,
134
137
self._check_upgrade()
136
patch_name = self.last_patch()
139
if no_color is False:
138
143
if patch_name is None:
144
patch_path = self.last_patch()
146
patch_path = self.__path_from_user(patch_name)
148
if patch_path is None:
139
149
raise CommandError("No patch found on shelf %s" % self.name)
141
hunks = FilePatchSource(patch_name).readhunks()
151
patches = FilePatchSource(patch_path).readpatches()
153
to_unshelve = patches
146
to_unshelve, to_remain = UnshelveHunkSelector(hunks).select()
156
hs = UnshelveHunkSelector(patches, color)
157
to_unshelve, to_remain = hs.select()
148
159
if len(to_unshelve) == 0:
149
160
raise CommandError('Nothing to unshelve')
151
message = self.get_patch_message(patch_name)
162
message = self.get_patch_message(patch_path)
152
163
if message is None:
153
164
message = "No message saved with patch."
154
165
self.log('Unshelving from %s/%s: "%s"\n' % \
155
(self.name, os.path.basename(patch_name), message))
166
(self.name, os.path.basename(patch_path), message))
158
169
self._run_patch(to_unshelve, dry_run=True)
159
170
self._run_patch(to_unshelve)
160
171
except PatchFailed:
162
self._run_patch(to_unshelve, strip=0, dry_run=True)
163
self._run_patch(to_unshelve, strip=0)
173
self._run_patch(to_unshelve, strip=1, dry_run=True)
174
self._run_patch(to_unshelve, strip=1)
164
175
except PatchFailed:
166
177
self.log('Warning: Unshelving failed, forcing as ' \
174
185
"longer applies cleanly to the working tree!")
176
187
# Backup the shelved patch
177
os.rename(patch_name, '%s~' % patch_name)
188
rename(patch_path, '%s~' % patch_path)
179
190
if len(to_remain) > 0:
180
f = open(patch_name, 'w')
181
for hunk in to_remain:
191
f = open(patch_path, 'w')
192
for patch in to_remain:
185
def shelve(self, patch_source, all_hunks=False, message=None):
196
def shelve(self, patch_source, all=False, message=None, no_color=False):
186
197
self._check_upgrade()
188
hunks = patch_source.readhunks()
193
to_shelve = ShelveHunkSelector(hunks).select()[0]
198
if no_color is False:
203
patches = patch_source.readpatches()
208
to_shelve = ShelveHunkSelector(patches, color).select()[0]
195
210
if len(to_shelve) == 0:
196
211
raise CommandError('Nothing to shelve')
199
214
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
200
215
message = "Changes shelved on %s" % timestamp
202
patch_name = self.next_patch()
217
patch_path = self.next_patch()
203
218
self.log('Shelving to %s/%s: "%s"\n' % \
204
(self.name, os.path.basename(patch_name), message))
219
(self.name, os.path.basename(patch_path), message))
206
patch = open(patch_name, 'a')
221
f = open(patch_path, 'a')
208
223
assert '\n' not in message
209
patch.write("%s%s\n" % (self.MESSAGE_PREFIX, message))
211
for hunk in to_shelve:
212
patch.write(str(hunk))
215
os.fsync(patch.fileno())
224
f.write("%s%s\n" % (self.MESSAGE_PREFIX, message))
226
for patch in to_shelve:
219
234
self._run_patch(to_shelve, reverse=True, dry_run=True)
220
235
self._run_patch(to_shelve, reverse=True)
221
236
except PatchFailed:
223
self._run_patch(to_shelve, reverse=True, strip=0, dry_run=True)
224
self._run_patch(to_shelve, reverse=True, strip=0)
238
self._run_patch(to_shelve, reverse=True, strip=1, dry_run=True)
239
self._run_patch(to_shelve, reverse=True, strip=1)
225
240
except PatchFailed:
226
241
raise CommandError("Failed removing shelved changes from the"
229
def _run_patch(self, patches, strip=1, reverse=False, dry_run=False):
244
def _run_patch(self, patches, strip=0, reverse=False, dry_run=False):
230
245
args = ['patch', '-d', self.base, '-s', '-p%d' % strip, '-f']
247
if sys.platform == "win32":
248
args.append('--binary')
232
251
args.append('-R')
237
256
stdout = stderr = None
239
process = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=stdout,
241
for patch in patches:
242
process.stdin.write(str(patch))
259
process = subprocess.Popen(args, stdin=subprocess.PIPE,
260
stdout=stdout, stderr=stderr)
261
for patch in patches:
262
process.stdin.write(str(patch))
265
raise PatchInvokeError(e, process.stderr.read())
244
267
process.communicate()
305
328
self.log('Copied %s to %s/%s\n' % (os.path.basename(patch),
306
329
self.name, os.path.basename(new_path)))
307
os.rename(patch, patch + '~')
330
rename(patch, patch + '~')