Mercurial > hg
changeset 4900:e56c7e05c7e6
patch.py: re-add the ability to use an external patch program
This is now invoked by default only if ui.patch is set. Otherwise, we
use our built-in patch. If that fails because it can't find any valid
hunks, we'll fall back to trying the external patch command.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Tue, 17 Jul 2007 09:39:30 -0700 |
parents | 1b7bbc4349e7 |
children | 27414950abf5 |
files | mercurial/patch.py |
diffstat | 1 files changed, 75 insertions(+), 14 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/patch.py Tue Jul 17 09:39:30 2007 -0700 +++ b/mercurial/patch.py Tue Jul 17 09:39:30 2007 -0700 @@ -15,6 +15,9 @@ class PatchError(Exception): pass +class NoHunks(PatchError): + pass + # helper functions def copyfile(src, dst, basedir=None): @@ -73,7 +76,7 @@ ui.debug('From: %s\n' % user) diffs_seen = 0 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch') - + message = '' for part in msg.walk(): content_type = part.get_content_type() ui.debug('Content-Type: %s\n' % content_type) @@ -213,25 +216,83 @@ return (dopatch, gitpatches) def patch(patchname, ui, strip=1, cwd=None, files={}): - """apply the patch <patchname> to the working directory. - a list of patched files is returned""" + """apply <patchname> to the working directory. + returns whether patch was applied with fuzz factor.""" + patcher = ui.config('ui', 'patch') + args = [] + try: + if patcher: + return externalpatch(patcher, args, patchname, ui, strip, cwd, + files) + else: + try: + return internalpatch(patchname, ui, strip, cwd, files) + except NoHunks: + patcher = util.find_exe('gpatch') or util.find_exe('patch') + ui.debug('no valid hunks found; trying with %r instead\n' % + patcher) + if util.needbinarypatch(): + args.append('--binary') + return externalpatch(patcher, args, patchname, ui, strip, cwd, + files) + except PatchError, err: + s = str(err) + if s: + raise util.Abort(s) + else: + raise util.Abort(_('patch failed to apply')) + +def externalpatch(patcher, args, patchname, ui, strip, cwd, files): + """use <patcher> to apply <patchname> to the working directory. + returns whether patch was applied with fuzz factor.""" + + fuzz = False + if cwd: + args.append('-d %s' % util.shellquote(cwd)) + fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, + util.shellquote(patchname))) + + for line in fp: + line = line.rstrip() + ui.note(line + '\n') + if line.startswith('patching file '): + pf = util.parse_patch_output(line) + printed_file = False + files.setdefault(pf, (None, None)) + elif line.find('with fuzz') >= 0: + fuzz = True + if not printed_file: + ui.warn(pf + '\n') + printed_file = True + ui.warn(line + '\n') + elif line.find('saving rejects to file') >= 0: + ui.warn(line + '\n') + elif line.find('FAILED') >= 0: + if not printed_file: + ui.warn(pf + '\n') + printed_file = True + ui.warn(line + '\n') + code = fp.close() + if code: + raise PatchError(_("patch command failed: %s") % + util.explain_exit(code)[0]) + return fuzz + +def internalpatch(patchname, ui, strip, cwd, files): + """use builtin patch to apply <patchname> to the working directory. + returns whether patch was applied with fuzz factor.""" fp = file(patchname) - fuzz = False if cwd: curdir = os.getcwd() os.chdir(cwd) try: ret = applydiff(ui, fp, files, strip=strip) - except PatchError, err: - ui.debug(err) - raise util.Abort(_("patch failed to apply")) - if cwd: - os.chdir(curdir) + finally: + if cwd: + os.chdir(curdir) if ret < 0: - raise util.Abort(_("patch failed to apply")) - if ret > 0: - fuzz = True - return fuzz + raise PatchError + return ret > 0 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@') @@ -936,7 +997,7 @@ if rejects: return -1 if hunknum == 0 and dopatch and not gitworkdone: - raise PatchError(_("no valid hunks found")) + raise NoHunks return err def diffopts(ui, opts={}, untrusted=False):