comparison hgext/mq.py @ 2748:752b9475a700

New mq command qfold: Merge patches into the current patch. Patches should be in the series file but not yet applied.
author Brendan Cully <brendan@kublai.com>
date Mon, 31 Jul 2006 20:33:56 -0700
parents 0016fc748f61
children 8c814c1ab31e 84218111e80f
comparison
equal deleted inserted replaced
2747:0016fc748f61 2748:752b9475a700
1
1 # queue.py - patch queues for mercurial 2 # queue.py - patch queues for mercurial
2 # 3 #
3 # Copyright 2005 Chris Mason <mason@suse.com> 4 # Copyright 2005 Chris Mason <mason@suse.com>
4 # 5 #
5 # This software may be used and distributed according to the terms 6 # This software may be used and distributed according to the terms
268 self.applied_dirty = 1 269 self.applied_dirty = 1
269 if err: 270 if err:
270 return (err, head) 271 return (err, head)
271 return (0, head) 272 return (0, head)
272 273
274 def patch(self, repo, patchfile):
275 '''Apply patchfile to the working directory.
276 patchfile: file name of patch'''
277 try:
278 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
279 f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
280 (pp, repo.root, patchfile))
281 except:
282 self.ui.warn("patch failed, unable to continue (try -v)\n")
283 return (None, [], False)
284 files = []
285 fuzz = False
286 for l in f:
287 l = l.rstrip('\r\n');
288 if self.ui.verbose:
289 self.ui.warn(l + "\n")
290 if l[:14] == 'patching file ':
291 pf = os.path.normpath(l[14:])
292 # when patch finds a space in the file name, it puts
293 # single quotes around the filename. strip them off
294 if pf[0] == "'" and pf[-1] == "'":
295 pf = pf[1:-1]
296 if pf not in files:
297 files.append(pf)
298 printed_file = False
299 file_str = l
300 elif l.find('with fuzz') >= 0:
301 if not printed_file:
302 self.ui.warn(file_str + '\n')
303 printed_file = True
304 self.ui.warn(l + '\n')
305 fuzz = True
306 elif l.find('saving rejects to file') >= 0:
307 self.ui.warn(l + '\n')
308 elif l.find('FAILED') >= 0:
309 if not printed_file:
310 self.ui.warn(file_str + '\n')
311 printed_file = True
312 self.ui.warn(l + '\n')
313
314 return (not f.close(), files, fuzz)
315
273 def apply(self, repo, series, list=False, update_status=True, 316 def apply(self, repo, series, list=False, update_status=True,
274 strict=False, patchdir=None, merge=None, wlock=None): 317 strict=False, patchdir=None, merge=None, wlock=None):
275 # TODO unify with commands.py 318 # TODO unify with commands.py
276 if not patchdir: 319 if not patchdir:
277 patchdir = self.path 320 patchdir = self.path
297 else: 340 else:
298 if list: 341 if list:
299 message.append("\nimported patch %s" % patch) 342 message.append("\nimported patch %s" % patch)
300 message = '\n'.join(message) 343 message = '\n'.join(message)
301 344
302 try: 345 (patcherr, files, fuzz) = self.patch(repo, pf)
303 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') 346 patcherr = not patcherr
304 f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
305 (pp, repo.root, pf))
306 except:
307 self.ui.warn("patch failed, unable to continue (try -v)\n")
308 err = 1
309 break
310 files = []
311 fuzz = False
312 for l in f:
313 l = l.rstrip('\r\n');
314 if self.ui.verbose:
315 self.ui.warn(l + "\n")
316 if l[:14] == 'patching file ':
317 pf = os.path.normpath(l[14:])
318 # when patch finds a space in the file name, it puts
319 # single quotes around the filename. strip them off
320 if pf[0] == "'" and pf[-1] == "'":
321 pf = pf[1:-1]
322 if pf not in files:
323 files.append(pf)
324 printed_file = False
325 file_str = l
326 elif l.find('with fuzz') >= 0:
327 if not printed_file:
328 self.ui.warn(file_str + '\n')
329 printed_file = True
330 self.ui.warn(l + '\n')
331 fuzz = True
332 elif l.find('saving rejects to file') >= 0:
333 self.ui.warn(l + '\n')
334 elif l.find('FAILED') >= 0:
335 if not printed_file:
336 self.ui.warn(file_str + '\n')
337 printed_file = True
338 self.ui.warn(l + '\n')
339 patcherr = f.close()
340 347
341 if merge and len(files) > 0: 348 if merge and len(files) > 0:
342 # Mark as merged and update dirstate parent info 349 # Mark as merged and update dirstate parent info
343 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm') 350 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
344 p1, p2 = repo.dirstate.parents() 351 p1, p2 = repo.dirstate.parents()
1287 """diff of the current patch""" 1294 """diff of the current patch"""
1288 # deep in the dirstate code, the walkhelper method wants a list, not a tuple 1295 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1289 repo.mq.diff(repo, list(files)) 1296 repo.mq.diff(repo, list(files))
1290 return 0 1297 return 0
1291 1298
1299 def fold(ui, repo, *files):
1300 """fold the named patches into the current patch
1301 Patches must not yet be applied."""
1302 q = repo.mq
1303
1304 if not files:
1305 raise util.Abort(_('qfold requires at least one patch name'))
1306 if not q.check_toppatch(repo):
1307 raise util.Abort(_('No patches applied\n'))
1308
1309 parent = q.lookup('qtip')
1310 patches = []
1311 messages = []
1312 for f in files:
1313 patch = q.lookup(f)
1314 if patch in patches or patch == parent:
1315 self.ui.warn(_('Skipping already folded patch %s') % patch)
1316 if q.isapplied(patch):
1317 raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
1318 patches.append(patch)
1319
1320 for patch in patches:
1321 messages.append(q.readheaders(patch)[0])
1322 pf = os.path.join(q.path, patch)
1323 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1324 if not patchsuccess:
1325 raise util.Abort(_('Error folding patch %s') % patch)
1326
1327 message = q.readheaders(parent)[0]
1328 for msg in messages:
1329 message.append('* * *')
1330 message.extend(msg)
1331 message = '\n'.join(message)
1332
1333 q.refresh(repo, msg=message)
1334
1335 for patch in patches:
1336 q.delete(repo, patch)
1337
1338 q.save_dirty()
1339
1292 def header(ui, repo, patch=None): 1340 def header(ui, repo, patch=None):
1293 """Print the header of the topmost or specified patch""" 1341 """Print the header of the topmost or specified patch"""
1294 q = repo.mq 1342 q = repo.mq
1295 1343
1296 if patch: 1344 if patch:
1460 (commit, 1508 (commit,
1461 commands.table["^commit|ci"][1], 1509 commands.table["^commit|ci"][1],
1462 'hg qcommit [OPTION]... [FILE]...'), 1510 'hg qcommit [OPTION]... [FILE]...'),
1463 "^qdiff": (diff, [], 'hg qdiff [FILE]...'), 1511 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1464 "qdelete": (delete, [], 'hg qdelete PATCH'), 1512 "qdelete": (delete, [], 'hg qdelete PATCH'),
1513 'qfold': (fold, [], 'hg qfold PATCH...'),
1465 'qheader': (header, [], 1514 'qheader': (header, [],
1466 _('hg qheader [PATCH]')), 1515 _('hg qheader [PATCH]')),
1467 "^qimport": 1516 "^qimport":
1468 (qimport, 1517 (qimport,
1469 [('e', 'existing', None, 'import file in patch dir'), 1518 [('e', 'existing', None, 'import file in patch dir'),