##// END OF EJS Templates
progress: use 'encoding.trim' to trim items in output line correctly...
progress: use 'encoding.trim' to trim items in output line correctly Before this patch, 'progress' extension trims items in output line by directly slicing byte sequence, but it may split at intermediate multi-byte sequence. This patch uses 'encoding.trim' to trim items in output line correctly, even if it contains multi-byte characters.

File last commit:

r21805:23f32414 default
r21862:ba7f75e7 default
Show More
record.py
662 lines | 23.0 KiB | text/x-python | PythonLexer
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 # record.py
#
# Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
Dirkjan Ochtman
extensions: change descriptions for extensions providing a few commands
r8934 '''commands to interactively select changes for commit/qrefresh'''
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
FUJIWARA Katsunori
record: use "ui.extractchoices()" to get the list of available responses...
r20266 from mercurial.i18n import _
Idan Kamara
record: remove unused import
r18287 from mercurial import cmdutil, commands, extensions, hg, patch
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 from mercurial import util
Brodie Rao
record: move copystat() hack out of util.copyfile() and into record...
r13099 import copy, cStringIO, errno, os, re, shutil, tempfile
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
Idan Kamara
record: use cmdutil.command decorator
r14408 cmdtable = {}
command = cmdutil.command(cmdtable)
Augie Fackler
hgext: mark all first-party extensions as such
r16743 testedwith = 'internal'
Idan Kamara
record: use cmdutil.command decorator
r14408
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
def scanpatch(fp):
Kirill Smelkov
record: some docs...
r5826 """like patch.iterhunks, but yield different events
- ('file', [header_lines + fromfile + tofile])
- ('context', [context_lines])
- ('hunk', [hunk_lines])
Mads Kiilerich
fix wording and not-completely-trivial spelling errors and bad docstrings
r17425 - ('range', (-start,len, +start,len, proc))
Kirill Smelkov
record: some docs...
r5826 """
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 lr = patch.linereader(fp)
def scanwhile(first, p):
Kirill Smelkov
record: some docs...
r5826 """scan lr while predicate holds"""
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 lines = [first]
while True:
line = lr.readline()
if not line:
break
if p(line):
lines.append(line)
else:
lr.push(line)
break
return lines
while True:
line = lr.readline()
if not line:
break
Steve Borho
record: teach parsepatch() about non-git style headers...
r13157 if line.startswith('diff --git a/') or line.startswith('diff -r '):
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 def notheader(line):
s = line.split(None, 1)
return not s or s[0] not in ('---', 'diff')
header = scanwhile(line, notheader)
fromfile = lr.readline()
if fromfile.startswith('---'):
tofile = lr.readline()
header += [fromfile, tofile]
else:
lr.push(fromfile)
yield 'file', header
elif line[0] == ' ':
yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
elif line[0] in '-+':
yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
else:
m = lines_re.match(line)
if m:
yield 'range', m.groups()
else:
Mads Kiilerich
record: ignore trailing content when parsing patches - introduce 'other' lines...
r18954 yield 'other', line
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
class header(object):
Kirill Smelkov
record: some docs...
r5826 """patch header
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Mads Kiilerich
fix trivial spelling errors
r17424 XXX shouldn't we move this to mercurial/patch.py ?
Kirill Smelkov
record: some docs...
r5826 """
Steve Borho
record: teach parsepatch() about non-git style headers...
r13157 diffgit_re = re.compile('diff --git a/(.*) b/(.*)$')
diff_re = re.compile('diff -r .* (.*)$')
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 allhunks_re = re.compile('(?:index|new file|deleted file) ')
pretty_re = re.compile('(?:new file|deleted file) ')
special_re = re.compile('(?:index|new|deleted|copy|rename) ')
def __init__(self, header):
self.header = header
self.hunks = []
def binary(self):
Patrick Mezard
record: simplify header methods with util.any
r13294 return util.any(h.startswith('index ') for h in self.header)
Thomas Arendsen Hein
Remove trailing spaces, fix indentation
r5143
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 def pretty(self, fp):
for h in self.header:
if h.startswith('index '):
fp.write(_('this modifies a binary file (all or nothing)\n'))
break
if self.pretty_re.match(h):
fp.write(h)
if self.binary():
fp.write(_('this is a binary file\n'))
break
if h.startswith('---'):
fp.write(_('%d hunks, %d lines changed\n') %
(len(self.hunks),
timeless
record: count lines changed as the number of lines added or removed...
r11728 sum([max(h.added, h.removed) for h in self.hunks])))
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 break
fp.write(h)
def write(self, fp):
fp.write(''.join(self.header))
def allhunks(self):
Patrick Mezard
record: simplify header methods with util.any
r13294 return util.any(self.allhunks_re.match(h) for h in self.header)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
def files(self):
Steve Borho
record: teach parsepatch() about non-git style headers...
r13157 match = self.diffgit_re.match(self.header[0])
if match:
fromfile, tofile = match.groups()
if fromfile == tofile:
return [fromfile]
return [fromfile, tofile]
else:
return self.diff_re.match(self.header[0]).groups()
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
def filename(self):
return self.files()[-1]
def __repr__(self):
return '<header %s>' % (' '.join(map(repr, self.files())))
def special(self):
Patrick Mezard
record: simplify header methods with util.any
r13294 return util.any(self.special_re.match(h) for h in self.header)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
def countchanges(hunk):
Kirill Smelkov
record: some docs...
r5826 """hunk -> (n+,n-)"""
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 add = len([h for h in hunk if h[0] == '+'])
rem = len([h for h in hunk if h[0] == '-'])
return add, rem
class hunk(object):
Kirill Smelkov
record: some docs...
r5826 """patch hunk
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Kirill Smelkov
record: some docs...
r5826 XXX shouldn't we merge this with patch.hunk ?
"""
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 maxcontext = 3
def __init__(self, header, fromline, toline, proc, before, hunk, after):
def trimcontext(number, lines):
delta = len(lines) - self.maxcontext
if False and delta > 0:
return number + delta, lines[:self.maxcontext]
return number, lines
self.header = header
self.fromline, self.before = trimcontext(fromline, before)
self.toline, self.after = trimcontext(toline, after)
self.proc = proc
self.hunk = hunk
self.added, self.removed = countchanges(self.hunk)
def write(self, fp):
delta = len(self.before) + len(self.after)
Dirkjan Ochtman
record: take diff lines for lack of trailing newlines into account (issue1282)...
r6949 if self.after and self.after[-1] == '\\ No newline at end of file\n':
delta -= 1
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 fromlen = delta + self.removed
tolen = delta + self.added
fp.write('@@ -%d,%d +%d,%d @@%s\n' %
(self.fromline, fromlen, self.toline, tolen,
self.proc and (' ' + self.proc)))
fp.write(''.join(self.before + self.hunk + self.after))
pretty = write
def filename(self):
return self.header.filename()
def __repr__(self):
return '<hunk %r@%d>' % (self.filename(), self.fromline)
def parsepatch(fp):
Patrick Mezard
record: refactor the prompt loop...
r13293 """patch -> [] of headers -> [] of hunks """
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 class parser(object):
Kirill Smelkov
record: some docs...
r5826 """patch parsing state machine"""
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 def __init__(self):
self.fromline = 0
self.toline = 0
self.proc = ''
self.header = None
self.context = []
self.before = []
self.hunk = []
Patrick Mezard
record: refactor the prompt loop...
r13293 self.headers = []
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
Renato Cunha
record: tuple parameter unpacking is deprecated in py3k
r11499 def addrange(self, limits):
fromstart, fromend, tostart, toend, proc = limits
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 self.fromline = int(fromstart)
self.toline = int(tostart)
self.proc = proc
def addcontext(self, context):
if self.hunk:
h = hunk(self.header, self.fromline, self.toline, self.proc,
self.before, self.hunk, context)
self.header.hunks.append(h)
self.fromline += len(self.before) + h.removed
self.toline += len(self.before) + h.added
self.before = []
self.hunk = []
self.proc = ''
self.context = context
def addhunk(self, hunk):
if self.context:
self.before = self.context
self.context = []
Dirkjan Ochtman
record: take diff lines for lack of trailing newlines into account (issue1282)...
r6949 self.hunk = hunk
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
def newfile(self, hdr):
self.addcontext([])
h = header(hdr)
Patrick Mezard
record: refactor the prompt loop...
r13293 self.headers.append(h)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 self.header = h
Mads Kiilerich
record: ignore trailing content when parsing patches - introduce 'other' lines...
r18954 def addother(self, line):
pass # 'other' lines are ignored
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 def finished(self):
self.addcontext([])
Patrick Mezard
record: refactor the prompt loop...
r13293 return self.headers
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
transitions = {
'file': {'context': addcontext,
'file': newfile,
'hunk': addhunk,
'range': addrange},
'context': {'file': newfile,
'hunk': addhunk,
Mads Kiilerich
record: ignore trailing content when parsing patches - introduce 'other' lines...
r18954 'range': addrange,
'other': addother},
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 'hunk': {'context': addcontext,
'file': newfile,
'range': addrange},
'range': {'context': addcontext,
'hunk': addhunk},
Mads Kiilerich
record: ignore trailing content when parsing patches - introduce 'other' lines...
r18954 'other': {'other': addother},
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 }
Thomas Arendsen Hein
Remove trailing spaces, fix indentation
r5143
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 p = parser()
state = 'context'
for newstate, data in scanpatch(fp):
try:
p.transitions[state][newstate](p, data)
except KeyError:
raise patch.PatchError('unhandled transition: %s -> %s' %
(state, newstate))
state = newstate
return p.finished()
Patrick Mezard
record: refactor the prompt loop...
r13293 def filterpatch(ui, headers):
Kirill Smelkov
record: some docs...
r5826 """Interactively filter patch chunks into applied-only chunks"""
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 def prompt(skipfile, skipall, query, chunk):
Kirill Smelkov
record: some docs...
r5826 """prompt query, and process base inputs
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Kirill Smelkov
record: some docs...
r5826 - y/n for the rest of file
- y/n for the rest
- ? (help)
- q (quit)
Patrick Mezard
record: turn prompt() into a pure function
r13291 Return True/False and possibly updated skipfile and skipall.
Kirill Smelkov
record: some docs...
r5826 """
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 newpatches = None
Patrick Mezard
record: turn prompt() into a pure function
r13291 if skipall is not None:
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 return skipall, skipfile, skipall, newpatches
Patrick Mezard
record: turn prompt() into a pure function
r13291 if skipfile is not None:
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 return skipfile, skipfile, skipall, newpatches
Bryan O'Sullivan
record: improve docs, improve prompts
r5154 while True:
Matt Mackall
ui: merge prompt text components into a singe string...
r19226 resps = _('[Ynesfdaq?]'
'$$ &Yes, record this change'
'$$ &No, skip this change'
FUJIWARA Katsunori
record: use "ui.extractchoices()" to get the list of available responses...
r20266 '$$ &Edit this change manually'
Matt Mackall
ui: merge prompt text components into a singe string...
r19226 '$$ &Skip remaining changes to this file'
'$$ Record remaining changes to this &file'
'$$ &Done, skip remaining changes and files'
'$$ Record &all changes to all remaining files'
'$$ &Quit, recording no changes'
FUJIWARA Katsunori
record: use "ui.extractchoices()" to get the list of available responses...
r20266 '$$ &? (display help)')
Matt Mackall
ui: merge prompt text components into a singe string...
r19226 r = ui.promptchoice("%s %s" % (query, resps))
Martin Geisler
record: separate each hunk with a blank line...
r10694 ui.write("\n")
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 if r == 8: # ?
FUJIWARA Katsunori
record: use "ui.extractchoices()" to get the list of available responses...
r20266 for c, t in ui.extractchoices(resps)[1]:
ui.write('%s - %s\n' % (c, t.lower()))
Bryan O'Sullivan
record: improve docs, improve prompts
r5154 continue
Simon Heimberg
ui: extract choice from prompt...
r9048 elif r == 0: # yes
Martin Geisler
record: handle translated prompt correctly...
r9837 ret = True
Simon Heimberg
ui: extract choice from prompt...
r9048 elif r == 1: # no
Martin Geisler
record: handle translated prompt correctly...
r9837 ret = False
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 elif r == 2: # Edit patch
if chunk is None:
ui.write(_('cannot edit patch for whole file'))
ui.write("\n")
continue
if chunk.header.binary():
ui.write(_('cannot edit patch for binary file'))
ui.write("\n")
continue
# Patch comment based on the Git one (based on comment at end of
# http://mercurial.selenic.com/wiki/RecordExtension)
phelp = '---' + _("""
To remove '-' lines, make them ' ' lines (context).
To remove '+' lines, delete them.
Lines starting with # will be removed from the patch.
If the patch applies cleanly, the edited hunk will immediately be
added to the record list. If it does not apply cleanly, a rejects
file will be generated: you can use that when you try again. If
all lines of the hunk are removed, then the edit is aborted and
the hunk is left unchanged.
""")
(patchfd, patchfn) = tempfile.mkstemp(prefix="hg-editor-",
suffix=".diff", text=True)
Matt Mackall
record: fix up test issues...
r16328 ncpatchfp = None
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 try:
# Write the initial patch
f = os.fdopen(patchfd, "w")
chunk.header.write(f)
chunk.write(f)
f.write('\n'.join(['# ' + i for i in phelp.splitlines()]))
f.close()
# Start the editor and wait for it to complete
editor = ui.geteditor()
util.system("%s \"%s\"" % (editor, patchfn),
environ={'HGUSER': ui.username()},
onerr=util.Abort, errprefix=_("edit failed"),
out=ui.fout)
# Remove comment lines
patchfp = open(patchfn)
ncpatchfp = cStringIO.StringIO()
for line in patchfp:
if not line.startswith('#'):
ncpatchfp.write(line)
patchfp.close()
ncpatchfp.seek(0)
newpatches = parsepatch(ncpatchfp)
finally:
os.unlink(patchfn)
del ncpatchfp
# Signal that the chunk shouldn't be applied as-is, but
# provide the new patch to be used instead.
ret = False
elif r == 3: # Skip
Patrick Mezard
record: turn prompt() into a pure function
r13291 ret = skipfile = False
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 elif r == 4: # file (Record remaining)
Patrick Mezard
record: turn prompt() into a pure function
r13291 ret = skipfile = True
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 elif r == 5: # done, skip remaining
Patrick Mezard
record: turn prompt() into a pure function
r13291 ret = skipall = False
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 elif r == 6: # all
Patrick Mezard
record: turn prompt() into a pure function
r13291 ret = skipall = True
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 elif r == 7: # quit
Bryan O'Sullivan
record: improve docs, improve prompts
r5154 raise util.Abort(_('user quit'))
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 return ret, skipfile, skipall, newpatches
Patrick Mezard
record: turn prompt() into a pure function
r13291
seen = set()
applied = {} # 'filename' -> [] of chunks
skipfile, skipall = None, None
Patrick Mezard
record: do not include files into changes count...
r13295 pos, total = 1, sum(len(h.hunks) for h in headers)
Patrick Mezard
record: refactor the prompt loop...
r13293 for h in headers:
Patrick Mezard
record: do not include files into changes count...
r13295 pos += len(h.hunks)
Patrick Mezard
record: refactor the prompt loop...
r13293 skipfile = None
fixoffset = 0
hdr = ''.join(h.header)
if hdr in seen:
continue
seen.add(hdr)
if skipall is None:
h.pretty(ui)
msg = (_('examine changes to %s?') %
Matt Mackall
record: fix display of non-ASCII names...
r17081 _(' and ').join("'%s'" % f for f in h.files()))
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 r, skipfile, skipall, np = prompt(skipfile, skipall, msg, None)
Patrick Mezard
record: refactor the prompt loop...
r13293 if not r:
continue
applied[h.filename()] = [h]
if h.allhunks():
applied[h.filename()] += h.hunks
continue
for i, chunk in enumerate(h.hunks):
Patrick Mezard
record: turn prompt() into a pure function
r13291 if skipfile is None and skipall is None:
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 chunk.pretty(ui)
Martin Geisler
record: replace poor man's if-statement with real if-statement
r13773 if total == 1:
Nikolaj Sjujskij
record: fix display of non-ASCII names in chunk selection...
r17566 msg = _("record this change to '%s'?") % chunk.filename()
Martin Geisler
record: replace poor man's if-statement with real if-statement
r13773 else:
idx = pos - len(h.hunks) + i
Nikolaj Sjujskij
record: fix display of non-ASCII names in chunk selection...
r17566 msg = _("record change %d/%d to '%s'?") % (idx, total,
chunk.filename())
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 r, skipfile, skipall, newpatches = prompt(skipfile,
skipall, msg, chunk)
Martin Geisler
record: handle translated prompt correctly...
r9837 if r:
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 if fixoffset:
chunk = copy.copy(chunk)
chunk.toline += fixoffset
applied[chunk.filename()].append(chunk)
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 elif newpatches is not None:
for newpatch in newpatches:
for newhunk in newpatch.hunks:
if fixoffset:
newhunk.toline += fixoffset
applied[newhunk.filename()].append(newhunk)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 else:
fixoffset += chunk.removed - chunk.added
Renato Cunha
record: removed 'reduce' calls (unsupported by py3k)...
r11500 return sum([h for h in applied.itervalues()
if h[0].special() or len(h) > 1], [])
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
Idan Kamara
record: use cmdutil.command decorator
r14408 @command("record",
Ingo Proetel
record: add white space diff options
r14597 # same options as commit + white space diff options
Jordi Gutiérrez Hermoso
record: use commands.diffwsopts instead of ad-hoc diffopts...
r20300 commands.table['^commit|ci'][1][:] + commands.diffwsopts,
Idan Kamara
record: use cmdutil.command decorator
r14408 _('hg record [OPTION]... [FILE]...'))
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 def record(ui, repo, *pats, **opts):
Bryan O'Sullivan
record: improve docs, improve prompts
r5154 '''interactively select changes to commit
Martin Geisler
Use hg role in help strings
r10973 If a list of files is omitted, all changes reported by :hg:`status`
Martin Geisler
record: wrap docstrings at 70 characters
r9272 will be candidates for recording.
Bryan O'Sullivan
record: improve docs, improve prompts
r5154
Martin Geisler
Use hg role in help strings
r10973 See :hg:`help dates` for a list of formats valid for -d/--date.
Thomas Arendsen Hein
Document log date ranges and mention 'hg help dates' for all commands (issue998)
r6163
Martin Geisler
record: wrap docstrings at 70 characters
r9272 You will be prompted for whether to record changes to each
modified file, and for files with multiple changes, for each
change to use. For each query, the following responses are
possible::
Bryan O'Sullivan
record: improve docs, improve prompts
r5154
Martin Geisler
commands: use minirst parser when displaying help
r9157 y - record this change
n - skip this change
A. S. Budden
record: allow splitting of hunks by manually editing patches...
r16324 e - edit this change manually
Bryan O'Sullivan
record: improve docs, improve prompts
r5154
Martin Geisler
commands: use minirst parser when displaying help
r9157 s - skip remaining changes to this file
f - record remaining changes to this file
Bryan O'Sullivan
record: improve docs, improve prompts
r5154
Martin Geisler
commands: use minirst parser when displaying help
r9157 d - done, skip remaining changes and files
a - record all changes to all remaining files
q - quit, recording no changes
Bryan O'Sullivan
record: improve docs, improve prompts
r5154
Nicolas Dumazet
record: check that we are not committing a merge before patch selection...
r11237 ? - display help
This command is not available when committing a merge.'''
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
Idan Kamara
record: add an option to backup all wc modifications...
r14425 dorecord(ui, repo, commands.commit, 'commit', False, *pats, **opts)
Kirill Smelkov
hg qrecord -- like record, but for mq...
r5830
Matt Mackall
record: use command wrapper properly for qnew/qrefresh (issue3001)
r15184 def qrefresh(origfn, ui, repo, *pats, **opts):
if not opts['interactive']:
return origfn(ui, repo, *pats, **opts)
Idan Kamara
record: add qrefresh -i/--interactive...
r14426 mq = extensions.find('mq')
def committomq(ui, repo, *pats, **opts):
# At this point the working copy contains only changes that
# were accepted. All other changes were reverted.
# We can't pass *pats here since qrefresh will undo all other
# changed files in the patch that aren't in pats.
mq.refresh(ui, repo, **opts)
# backup all changed files
dorecord(ui, repo, committomq, 'qrefresh', True, *pats, **opts)
Kirill Smelkov
hg qrecord -- like record, but for mq...
r5830
Gregory Szorc
record: declare commands using decorator
r21251 # This command registration is replaced during uisetup().
Gregory Szorc
record: define inferrepo in command decorator
r21787 @command('qrecord',
[],
_('hg qrecord [OPTION]... PATCH [FILE]...'),
inferrepo=True)
Kirill Smelkov
qrecord: record complements commit, so qrecord should complement qnew...
r5932 def qrecord(ui, repo, patch, *pats, **opts):
'''interactively record a new patch
Kirill Smelkov
hg qrecord -- like record, but for mq...
r5830
Martin Geisler
Use hg role in help strings
r10973 See :hg:`help qnew` & :hg:`help record` for more information and
Martin Geisler
record: wrap docstrings at 70 characters
r9272 usage.
Kirill Smelkov
hg qrecord -- like record, but for mq...
r5830 '''
try:
mq = extensions.find('mq')
except KeyError:
raise util.Abort(_("'mq' extension not loaded"))
Idan Kamara
record: check patch name is valid before prompting in qrecord
r14424 repo.mq.checkpatchname(patch)
Dan Villiom Podlaski Christiansen
record: function variable naming & signature cleanup....
r10323 def committomq(ui, repo, *pats, **opts):
Idan Kamara
record: check patch name is valid before prompting in qrecord
r14424 opts['checkname'] = False
Kirill Smelkov
qrecord: record complements commit, so qrecord should complement qnew...
r5932 mq.new(ui, repo, patch, *pats, **opts)
Kirill Smelkov
hg qrecord -- like record, but for mq...
r5830
Idan Kamara
record: add an option to backup all wc modifications...
r14425 dorecord(ui, repo, committomq, 'qnew', False, *pats, **opts)
Kirill Smelkov
record: refactor record into generic record driver...
r5827
Matt Mackall
record: use command wrapper properly for qnew/qrefresh (issue3001)
r15184 def qnew(origfn, ui, repo, patch, *args, **opts):
if opts['interactive']:
return qrecord(ui, repo, patch, *args, **opts)
return origfn(ui, repo, patch, *args, **opts)
Idan Kamara
record: add an option to backup all wc modifications...
r14425 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall, *pats, **opts):
Matt Mackall
ui: make interactive a method
r8208 if not ui.interactive():
Idan Kamara
record: suggest the right command when running non interactively
r14407 raise util.Abort(_('running non-interactively, use %s instead') %
cmdsuggest)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
Sumeet
record: checks for valid username before starting recording process (issue3456)
r17446 # make sure username is set before going interactive
Prasoon Shukla
record: --user/-u now works with record when ui.username not set (issue3857)...
r20171 if not opts.get('user'):
ui.username() # raise exception, username not provided
Sumeet
record: checks for valid username before starting recording process (issue3456)
r17446
Matt Mackall
match: stop passing files through commitfunc
r6600 def recordfunc(ui, repo, message, match, opts):
Kirill Smelkov
record: refactor record into generic record driver...
r5827 """This is generic record driver.
Kevin Bullock
record: clean up comments and docstrings...
r13195 Its job is to interactively filter local changes, and
accordingly prepare working directory into a state in which the
job can be delegated to a non-interactive commit command such as
'commit' or 'qrefresh'.
Kirill Smelkov
record: refactor record into generic record driver...
r5827
Kevin Bullock
record: clean up comments and docstrings...
r13195 After the actual job is done by non-interactive command, the
working directory is restored to its original state.
Kirill Smelkov
record: refactor record into generic record driver...
r5827
Kevin Bullock
record: clean up comments and docstrings...
r13195 In the end we'll record interesting changes, and everything else
will be left in place, so the user can continue working.
Kirill Smelkov
record: refactor record into generic record driver...
r5827 """
Dirkjan Ochtman
record: minimize number of status calls
r7754
Matt Mackall
record: add checkunfinished support (issue3955)
r19497 cmdutil.checkunfinished(repo, commit=True)
Nicolas Dumazet
record: check that we are not committing a merge before patch selection...
r11237 merge = len(repo[None].parents()) > 1
if merge:
raise util.Abort(_('cannot partially commit a merge '
timeless
record: quote command in use hg commit message
r13023 '(use "hg commit" instead)'))
Nicolas Dumazet
record: check that we are not committing a merge before patch selection...
r11237
Dirkjan Ochtman
record: minimize number of status calls
r7754 changes = repo.status(match=match)[:3]
Jordi Gutiérrez Hermoso
record: re-enable whitespace-ignoring options...
r20301 diffopts = opts.copy()
diffopts['nodates'] = True
diffopts['git'] = True
diffopts = patch.diffopts(ui, opts=diffopts)
Dirkjan Ochtman
record: minimize number of status calls
r7754 chunks = patch.diff(repo, changes=changes, opts=diffopts)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 fp = cStringIO.StringIO()
Dirkjan Ochtman
patch: turn patch.diff() into a generator...
r7308 fp.write(''.join(chunks))
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 fp.seek(0)
Kirill Smelkov
record: refactor record into generic record driver...
r5827 # 1. filter patch, so we have intending-to apply subset of it
Mads Kiilerich
record: abort on malformed patches instead of crashing
r18953 try:
chunks = filterpatch(ui, parsepatch(fp))
except patch.PatchError, err:
raise util.Abort(_('error parsing patch: %s') % err)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 del fp
Martin Geisler
replace set-like dictionaries with real sets...
r8152 contenders = set()
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 for h in chunks:
Matt Mackall
many, many trivial check-code fixups
r10282 try:
contenders.update(set(h.files()))
except AttributeError:
pass
Thomas Arendsen Hein
Remove trailing spaces, fix indentation
r5143
Dirkjan Ochtman
record: minimize number of status calls
r7754 changed = changes[0] + changes[1] + changes[2]
newfiles = [f for f in changed if f in contenders]
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 if not newfiles:
ui.status(_('no changes to record\n'))
return 0
Martin Geisler
replace set-like dictionaries with real sets...
r8152 modified = set(changes[0])
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
Kirill Smelkov
record: refactor record into generic record driver...
r5827 # 2. backup changed files, so we can restore them in the end
Idan Kamara
record: add an option to backup all wc modifications...
r14425 if backupall:
tobackup = changed
else:
tobackup = [f for f in newfiles if f in modified]
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 backups = {}
Idan Kamara
record: add an option to backup all wc modifications...
r14425 if tobackup:
backupdir = repo.join('record-backups')
try:
os.mkdir(backupdir)
except OSError, err:
if err.errno != errno.EEXIST:
raise
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 try:
Kirill Smelkov
record: refactor record into generic record driver...
r5827 # backup continues
Idan Kamara
record: add an option to backup all wc modifications...
r14425 for f in tobackup:
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
dir=backupdir)
os.close(fd)
Martin Geisler
do not attempt to translate ui.debug output
r9467 ui.debug('backup %r as %r\n' % (f, tmpname))
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 util.copyfile(repo.wjoin(f), tmpname)
Brodie Rao
record: move copystat() hack out of util.copyfile() and into record...
r13099 shutil.copystat(repo.wjoin(f), tmpname)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 backups[f] = tmpname
fp = cStringIO.StringIO()
for c in chunks:
if c.filename() in backups:
c.write(fp)
dopatch = fp.tell()
fp.seek(0)
Kirill Smelkov
record: refactor record into generic record driver...
r5827 # 3a. apply filtered patch to clean repo (clean)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 if backups:
Matt Mackall
misc: replace .parents()[0] with p1()
r13878 hg.revert(repo, repo.dirstate.p1(),
Renato Cunha
record: removed 'has_key' usage...
r11564 lambda key: key in backups)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
Kirill Smelkov
record: refactor record into generic record driver...
r5827 # 3b. (apply)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 if dopatch:
Dirkjan Ochtman
record: catch PatchErrors from internalpatch and display error message...
r6950 try:
Martin Geisler
do not attempt to translate ui.debug output
r9467 ui.debug('applying patch\n')
Dirkjan Ochtman
record: catch PatchErrors from internalpatch and display error message...
r6950 ui.debug(fp.getvalue())
Patrick Mezard
patch: add a workingbackend dirstate layer on top of fsbackend...
r14370 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
Dirkjan Ochtman
record: catch PatchErrors from internalpatch and display error message...
r6950 except patch.PatchError, err:
Patrick Mezard
patch: always raise PatchError with a message, simplify handling
r12674 raise util.Abort(str(err))
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 del fp
Kevin Bullock
record: clean up comments and docstrings...
r13195 # 4. We prepared working directory according to filtered
# patch. Now is the time to delegate the job to
# commit/qrefresh or the like!
Kirill Smelkov
record: refactor record into generic record driver...
r5827
Jordi Gutiérrez Hermoso
record: update comment to match code...
r21805 # Make all of the pathnames absolute.
Pierre-Yves David
record: use absolute path instead of os.chdir...
r20334 newfiles = [repo.wjoin(nf) for nf in newfiles]
commitfunc(ui, repo, *newfiles, **opts)
Kirill Smelkov
record: refactor record into generic record driver...
r5827
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 return 0
finally:
Kirill Smelkov
record: refactor record into generic record driver...
r5827 # 5. finally restore backed-up files
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 try:
for realname, tmpname in backups.iteritems():
Martin Geisler
do not attempt to translate ui.debug output
r9467 ui.debug('restoring %r to %r\n' % (tmpname, realname))
Bryan O'Sullivan
record: work properly if invoked in a subdirectory
r5128 util.copyfile(tmpname, repo.wjoin(realname))
Brodie Rao
record: move copystat() hack out of util.copyfile() and into record...
r13099 # Our calls to copystat() here and above are a
# hack to trick any editors that have f open that
# we haven't modified them.
#
# Also note that this racy as an editor could
# notice the file's mtime before we've finished
# writing it.
shutil.copystat(tmpname, repo.wjoin(realname))
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 os.unlink(tmpname)
Idan Kamara
record: add an option to backup all wc modifications...
r14425 if tobackup:
os.rmdir(backupdir)
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037 except OSError:
pass
Brodie Rao
record: make use of output labeling
r10825
# wrap ui.write so diff output can be labeled/colorized
def wrapwrite(orig, *args, **kw):
label = kw.pop('label', '')
for chunk, l in patch.difflabel(lambda: args):
orig(chunk, label=label + l)
oldwrite = ui.write
extensions.wrapfunction(ui, 'write', wrapwrite)
try:
return cmdutil.commit(ui, repo, recordfunc, pats, opts)
finally:
ui.write = oldwrite
Bryan O'Sullivan
Add record extension, giving darcs-like interactive hunk picking
r5037
Martin Geisler
record: use uisetup instead of extsetup to register qrecord...
r9710 def uisetup(ui):
Kirill Smelkov
hg qrecord -- like record, but for mq...
r5830 try:
mq = extensions.find('mq')
except KeyError:
return
Idan Kamara
record: use cmdutil.command decorator
r14408 cmdtable["qrecord"] = \
Idan Kamara
record: alias qrecord to qnew -i/--interactive
r14427 (qrecord,
# same options as qnew, but copy them so we don't get
Ingo Proetel
record: add white space diff options
r14597 # -i/--interactive for qrecord and add white space diff options
Jordi Gutiérrez Hermoso
record: use commands.diffwsopts instead of ad-hoc diffopts...
r20300 mq.cmdtable['^qnew'][1][:] + commands.diffwsopts,
Idan Kamara
record: use cmdutil.command decorator
r14408 _('hg qrecord [OPTION]... PATCH [FILE]...'))
Idan Kamara
record: add qrefresh -i/--interactive...
r14426
Matt Mackall
record: use command wrapper properly for qnew/qrefresh (issue3001)
r15184 _wrapcmd('qnew', mq.cmdtable, qnew, _("interactively record a new patch"))
Idan Kamara
record: add qrefresh -i/--interactive...
r14426 _wrapcmd('qrefresh', mq.cmdtable, qrefresh,
_("interactively select changes to refresh"))
def _wrapcmd(cmd, table, wrapfn, msg):
Matt Mackall
record: use command wrapper properly for qnew/qrefresh (issue3001)
r15184 entry = extensions.wrapcommand(table, cmd, wrapfn)
Idan Kamara
record: add qrefresh -i/--interactive...
r14426 entry[1].append(('i', 'interactive', None, msg))