hgext/record.py
changeset 16324 46b991a1f428
parent 15184 351a9292e430
child 16328 d388c3fc8319
equal deleted inserted replaced
16318:73f4e05287b4 16324:46b991a1f428
   259     return p.finished()
   259     return p.finished()
   260 
   260 
   261 def filterpatch(ui, headers):
   261 def filterpatch(ui, headers):
   262     """Interactively filter patch chunks into applied-only chunks"""
   262     """Interactively filter patch chunks into applied-only chunks"""
   263 
   263 
   264     def prompt(skipfile, skipall, query):
   264     def prompt(skipfile, skipall, query, chunk):
   265         """prompt query, and process base inputs
   265         """prompt query, and process base inputs
   266 
   266 
   267         - y/n for the rest of file
   267         - y/n for the rest of file
   268         - y/n for the rest
   268         - y/n for the rest
   269         - ? (help)
   269         - ? (help)
   270         - q (quit)
   270         - q (quit)
   271 
   271 
   272         Return True/False and possibly updated skipfile and skipall.
   272         Return True/False and possibly updated skipfile and skipall.
   273         """
   273         """
       
   274         newpatches = None
   274         if skipall is not None:
   275         if skipall is not None:
   275             return skipall, skipfile, skipall
   276             return skipall, skipfile, skipall, newpatches
   276         if skipfile is not None:
   277         if skipfile is not None:
   277             return skipfile, skipfile, skipall
   278             return skipfile, skipfile, skipall, newpatches
   278         while True:
   279         while True:
   279             resps = _('[Ynsfdaq?]')
   280             resps = _('[Ynesfdaq?]')
   280             choices = (_('&Yes, record this change'),
   281             choices = (_('&Yes, record this change'),
   281                     _('&No, skip this change'),
   282                     _('&No, skip this change'),
       
   283                     _('&Edit the change manually'),
   282                     _('&Skip remaining changes to this file'),
   284                     _('&Skip remaining changes to this file'),
   283                     _('Record remaining changes to this &file'),
   285                     _('Record remaining changes to this &file'),
   284                     _('&Done, skip remaining changes and files'),
   286                     _('&Done, skip remaining changes and files'),
   285                     _('Record &all changes to all remaining files'),
   287                     _('Record &all changes to all remaining files'),
   286                     _('&Quit, recording no changes'),
   288                     _('&Quit, recording no changes'),
   287                     _('&?'))
   289                     _('&?'))
   288             r = ui.promptchoice("%s %s" % (query, resps), choices)
   290             r = ui.promptchoice("%s %s" % (query, resps), choices)
   289             ui.write("\n")
   291             ui.write("\n")
   290             if r == 7: # ?
   292             if r == 8: # ?
   291                 doc = gettext(record.__doc__)
   293                 doc = gettext(record.__doc__)
   292                 c = doc.find('::') + 2
   294                 c = doc.find('::') + 2
   293                 for l in doc[c:].splitlines():
   295                 for l in doc[c:].splitlines():
   294                     if l.startswith('      '):
   296                     if l.startswith('      '):
   295                         ui.write(l.strip(), '\n')
   297                         ui.write(l.strip(), '\n')
   296                 continue
   298                 continue
   297             elif r == 0: # yes
   299             elif r == 0: # yes
   298                 ret = True
   300                 ret = True
   299             elif r == 1: # no
   301             elif r == 1: # no
   300                 ret = False
   302                 ret = False
   301             elif r == 2: # Skip
   303             elif r == 2: # Edit patch
       
   304                 if chunk is None:
       
   305                     ui.write(_('cannot edit patch for whole file'))
       
   306                     ui.write("\n")
       
   307                     continue
       
   308                 if chunk.header.binary():
       
   309                     ui.write(_('cannot edit patch for binary file'))
       
   310                     ui.write("\n")
       
   311                     continue
       
   312                 # Patch comment based on the Git one (based on comment at end of
       
   313                 # http://mercurial.selenic.com/wiki/RecordExtension)
       
   314                 phelp = '---' + _("""
       
   315 To remove '-' lines, make them ' ' lines (context).
       
   316 To remove '+' lines, delete them.
       
   317 Lines starting with # will be removed from the patch.
       
   318 
       
   319 If the patch applies cleanly, the edited hunk will immediately be
       
   320 added to the record list. If it does not apply cleanly, a rejects
       
   321 file will be generated: you can use that when you try again. If
       
   322 all lines of the hunk are removed, then the edit is aborted and
       
   323 the hunk is left unchanged.
       
   324 """)
       
   325                 (patchfd, patchfn) = tempfile.mkstemp(prefix="hg-editor-",
       
   326                         suffix=".diff", text=True)
       
   327                 try:
       
   328                     # Write the initial patch
       
   329                     f = os.fdopen(patchfd, "w")
       
   330                     chunk.header.write(f)
       
   331                     chunk.write(f)
       
   332                     f.write('\n'.join(['# ' + i for i in phelp.splitlines()]))
       
   333                     f.close()
       
   334                     # Start the editor and wait for it to complete
       
   335                     editor = ui.geteditor()
       
   336                     util.system("%s \"%s\"" % (editor, patchfn),
       
   337                             environ={'HGUSER': ui.username()},
       
   338                             onerr=util.Abort, errprefix=_("edit failed"),
       
   339                             out=ui.fout)
       
   340                     # Remove comment lines
       
   341                     patchfp = open(patchfn)
       
   342                     ncpatchfp = cStringIO.StringIO()
       
   343                     for line in patchfp:
       
   344                         if not line.startswith('#'):
       
   345                             ncpatchfp.write(line)
       
   346                     patchfp.close()
       
   347                     ncpatchfp.seek(0)
       
   348                     newpatches = parsepatch(ncpatchfp)
       
   349                 finally:
       
   350                     os.unlink(patchfn)
       
   351                     del ncpatchfp
       
   352                 # Signal that the chunk shouldn't be applied as-is, but
       
   353                 # provide the new patch to be used instead.
       
   354                 ret = False
       
   355             elif r == 3: # Skip
   302                 ret = skipfile = False
   356                 ret = skipfile = False
   303             elif r == 3: # file (Record remaining)
   357             elif r == 4: # file (Record remaining)
   304                 ret = skipfile = True
   358                 ret = skipfile = True
   305             elif r == 4: # done, skip remaining
   359             elif r == 5: # done, skip remaining
   306                 ret = skipall = False
   360                 ret = skipall = False
   307             elif r == 5: # all
   361             elif r == 6: # all
   308                 ret = skipall = True
   362                 ret = skipall = True
   309             elif r == 6: # quit
   363             elif r == 7: # quit
   310                 raise util.Abort(_('user quit'))
   364                 raise util.Abort(_('user quit'))
   311             return ret, skipfile, skipall
   365             return ret, skipfile, skipall, newpatches
   312 
   366 
   313     seen = set()
   367     seen = set()
   314     applied = {}        # 'filename' -> [] of chunks
   368     applied = {}        # 'filename' -> [] of chunks
   315     skipfile, skipall = None, None
   369     skipfile, skipall = None, None
   316     pos, total = 1, sum(len(h.hunks) for h in headers)
   370     pos, total = 1, sum(len(h.hunks) for h in headers)
   324         seen.add(hdr)
   378         seen.add(hdr)
   325         if skipall is None:
   379         if skipall is None:
   326             h.pretty(ui)
   380             h.pretty(ui)
   327         msg = (_('examine changes to %s?') %
   381         msg = (_('examine changes to %s?') %
   328                _(' and ').join(map(repr, h.files())))
   382                _(' and ').join(map(repr, h.files())))
   329         r, skipfile, skipall = prompt(skipfile, skipall, msg)
   383         r, skipfile, skipall, np = prompt(skipfile, skipall, msg, None)
   330         if not r:
   384         if not r:
   331             continue
   385             continue
   332         applied[h.filename()] = [h]
   386         applied[h.filename()] = [h]
   333         if h.allhunks():
   387         if h.allhunks():
   334             applied[h.filename()] += h.hunks
   388             applied[h.filename()] += h.hunks
   340                 msg = _('record this change to %r?') % chunk.filename()
   394                 msg = _('record this change to %r?') % chunk.filename()
   341             else:
   395             else:
   342                 idx = pos - len(h.hunks) + i
   396                 idx = pos - len(h.hunks) + i
   343                 msg = _('record change %d/%d to %r?') % (idx, total,
   397                 msg = _('record change %d/%d to %r?') % (idx, total,
   344                                                          chunk.filename())
   398                                                          chunk.filename())
   345             r, skipfile, skipall = prompt(skipfile, skipall, msg)
   399             r, skipfile, skipall, newpatches = prompt(skipfile,
       
   400                     skipall, msg, chunk)
   346             if r:
   401             if r:
   347                 if fixoffset:
   402                 if fixoffset:
   348                     chunk = copy.copy(chunk)
   403                     chunk = copy.copy(chunk)
   349                     chunk.toline += fixoffset
   404                     chunk.toline += fixoffset
   350                 applied[chunk.filename()].append(chunk)
   405                 applied[chunk.filename()].append(chunk)
       
   406             elif newpatches is not None:
       
   407                 for newpatch in newpatches:
       
   408                     for newhunk in newpatch.hunks:
       
   409                         if fixoffset:
       
   410                             newhunk.toline += fixoffset
       
   411                         applied[newhunk.filename()].append(newhunk)
   351             else:
   412             else:
   352                 fixoffset += chunk.removed - chunk.added
   413                 fixoffset += chunk.removed - chunk.added
   353     return sum([h for h in applied.itervalues()
   414     return sum([h for h in applied.itervalues()
   354                if h[0].special() or len(h) > 1], [])
   415                if h[0].special() or len(h) > 1], [])
   355 
   416 
   370     change to use. For each query, the following responses are
   431     change to use. For each query, the following responses are
   371     possible::
   432     possible::
   372 
   433 
   373       y - record this change
   434       y - record this change
   374       n - skip this change
   435       n - skip this change
       
   436       e - edit this change manually
   375 
   437 
   376       s - skip remaining changes to this file
   438       s - skip remaining changes to this file
   377       f - record remaining changes to this file
   439       f - record remaining changes to this file
   378 
   440 
   379       d - done, skip remaining changes and files
   441       d - done, skip remaining changes and files