##// END OF EJS Templates
record: use commands.diffwsopts instead of ad-hoc diffopts...
Jordi Gutiérrez Hermoso -
r20300:00766430 default
parent child Browse files
Show More
@@ -1,679 +1,670 b''
1 1 # record.py
2 2 #
3 3 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''commands to interactively select changes for commit/qrefresh'''
9 9
10 10 from mercurial.i18n import _
11 11 from mercurial import cmdutil, commands, extensions, hg, patch
12 12 from mercurial import util
13 13 import copy, cStringIO, errno, os, re, shutil, tempfile
14 14
15 15 cmdtable = {}
16 16 command = cmdutil.command(cmdtable)
17 17 testedwith = 'internal'
18 18
19 19 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
20 20
21 diffopts = [
22 ('w', 'ignore-all-space', False,
23 _('ignore white space when comparing lines')),
24 ('b', 'ignore-space-change', None,
25 _('ignore changes in the amount of white space')),
26 ('B', 'ignore-blank-lines', None,
27 _('ignore changes whose lines are all blank')),
28 ]
29
30 21 def scanpatch(fp):
31 22 """like patch.iterhunks, but yield different events
32 23
33 24 - ('file', [header_lines + fromfile + tofile])
34 25 - ('context', [context_lines])
35 26 - ('hunk', [hunk_lines])
36 27 - ('range', (-start,len, +start,len, proc))
37 28 """
38 29 lr = patch.linereader(fp)
39 30
40 31 def scanwhile(first, p):
41 32 """scan lr while predicate holds"""
42 33 lines = [first]
43 34 while True:
44 35 line = lr.readline()
45 36 if not line:
46 37 break
47 38 if p(line):
48 39 lines.append(line)
49 40 else:
50 41 lr.push(line)
51 42 break
52 43 return lines
53 44
54 45 while True:
55 46 line = lr.readline()
56 47 if not line:
57 48 break
58 49 if line.startswith('diff --git a/') or line.startswith('diff -r '):
59 50 def notheader(line):
60 51 s = line.split(None, 1)
61 52 return not s or s[0] not in ('---', 'diff')
62 53 header = scanwhile(line, notheader)
63 54 fromfile = lr.readline()
64 55 if fromfile.startswith('---'):
65 56 tofile = lr.readline()
66 57 header += [fromfile, tofile]
67 58 else:
68 59 lr.push(fromfile)
69 60 yield 'file', header
70 61 elif line[0] == ' ':
71 62 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
72 63 elif line[0] in '-+':
73 64 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
74 65 else:
75 66 m = lines_re.match(line)
76 67 if m:
77 68 yield 'range', m.groups()
78 69 else:
79 70 yield 'other', line
80 71
81 72 class header(object):
82 73 """patch header
83 74
84 75 XXX shouldn't we move this to mercurial/patch.py ?
85 76 """
86 77 diffgit_re = re.compile('diff --git a/(.*) b/(.*)$')
87 78 diff_re = re.compile('diff -r .* (.*)$')
88 79 allhunks_re = re.compile('(?:index|new file|deleted file) ')
89 80 pretty_re = re.compile('(?:new file|deleted file) ')
90 81 special_re = re.compile('(?:index|new|deleted|copy|rename) ')
91 82
92 83 def __init__(self, header):
93 84 self.header = header
94 85 self.hunks = []
95 86
96 87 def binary(self):
97 88 return util.any(h.startswith('index ') for h in self.header)
98 89
99 90 def pretty(self, fp):
100 91 for h in self.header:
101 92 if h.startswith('index '):
102 93 fp.write(_('this modifies a binary file (all or nothing)\n'))
103 94 break
104 95 if self.pretty_re.match(h):
105 96 fp.write(h)
106 97 if self.binary():
107 98 fp.write(_('this is a binary file\n'))
108 99 break
109 100 if h.startswith('---'):
110 101 fp.write(_('%d hunks, %d lines changed\n') %
111 102 (len(self.hunks),
112 103 sum([max(h.added, h.removed) for h in self.hunks])))
113 104 break
114 105 fp.write(h)
115 106
116 107 def write(self, fp):
117 108 fp.write(''.join(self.header))
118 109
119 110 def allhunks(self):
120 111 return util.any(self.allhunks_re.match(h) for h in self.header)
121 112
122 113 def files(self):
123 114 match = self.diffgit_re.match(self.header[0])
124 115 if match:
125 116 fromfile, tofile = match.groups()
126 117 if fromfile == tofile:
127 118 return [fromfile]
128 119 return [fromfile, tofile]
129 120 else:
130 121 return self.diff_re.match(self.header[0]).groups()
131 122
132 123 def filename(self):
133 124 return self.files()[-1]
134 125
135 126 def __repr__(self):
136 127 return '<header %s>' % (' '.join(map(repr, self.files())))
137 128
138 129 def special(self):
139 130 return util.any(self.special_re.match(h) for h in self.header)
140 131
141 132 def countchanges(hunk):
142 133 """hunk -> (n+,n-)"""
143 134 add = len([h for h in hunk if h[0] == '+'])
144 135 rem = len([h for h in hunk if h[0] == '-'])
145 136 return add, rem
146 137
147 138 class hunk(object):
148 139 """patch hunk
149 140
150 141 XXX shouldn't we merge this with patch.hunk ?
151 142 """
152 143 maxcontext = 3
153 144
154 145 def __init__(self, header, fromline, toline, proc, before, hunk, after):
155 146 def trimcontext(number, lines):
156 147 delta = len(lines) - self.maxcontext
157 148 if False and delta > 0:
158 149 return number + delta, lines[:self.maxcontext]
159 150 return number, lines
160 151
161 152 self.header = header
162 153 self.fromline, self.before = trimcontext(fromline, before)
163 154 self.toline, self.after = trimcontext(toline, after)
164 155 self.proc = proc
165 156 self.hunk = hunk
166 157 self.added, self.removed = countchanges(self.hunk)
167 158
168 159 def write(self, fp):
169 160 delta = len(self.before) + len(self.after)
170 161 if self.after and self.after[-1] == '\\ No newline at end of file\n':
171 162 delta -= 1
172 163 fromlen = delta + self.removed
173 164 tolen = delta + self.added
174 165 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
175 166 (self.fromline, fromlen, self.toline, tolen,
176 167 self.proc and (' ' + self.proc)))
177 168 fp.write(''.join(self.before + self.hunk + self.after))
178 169
179 170 pretty = write
180 171
181 172 def filename(self):
182 173 return self.header.filename()
183 174
184 175 def __repr__(self):
185 176 return '<hunk %r@%d>' % (self.filename(), self.fromline)
186 177
187 178 def parsepatch(fp):
188 179 """patch -> [] of headers -> [] of hunks """
189 180 class parser(object):
190 181 """patch parsing state machine"""
191 182 def __init__(self):
192 183 self.fromline = 0
193 184 self.toline = 0
194 185 self.proc = ''
195 186 self.header = None
196 187 self.context = []
197 188 self.before = []
198 189 self.hunk = []
199 190 self.headers = []
200 191
201 192 def addrange(self, limits):
202 193 fromstart, fromend, tostart, toend, proc = limits
203 194 self.fromline = int(fromstart)
204 195 self.toline = int(tostart)
205 196 self.proc = proc
206 197
207 198 def addcontext(self, context):
208 199 if self.hunk:
209 200 h = hunk(self.header, self.fromline, self.toline, self.proc,
210 201 self.before, self.hunk, context)
211 202 self.header.hunks.append(h)
212 203 self.fromline += len(self.before) + h.removed
213 204 self.toline += len(self.before) + h.added
214 205 self.before = []
215 206 self.hunk = []
216 207 self.proc = ''
217 208 self.context = context
218 209
219 210 def addhunk(self, hunk):
220 211 if self.context:
221 212 self.before = self.context
222 213 self.context = []
223 214 self.hunk = hunk
224 215
225 216 def newfile(self, hdr):
226 217 self.addcontext([])
227 218 h = header(hdr)
228 219 self.headers.append(h)
229 220 self.header = h
230 221
231 222 def addother(self, line):
232 223 pass # 'other' lines are ignored
233 224
234 225 def finished(self):
235 226 self.addcontext([])
236 227 return self.headers
237 228
238 229 transitions = {
239 230 'file': {'context': addcontext,
240 231 'file': newfile,
241 232 'hunk': addhunk,
242 233 'range': addrange},
243 234 'context': {'file': newfile,
244 235 'hunk': addhunk,
245 236 'range': addrange,
246 237 'other': addother},
247 238 'hunk': {'context': addcontext,
248 239 'file': newfile,
249 240 'range': addrange},
250 241 'range': {'context': addcontext,
251 242 'hunk': addhunk},
252 243 'other': {'other': addother},
253 244 }
254 245
255 246 p = parser()
256 247
257 248 state = 'context'
258 249 for newstate, data in scanpatch(fp):
259 250 try:
260 251 p.transitions[state][newstate](p, data)
261 252 except KeyError:
262 253 raise patch.PatchError('unhandled transition: %s -> %s' %
263 254 (state, newstate))
264 255 state = newstate
265 256 return p.finished()
266 257
267 258 def filterpatch(ui, headers):
268 259 """Interactively filter patch chunks into applied-only chunks"""
269 260
270 261 def prompt(skipfile, skipall, query, chunk):
271 262 """prompt query, and process base inputs
272 263
273 264 - y/n for the rest of file
274 265 - y/n for the rest
275 266 - ? (help)
276 267 - q (quit)
277 268
278 269 Return True/False and possibly updated skipfile and skipall.
279 270 """
280 271 newpatches = None
281 272 if skipall is not None:
282 273 return skipall, skipfile, skipall, newpatches
283 274 if skipfile is not None:
284 275 return skipfile, skipfile, skipall, newpatches
285 276 while True:
286 277 resps = _('[Ynesfdaq?]'
287 278 '$$ &Yes, record this change'
288 279 '$$ &No, skip this change'
289 280 '$$ &Edit this change manually'
290 281 '$$ &Skip remaining changes to this file'
291 282 '$$ Record remaining changes to this &file'
292 283 '$$ &Done, skip remaining changes and files'
293 284 '$$ Record &all changes to all remaining files'
294 285 '$$ &Quit, recording no changes'
295 286 '$$ &? (display help)')
296 287 r = ui.promptchoice("%s %s" % (query, resps))
297 288 ui.write("\n")
298 289 if r == 8: # ?
299 290 for c, t in ui.extractchoices(resps)[1]:
300 291 ui.write('%s - %s\n' % (c, t.lower()))
301 292 continue
302 293 elif r == 0: # yes
303 294 ret = True
304 295 elif r == 1: # no
305 296 ret = False
306 297 elif r == 2: # Edit patch
307 298 if chunk is None:
308 299 ui.write(_('cannot edit patch for whole file'))
309 300 ui.write("\n")
310 301 continue
311 302 if chunk.header.binary():
312 303 ui.write(_('cannot edit patch for binary file'))
313 304 ui.write("\n")
314 305 continue
315 306 # Patch comment based on the Git one (based on comment at end of
316 307 # http://mercurial.selenic.com/wiki/RecordExtension)
317 308 phelp = '---' + _("""
318 309 To remove '-' lines, make them ' ' lines (context).
319 310 To remove '+' lines, delete them.
320 311 Lines starting with # will be removed from the patch.
321 312
322 313 If the patch applies cleanly, the edited hunk will immediately be
323 314 added to the record list. If it does not apply cleanly, a rejects
324 315 file will be generated: you can use that when you try again. If
325 316 all lines of the hunk are removed, then the edit is aborted and
326 317 the hunk is left unchanged.
327 318 """)
328 319 (patchfd, patchfn) = tempfile.mkstemp(prefix="hg-editor-",
329 320 suffix=".diff", text=True)
330 321 ncpatchfp = None
331 322 try:
332 323 # Write the initial patch
333 324 f = os.fdopen(patchfd, "w")
334 325 chunk.header.write(f)
335 326 chunk.write(f)
336 327 f.write('\n'.join(['# ' + i for i in phelp.splitlines()]))
337 328 f.close()
338 329 # Start the editor and wait for it to complete
339 330 editor = ui.geteditor()
340 331 util.system("%s \"%s\"" % (editor, patchfn),
341 332 environ={'HGUSER': ui.username()},
342 333 onerr=util.Abort, errprefix=_("edit failed"),
343 334 out=ui.fout)
344 335 # Remove comment lines
345 336 patchfp = open(patchfn)
346 337 ncpatchfp = cStringIO.StringIO()
347 338 for line in patchfp:
348 339 if not line.startswith('#'):
349 340 ncpatchfp.write(line)
350 341 patchfp.close()
351 342 ncpatchfp.seek(0)
352 343 newpatches = parsepatch(ncpatchfp)
353 344 finally:
354 345 os.unlink(patchfn)
355 346 del ncpatchfp
356 347 # Signal that the chunk shouldn't be applied as-is, but
357 348 # provide the new patch to be used instead.
358 349 ret = False
359 350 elif r == 3: # Skip
360 351 ret = skipfile = False
361 352 elif r == 4: # file (Record remaining)
362 353 ret = skipfile = True
363 354 elif r == 5: # done, skip remaining
364 355 ret = skipall = False
365 356 elif r == 6: # all
366 357 ret = skipall = True
367 358 elif r == 7: # quit
368 359 raise util.Abort(_('user quit'))
369 360 return ret, skipfile, skipall, newpatches
370 361
371 362 seen = set()
372 363 applied = {} # 'filename' -> [] of chunks
373 364 skipfile, skipall = None, None
374 365 pos, total = 1, sum(len(h.hunks) for h in headers)
375 366 for h in headers:
376 367 pos += len(h.hunks)
377 368 skipfile = None
378 369 fixoffset = 0
379 370 hdr = ''.join(h.header)
380 371 if hdr in seen:
381 372 continue
382 373 seen.add(hdr)
383 374 if skipall is None:
384 375 h.pretty(ui)
385 376 msg = (_('examine changes to %s?') %
386 377 _(' and ').join("'%s'" % f for f in h.files()))
387 378 r, skipfile, skipall, np = prompt(skipfile, skipall, msg, None)
388 379 if not r:
389 380 continue
390 381 applied[h.filename()] = [h]
391 382 if h.allhunks():
392 383 applied[h.filename()] += h.hunks
393 384 continue
394 385 for i, chunk in enumerate(h.hunks):
395 386 if skipfile is None and skipall is None:
396 387 chunk.pretty(ui)
397 388 if total == 1:
398 389 msg = _("record this change to '%s'?") % chunk.filename()
399 390 else:
400 391 idx = pos - len(h.hunks) + i
401 392 msg = _("record change %d/%d to '%s'?") % (idx, total,
402 393 chunk.filename())
403 394 r, skipfile, skipall, newpatches = prompt(skipfile,
404 395 skipall, msg, chunk)
405 396 if r:
406 397 if fixoffset:
407 398 chunk = copy.copy(chunk)
408 399 chunk.toline += fixoffset
409 400 applied[chunk.filename()].append(chunk)
410 401 elif newpatches is not None:
411 402 for newpatch in newpatches:
412 403 for newhunk in newpatch.hunks:
413 404 if fixoffset:
414 405 newhunk.toline += fixoffset
415 406 applied[newhunk.filename()].append(newhunk)
416 407 else:
417 408 fixoffset += chunk.removed - chunk.added
418 409 return sum([h for h in applied.itervalues()
419 410 if h[0].special() or len(h) > 1], [])
420 411
421 412 @command("record",
422 413 # same options as commit + white space diff options
423 commands.table['^commit|ci'][1][:] + diffopts,
414 commands.table['^commit|ci'][1][:] + commands.diffwsopts,
424 415 _('hg record [OPTION]... [FILE]...'))
425 416 def record(ui, repo, *pats, **opts):
426 417 '''interactively select changes to commit
427 418
428 419 If a list of files is omitted, all changes reported by :hg:`status`
429 420 will be candidates for recording.
430 421
431 422 See :hg:`help dates` for a list of formats valid for -d/--date.
432 423
433 424 You will be prompted for whether to record changes to each
434 425 modified file, and for files with multiple changes, for each
435 426 change to use. For each query, the following responses are
436 427 possible::
437 428
438 429 y - record this change
439 430 n - skip this change
440 431 e - edit this change manually
441 432
442 433 s - skip remaining changes to this file
443 434 f - record remaining changes to this file
444 435
445 436 d - done, skip remaining changes and files
446 437 a - record all changes to all remaining files
447 438 q - quit, recording no changes
448 439
449 440 ? - display help
450 441
451 442 This command is not available when committing a merge.'''
452 443
453 444 dorecord(ui, repo, commands.commit, 'commit', False, *pats, **opts)
454 445
455 446 def qrefresh(origfn, ui, repo, *pats, **opts):
456 447 if not opts['interactive']:
457 448 return origfn(ui, repo, *pats, **opts)
458 449
459 450 mq = extensions.find('mq')
460 451
461 452 def committomq(ui, repo, *pats, **opts):
462 453 # At this point the working copy contains only changes that
463 454 # were accepted. All other changes were reverted.
464 455 # We can't pass *pats here since qrefresh will undo all other
465 456 # changed files in the patch that aren't in pats.
466 457 mq.refresh(ui, repo, **opts)
467 458
468 459 # backup all changed files
469 460 dorecord(ui, repo, committomq, 'qrefresh', True, *pats, **opts)
470 461
471 462 def qrecord(ui, repo, patch, *pats, **opts):
472 463 '''interactively record a new patch
473 464
474 465 See :hg:`help qnew` & :hg:`help record` for more information and
475 466 usage.
476 467 '''
477 468
478 469 try:
479 470 mq = extensions.find('mq')
480 471 except KeyError:
481 472 raise util.Abort(_("'mq' extension not loaded"))
482 473
483 474 repo.mq.checkpatchname(patch)
484 475
485 476 def committomq(ui, repo, *pats, **opts):
486 477 opts['checkname'] = False
487 478 mq.new(ui, repo, patch, *pats, **opts)
488 479
489 480 dorecord(ui, repo, committomq, 'qnew', False, *pats, **opts)
490 481
491 482 def qnew(origfn, ui, repo, patch, *args, **opts):
492 483 if opts['interactive']:
493 484 return qrecord(ui, repo, patch, *args, **opts)
494 485 return origfn(ui, repo, patch, *args, **opts)
495 486
496 487 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall, *pats, **opts):
497 488 if not ui.interactive():
498 489 raise util.Abort(_('running non-interactively, use %s instead') %
499 490 cmdsuggest)
500 491
501 492 # make sure username is set before going interactive
502 493 if not opts.get('user'):
503 494 ui.username() # raise exception, username not provided
504 495
505 496 def recordfunc(ui, repo, message, match, opts):
506 497 """This is generic record driver.
507 498
508 499 Its job is to interactively filter local changes, and
509 500 accordingly prepare working directory into a state in which the
510 501 job can be delegated to a non-interactive commit command such as
511 502 'commit' or 'qrefresh'.
512 503
513 504 After the actual job is done by non-interactive command, the
514 505 working directory is restored to its original state.
515 506
516 507 In the end we'll record interesting changes, and everything else
517 508 will be left in place, so the user can continue working.
518 509 """
519 510
520 511 cmdutil.checkunfinished(repo, commit=True)
521 512 merge = len(repo[None].parents()) > 1
522 513 if merge:
523 514 raise util.Abort(_('cannot partially commit a merge '
524 515 '(use "hg commit" instead)'))
525 516
526 517 changes = repo.status(match=match)[:3]
527 518 diffopts = patch.diffopts(ui, opts=dict(
528 519 git=True, nodates=True,
529 520 ignorews=opts.get('ignore_all_space'),
530 521 ignorewsamount=opts.get('ignore_space_change'),
531 522 ignoreblanklines=opts.get('ignore_blank_lines')))
532 523 chunks = patch.diff(repo, changes=changes, opts=diffopts)
533 524 fp = cStringIO.StringIO()
534 525 fp.write(''.join(chunks))
535 526 fp.seek(0)
536 527
537 528 # 1. filter patch, so we have intending-to apply subset of it
538 529 try:
539 530 chunks = filterpatch(ui, parsepatch(fp))
540 531 except patch.PatchError, err:
541 532 raise util.Abort(_('error parsing patch: %s') % err)
542 533
543 534 del fp
544 535
545 536 contenders = set()
546 537 for h in chunks:
547 538 try:
548 539 contenders.update(set(h.files()))
549 540 except AttributeError:
550 541 pass
551 542
552 543 changed = changes[0] + changes[1] + changes[2]
553 544 newfiles = [f for f in changed if f in contenders]
554 545 if not newfiles:
555 546 ui.status(_('no changes to record\n'))
556 547 return 0
557 548
558 549 modified = set(changes[0])
559 550
560 551 # 2. backup changed files, so we can restore them in the end
561 552 if backupall:
562 553 tobackup = changed
563 554 else:
564 555 tobackup = [f for f in newfiles if f in modified]
565 556
566 557 backups = {}
567 558 if tobackup:
568 559 backupdir = repo.join('record-backups')
569 560 try:
570 561 os.mkdir(backupdir)
571 562 except OSError, err:
572 563 if err.errno != errno.EEXIST:
573 564 raise
574 565 try:
575 566 # backup continues
576 567 for f in tobackup:
577 568 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
578 569 dir=backupdir)
579 570 os.close(fd)
580 571 ui.debug('backup %r as %r\n' % (f, tmpname))
581 572 util.copyfile(repo.wjoin(f), tmpname)
582 573 shutil.copystat(repo.wjoin(f), tmpname)
583 574 backups[f] = tmpname
584 575
585 576 fp = cStringIO.StringIO()
586 577 for c in chunks:
587 578 if c.filename() in backups:
588 579 c.write(fp)
589 580 dopatch = fp.tell()
590 581 fp.seek(0)
591 582
592 583 # 3a. apply filtered patch to clean repo (clean)
593 584 if backups:
594 585 hg.revert(repo, repo.dirstate.p1(),
595 586 lambda key: key in backups)
596 587
597 588 # 3b. (apply)
598 589 if dopatch:
599 590 try:
600 591 ui.debug('applying patch\n')
601 592 ui.debug(fp.getvalue())
602 593 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
603 594 except patch.PatchError, err:
604 595 raise util.Abort(str(err))
605 596 del fp
606 597
607 598 # 4. We prepared working directory according to filtered
608 599 # patch. Now is the time to delegate the job to
609 600 # commit/qrefresh or the like!
610 601
611 602 # it is important to first chdir to repo root -- we'll call
612 603 # a highlevel command with list of pathnames relative to
613 604 # repo root
614 605 cwd = os.getcwd()
615 606 os.chdir(repo.root)
616 607 try:
617 608 commitfunc(ui, repo, *newfiles, **opts)
618 609 finally:
619 610 os.chdir(cwd)
620 611
621 612 return 0
622 613 finally:
623 614 # 5. finally restore backed-up files
624 615 try:
625 616 for realname, tmpname in backups.iteritems():
626 617 ui.debug('restoring %r to %r\n' % (tmpname, realname))
627 618 util.copyfile(tmpname, repo.wjoin(realname))
628 619 # Our calls to copystat() here and above are a
629 620 # hack to trick any editors that have f open that
630 621 # we haven't modified them.
631 622 #
632 623 # Also note that this racy as an editor could
633 624 # notice the file's mtime before we've finished
634 625 # writing it.
635 626 shutil.copystat(tmpname, repo.wjoin(realname))
636 627 os.unlink(tmpname)
637 628 if tobackup:
638 629 os.rmdir(backupdir)
639 630 except OSError:
640 631 pass
641 632
642 633 # wrap ui.write so diff output can be labeled/colorized
643 634 def wrapwrite(orig, *args, **kw):
644 635 label = kw.pop('label', '')
645 636 for chunk, l in patch.difflabel(lambda: args):
646 637 orig(chunk, label=label + l)
647 638 oldwrite = ui.write
648 639 extensions.wrapfunction(ui, 'write', wrapwrite)
649 640 try:
650 641 return cmdutil.commit(ui, repo, recordfunc, pats, opts)
651 642 finally:
652 643 ui.write = oldwrite
653 644
654 645 cmdtable["qrecord"] = \
655 646 (qrecord, [], # placeholder until mq is available
656 647 _('hg qrecord [OPTION]... PATCH [FILE]...'))
657 648
658 649 def uisetup(ui):
659 650 try:
660 651 mq = extensions.find('mq')
661 652 except KeyError:
662 653 return
663 654
664 655 cmdtable["qrecord"] = \
665 656 (qrecord,
666 657 # same options as qnew, but copy them so we don't get
667 658 # -i/--interactive for qrecord and add white space diff options
668 mq.cmdtable['^qnew'][1][:] + diffopts,
659 mq.cmdtable['^qnew'][1][:] + commands.diffwsopts,
669 660 _('hg qrecord [OPTION]... PATCH [FILE]...'))
670 661
671 662 _wrapcmd('qnew', mq.cmdtable, qnew, _("interactively record a new patch"))
672 663 _wrapcmd('qrefresh', mq.cmdtable, qrefresh,
673 664 _("interactively select changes to refresh"))
674 665
675 666 def _wrapcmd(cmd, table, wrapfn, msg):
676 667 entry = extensions.wrapcommand(table, cmd, wrapfn)
677 668 entry[1].append(('i', 'interactive', None, msg))
678 669
679 670 commands.inferrepo += " record qrecord"
General Comments 0
You need to be logged in to leave comments. Login now