record.py
569 lines
| 18.2 KiB
| text/x-python
|
PythonLexer
/ hgext / record.py
Bryan O'Sullivan
|
r5037 | # record.py | ||
# | ||||
# Copyright 2007 Bryan O'Sullivan <bos@serpentine.com> | ||||
# | ||||
Martin Geisler
|
r8225 | # This software may be used and distributed according to the terms of the | ||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Bryan O'Sullivan
|
r5037 | |||
Dirkjan Ochtman
|
r8934 | '''commands to interactively select changes for commit/qrefresh''' | ||
Bryan O'Sullivan
|
r5037 | |||
Martin Geisler
|
r7015 | from mercurial.i18n import gettext, _ | ||
Joel Rosdahl
|
r6212 | from mercurial import cmdutil, commands, extensions, hg, mdiff, patch | ||
Bryan O'Sullivan
|
r5037 | from mercurial import util | ||
Renato Cunha
|
r11500 | import copy, cStringIO, errno, os, re, tempfile | ||
Bryan O'Sullivan
|
r5037 | |||
lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)') | ||||
def scanpatch(fp): | ||||
Kirill Smelkov
|
r5826 | """like patch.iterhunks, but yield different events | ||
- ('file', [header_lines + fromfile + tofile]) | ||||
- ('context', [context_lines]) | ||||
- ('hunk', [hunk_lines]) | ||||
- ('range', (-start,len, +start,len, diffp)) | ||||
""" | ||||
Bryan O'Sullivan
|
r5037 | lr = patch.linereader(fp) | ||
def scanwhile(first, p): | ||||
Kirill Smelkov
|
r5826 | """scan lr while predicate holds""" | ||
Bryan O'Sullivan
|
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 | ||||
if line.startswith('diff --git a/'): | ||||
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: | ||||
raise patch.PatchError('unknown patch content: %r' % line) | ||||
class header(object): | ||||
Kirill Smelkov
|
r5826 | """patch header | ||
Thomas Arendsen Hein
|
r6210 | |||
XXX shoudn't we move this to mercurial/patch.py ? | ||||
Kirill Smelkov
|
r5826 | """ | ||
Bryan O'Sullivan
|
r5037 | diff_re = re.compile('diff --git a/(.*) b/(.*)$') | ||
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): | ||||
for h in self.header: | ||||
if h.startswith('index '): | ||||
return True | ||||
Thomas Arendsen Hein
|
r5143 | |||
Bryan O'Sullivan
|
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
|
r11728 | sum([max(h.added, h.removed) for h in self.hunks]))) | ||
Bryan O'Sullivan
|
r5037 | break | ||
fp.write(h) | ||||
def write(self, fp): | ||||
fp.write(''.join(self.header)) | ||||
def allhunks(self): | ||||
for h in self.header: | ||||
if self.allhunks_re.match(h): | ||||
return True | ||||
def files(self): | ||||
fromfile, tofile = self.diff_re.match(self.header[0]).groups() | ||||
if fromfile == tofile: | ||||
return [fromfile] | ||||
return [fromfile, tofile] | ||||
def filename(self): | ||||
return self.files()[-1] | ||||
def __repr__(self): | ||||
return '<header %s>' % (' '.join(map(repr, self.files()))) | ||||
def special(self): | ||||
for h in self.header: | ||||
if self.special_re.match(h): | ||||
return True | ||||
def countchanges(hunk): | ||||
Kirill Smelkov
|
r5826 | """hunk -> (n+,n-)""" | ||
Bryan O'Sullivan
|
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
|
r5826 | """patch hunk | ||
Thomas Arendsen Hein
|
r6210 | |||
Kirill Smelkov
|
r5826 | XXX shouldn't we merge this with patch.hunk ? | ||
""" | ||||
Bryan O'Sullivan
|
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
|
r6949 | if self.after and self.after[-1] == '\\ No newline at end of file\n': | ||
delta -= 1 | ||||
Bryan O'Sullivan
|
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): | ||||
Kirill Smelkov
|
r5826 | """patch -> [] of hunks """ | ||
Bryan O'Sullivan
|
r5037 | class parser(object): | ||
Kirill Smelkov
|
r5826 | """patch parsing state machine""" | ||
Bryan O'Sullivan
|
r5037 | def __init__(self): | ||
self.fromline = 0 | ||||
self.toline = 0 | ||||
self.proc = '' | ||||
self.header = None | ||||
self.context = [] | ||||
self.before = [] | ||||
self.hunk = [] | ||||
self.stream = [] | ||||
Renato Cunha
|
r11499 | def addrange(self, limits): | ||
fromstart, fromend, tostart, toend, proc = limits | ||||
Bryan O'Sullivan
|
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.stream.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
|
r6949 | self.hunk = hunk | ||
Bryan O'Sullivan
|
r5037 | |||
def newfile(self, hdr): | ||||
self.addcontext([]) | ||||
h = header(hdr) | ||||
self.stream.append(h) | ||||
self.header = h | ||||
def finished(self): | ||||
self.addcontext([]) | ||||
return self.stream | ||||
transitions = { | ||||
'file': {'context': addcontext, | ||||
'file': newfile, | ||||
'hunk': addhunk, | ||||
'range': addrange}, | ||||
'context': {'file': newfile, | ||||
'hunk': addhunk, | ||||
'range': addrange}, | ||||
'hunk': {'context': addcontext, | ||||
'file': newfile, | ||||
'range': addrange}, | ||||
'range': {'context': addcontext, | ||||
'hunk': addhunk}, | ||||
} | ||||
Thomas Arendsen Hein
|
r5143 | |||
Bryan O'Sullivan
|
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() | ||||
def filterpatch(ui, chunks): | ||||
Kirill Smelkov
|
r5826 | """Interactively filter patch chunks into applied-only chunks""" | ||
Bryan O'Sullivan
|
r5037 | chunks = list(chunks) | ||
chunks.reverse() | ||||
Benoit Boissinot
|
r8460 | seen = set() | ||
Bryan O'Sullivan
|
r5037 | def consumefile(): | ||
Kirill Smelkov
|
r5826 | """fetch next portion from chunks until a 'header' is seen | ||
NB: header == new-file mark | ||||
""" | ||||
Bryan O'Sullivan
|
r5037 | consumed = [] | ||
while chunks: | ||||
if isinstance(chunks[-1], header): | ||||
break | ||||
else: | ||||
consumed.append(chunks.pop()) | ||||
return consumed | ||||
Kirill Smelkov
|
r5826 | |||
resp_all = [None] # this two are changed from inside prompt, | ||||
resp_file = [None] # so can't be usual variables | ||||
applied = {} # 'filename' -> [] of chunks | ||||
Bryan O'Sullivan
|
r5154 | def prompt(query): | ||
Kirill Smelkov
|
r5826 | """prompt query, and process base inputs | ||
Thomas Arendsen Hein
|
r6210 | |||
Kirill Smelkov
|
r5826 | - y/n for the rest of file | ||
- y/n for the rest | ||||
- ? (help) | ||||
- q (quit) | ||||
Martin Geisler
|
r9837 | Returns True/False and sets reps_all and resp_file as | ||
appropriate. | ||||
Kirill Smelkov
|
r5826 | """ | ||
Bryan O'Sullivan
|
r5154 | if resp_all[0] is not None: | ||
return resp_all[0] | ||||
if resp_file[0] is not None: | ||||
return resp_file[0] | ||||
while True: | ||||
Steve Borho
|
r8259 | resps = _('[Ynsfdaq?]') | ||
choices = (_('&Yes, record this change'), | ||||
_('&No, skip this change'), | ||||
_('&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'), | ||||
_('&?')) | ||||
timeless@mozdev.org
|
r9461 | r = ui.promptchoice("%s %s" % (query, resps), choices) | ||
Martin Geisler
|
r10694 | ui.write("\n") | ||
Simon Heimberg
|
r9048 | if r == 7: # ? | ||
Martin Geisler
|
r7015 | doc = gettext(record.__doc__) | ||
Martin Geisler
|
r11236 | c = doc.find('::') + 2 | ||
Martin Geisler
|
r7015 | for l in doc[c:].splitlines(): | ||
Martin Geisler
|
r11236 | if l.startswith(' '): | ||
Matt Mackall
|
r10282 | ui.write(l.strip(), '\n') | ||
Bryan O'Sullivan
|
r5154 | continue | ||
Simon Heimberg
|
r9048 | elif r == 0: # yes | ||
Martin Geisler
|
r9837 | ret = True | ||
Simon Heimberg
|
r9048 | elif r == 1: # no | ||
Martin Geisler
|
r9837 | ret = False | ||
Simon Heimberg
|
r9048 | elif r == 2: # Skip | ||
Martin Geisler
|
r9837 | ret = resp_file[0] = False | ||
Simon Heimberg
|
r9048 | elif r == 3: # file (Record remaining) | ||
Martin Geisler
|
r9837 | ret = resp_file[0] = True | ||
Simon Heimberg
|
r9048 | elif r == 4: # done, skip remaining | ||
Martin Geisler
|
r9837 | ret = resp_all[0] = False | ||
Simon Heimberg
|
r9048 | elif r == 5: # all | ||
Martin Geisler
|
r9837 | ret = resp_all[0] = True | ||
Simon Heimberg
|
r9048 | elif r == 6: # quit | ||
Bryan O'Sullivan
|
r5154 | raise util.Abort(_('user quit')) | ||
Simon Heimberg
|
r9048 | return ret | ||
Rocco Rutte
|
r7910 | pos, total = 0, len(chunks) - 1 | ||
Bryan O'Sullivan
|
r5037 | while chunks: | ||
Benoit Boissinot
|
r9688 | pos = total - len(chunks) + 1 | ||
Bryan O'Sullivan
|
r5037 | chunk = chunks.pop() | ||
if isinstance(chunk, header): | ||||
Kirill Smelkov
|
r5826 | # new-file mark | ||
Bryan O'Sullivan
|
r5154 | resp_file = [None] | ||
Bryan O'Sullivan
|
r5037 | fixoffset = 0 | ||
hdr = ''.join(chunk.header) | ||||
if hdr in seen: | ||||
consumefile() | ||||
continue | ||||
Benoit Boissinot
|
r8460 | seen.add(hdr) | ||
Bryan O'Sullivan
|
r5154 | if resp_all[0] is None: | ||
Bryan O'Sullivan
|
r5037 | chunk.pretty(ui) | ||
Bryan O'Sullivan
|
r5285 | r = prompt(_('examine changes to %s?') % | ||
Bryan O'Sullivan
|
r5154 | _(' and ').join(map(repr, chunk.files()))) | ||
Martin Geisler
|
r9837 | if r: | ||
Bryan O'Sullivan
|
r5037 | applied[chunk.filename()] = [chunk] | ||
if chunk.allhunks(): | ||||
applied[chunk.filename()] += consumefile() | ||||
else: | ||||
consumefile() | ||||
else: | ||||
Kirill Smelkov
|
r5826 | # new hunk | ||
Bryan O'Sullivan
|
r5154 | if resp_file[0] is None and resp_all[0] is None: | ||
Bryan O'Sullivan
|
r5037 | chunk.pretty(ui) | ||
Thomas Arendsen Hein
|
r13075 | r = (total == 1 | ||
and prompt(_('record this change to %r?') % chunk.filename()) | ||||
or prompt(_('record change %d/%d to %r?') % | ||||
(pos, total, chunk.filename()))) | ||||
Martin Geisler
|
r9837 | if r: | ||
Bryan O'Sullivan
|
r5037 | if fixoffset: | ||
chunk = copy.copy(chunk) | ||||
chunk.toline += fixoffset | ||||
applied[chunk.filename()].append(chunk) | ||||
else: | ||||
fixoffset += chunk.removed - chunk.added | ||||
Renato Cunha
|
r11500 | return sum([h for h in applied.itervalues() | ||
if h[0].special() or len(h) > 1], []) | ||||
Bryan O'Sullivan
|
r5037 | |||
def record(ui, repo, *pats, **opts): | ||||
Bryan O'Sullivan
|
r5154 | '''interactively select changes to commit | ||
Martin Geisler
|
r10973 | If a list of files is omitted, all changes reported by :hg:`status` | ||
Martin Geisler
|
r9272 | will be candidates for recording. | ||
Bryan O'Sullivan
|
r5154 | |||
Martin Geisler
|
r10973 | See :hg:`help dates` for a list of formats valid for -d/--date. | ||
Thomas Arendsen Hein
|
r6163 | |||
Martin Geisler
|
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
|
r5154 | |||
Martin Geisler
|
r9157 | y - record this change | ||
n - skip this change | ||||
Bryan O'Sullivan
|
r5154 | |||
Martin Geisler
|
r9157 | s - skip remaining changes to this file | ||
f - record remaining changes to this file | ||||
Bryan O'Sullivan
|
r5154 | |||
Martin Geisler
|
r9157 | d - done, skip remaining changes and files | ||
a - record all changes to all remaining files | ||||
q - quit, recording no changes | ||||
Bryan O'Sullivan
|
r5154 | |||
Nicolas Dumazet
|
r11237 | ? - display help | ||
This command is not available when committing a merge.''' | ||||
Bryan O'Sullivan
|
r5037 | |||
Dan Villiom Podlaski Christiansen
|
r10323 | dorecord(ui, repo, commands.commit, *pats, **opts) | ||
Kirill Smelkov
|
r5830 | |||
Kirill Smelkov
|
r5932 | def qrecord(ui, repo, patch, *pats, **opts): | ||
'''interactively record a new patch | ||||
Kirill Smelkov
|
r5830 | |||
Martin Geisler
|
r10973 | See :hg:`help qnew` & :hg:`help record` for more information and | ||
Martin Geisler
|
r9272 | usage. | ||
Kirill Smelkov
|
r5830 | ''' | ||
try: | ||||
mq = extensions.find('mq') | ||||
except KeyError: | ||||
raise util.Abort(_("'mq' extension not loaded")) | ||||
Dan Villiom Podlaski Christiansen
|
r10323 | def committomq(ui, repo, *pats, **opts): | ||
Kirill Smelkov
|
r5932 | mq.new(ui, repo, patch, *pats, **opts) | ||
Kirill Smelkov
|
r5830 | |||
Kirill Smelkov
|
r5932 | opts = opts.copy() | ||
opts['force'] = True # always 'qnew -f' | ||||
Dan Villiom Podlaski Christiansen
|
r10323 | dorecord(ui, repo, committomq, *pats, **opts) | ||
Kirill Smelkov
|
r5827 | |||
Dan Villiom Podlaski Christiansen
|
r10323 | def dorecord(ui, repo, commitfunc, *pats, **opts): | ||
Matt Mackall
|
r8208 | if not ui.interactive(): | ||
Bryan O'Sullivan
|
r5037 | raise util.Abort(_('running non-interactively, use commit instead')) | ||
Matt Mackall
|
r6600 | def recordfunc(ui, repo, message, match, opts): | ||
Kirill Smelkov
|
r5827 | """This is generic record driver. | ||
Martin Geisler
|
r9087 | Its job is to interactively filter local changes, and accordingly | ||
Kirill Smelkov
|
r5827 | prepare working dir into a state, where the job can be delegated to | ||
non-interactive commit command such as 'commit' or 'qrefresh'. | ||||
After the actual job is done by non-interactive command, working dir | ||||
state is restored to original. | ||||
Nicolas Dumazet
|
r11235 | In the end we'll record interesting changes, and everything else will be | ||
Kirill Smelkov
|
r5827 | left in place, so the user can continue his work. | ||
""" | ||||
Dirkjan Ochtman
|
r7754 | |||
Nicolas Dumazet
|
r11237 | merge = len(repo[None].parents()) > 1 | ||
if merge: | ||||
raise util.Abort(_('cannot partially commit a merge ' | ||||
timeless
|
r13023 | '(use "hg commit" instead)')) | ||
Nicolas Dumazet
|
r11237 | |||
Dirkjan Ochtman
|
r7754 | changes = repo.status(match=match)[:3] | ||
Bryan O'Sullivan
|
r5037 | diffopts = mdiff.diffopts(git=True, nodates=True) | ||
Dirkjan Ochtman
|
r7754 | chunks = patch.diff(repo, changes=changes, opts=diffopts) | ||
Bryan O'Sullivan
|
r5037 | fp = cStringIO.StringIO() | ||
Dirkjan Ochtman
|
r7308 | fp.write(''.join(chunks)) | ||
Bryan O'Sullivan
|
r5037 | fp.seek(0) | ||
Kirill Smelkov
|
r5827 | # 1. filter patch, so we have intending-to apply subset of it | ||
Bryan O'Sullivan
|
r5037 | chunks = filterpatch(ui, parsepatch(fp)) | ||
del fp | ||||
Martin Geisler
|
r8152 | contenders = set() | ||
Bryan O'Sullivan
|
r5037 | for h in chunks: | ||
Matt Mackall
|
r10282 | try: | ||
contenders.update(set(h.files())) | ||||
except AttributeError: | ||||
pass | ||||
Thomas Arendsen Hein
|
r5143 | |||
Dirkjan Ochtman
|
r7754 | changed = changes[0] + changes[1] + changes[2] | ||
newfiles = [f for f in changed if f in contenders] | ||||
Bryan O'Sullivan
|
r5037 | if not newfiles: | ||
ui.status(_('no changes to record\n')) | ||||
return 0 | ||||
Martin Geisler
|
r8152 | modified = set(changes[0]) | ||
Bryan O'Sullivan
|
r5037 | |||
Kirill Smelkov
|
r5827 | # 2. backup changed files, so we can restore them in the end | ||
Bryan O'Sullivan
|
r5037 | backups = {} | ||
backupdir = repo.join('record-backups') | ||||
try: | ||||
os.mkdir(backupdir) | ||||
except OSError, err: | ||||
Bryan O'Sullivan
|
r5129 | if err.errno != errno.EEXIST: | ||
raise | ||||
Bryan O'Sullivan
|
r5037 | try: | ||
Kirill Smelkov
|
r5827 | # backup continues | ||
Bryan O'Sullivan
|
r5037 | for f in newfiles: | ||
if f not in modified: | ||||
continue | ||||
fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.', | ||||
dir=backupdir) | ||||
os.close(fd) | ||||
Martin Geisler
|
r9467 | ui.debug('backup %r as %r\n' % (f, tmpname)) | ||
Bryan O'Sullivan
|
r5037 | util.copyfile(repo.wjoin(f), tmpname) | ||
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
|
r5827 | # 3a. apply filtered patch to clean repo (clean) | ||
Bryan O'Sullivan
|
r5037 | if backups: | ||
Renato Cunha
|
r11564 | hg.revert(repo, repo.dirstate.parents()[0], | ||
lambda key: key in backups) | ||||
Bryan O'Sullivan
|
r5037 | |||
Kirill Smelkov
|
r5827 | # 3b. (apply) | ||
Bryan O'Sullivan
|
r5037 | if dopatch: | ||
Dirkjan Ochtman
|
r6950 | try: | ||
Martin Geisler
|
r9467 | ui.debug('applying patch\n') | ||
Dirkjan Ochtman
|
r6950 | ui.debug(fp.getvalue()) | ||
Steve Borho
|
r7718 | pfiles = {} | ||
Patrick Mezard
|
r8811 | patch.internalpatch(fp, ui, 1, repo.root, files=pfiles, | ||
eolmode=None) | ||||
Martin Geisler
|
r12266 | cmdutil.updatedir(ui, repo, pfiles) | ||
Dirkjan Ochtman
|
r6950 | except patch.PatchError, err: | ||
Patrick Mezard
|
r12674 | raise util.Abort(str(err)) | ||
Bryan O'Sullivan
|
r5037 | del fp | ||
Kirill Smelkov
|
r5827 | # 4. We prepared working directory according to filtered patch. | ||
# Now is the time to delegate the job to commit/qrefresh or the like! | ||||
# it is important to first chdir to repo root -- we'll call a | ||||
# highlevel command with list of pathnames relative to repo root | ||||
cwd = os.getcwd() | ||||
os.chdir(repo.root) | ||||
try: | ||||
Dan Villiom Podlaski Christiansen
|
r10323 | commitfunc(ui, repo, *newfiles, **opts) | ||
Kirill Smelkov
|
r5827 | finally: | ||
os.chdir(cwd) | ||||
Bryan O'Sullivan
|
r5037 | return 0 | ||
finally: | ||||
Kirill Smelkov
|
r5827 | # 5. finally restore backed-up files | ||
Bryan O'Sullivan
|
r5037 | try: | ||
for realname, tmpname in backups.iteritems(): | ||||
Martin Geisler
|
r9467 | ui.debug('restoring %r to %r\n' % (tmpname, realname)) | ||
Bryan O'Sullivan
|
r5128 | util.copyfile(tmpname, repo.wjoin(realname)) | ||
Bryan O'Sullivan
|
r5037 | os.unlink(tmpname) | ||
os.rmdir(backupdir) | ||||
except OSError: | ||||
pass | ||||
Brodie Rao
|
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
|
r5037 | |||
cmdtable = { | ||||
Thomas Arendsen Hein
|
r5040 | "record": | ||
(record, | ||||
Kirill Smelkov
|
r5830 | |||
# add commit options | ||||
commands.table['^commit|ci'][1], | ||||
Thomas Arendsen Hein
|
r5040 | _('hg record [OPTION]... [FILE]...')), | ||
} | ||||
Kirill Smelkov
|
r5830 | |||
Martin Geisler
|
r9710 | def uisetup(ui): | ||
Kirill Smelkov
|
r5830 | try: | ||
mq = extensions.find('mq') | ||||
except KeyError: | ||||
return | ||||
qcmdtable = { | ||||
"qrecord": | ||||
(qrecord, | ||||
Kirill Smelkov
|
r5932 | # add qnew options, except '--force' | ||
Matt Mackall
|
r10890 | [opt for opt in mq.cmdtable['^qnew'][1] if opt[1] != 'force'], | ||
Kirill Smelkov
|
r5830 | |||
Kirill Smelkov
|
r5932 | _('hg qrecord [OPTION]... PATCH [FILE]...')), | ||
Kirill Smelkov
|
r5830 | } | ||
cmdtable.update(qcmdtable) | ||||