Mercurial > hg-stable
comparison mercurial/patch.py @ 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 | 126f527b3ba3 020ee9c781cf |
comparison
equal
deleted
inserted
replaced
4899:1b7bbc4349e7 | 4900:e56c7e05c7e6 |
---|---|
11 import base85, cmdutil, mdiff, util, context, revlog, diffhelpers | 11 import base85, cmdutil, mdiff, util, context, revlog, diffhelpers |
12 import cStringIO, email.Parser, os, popen2, re, sha | 12 import cStringIO, email.Parser, os, popen2, re, sha |
13 import sys, tempfile, zlib | 13 import sys, tempfile, zlib |
14 | 14 |
15 class PatchError(Exception): | 15 class PatchError(Exception): |
16 pass | |
17 | |
18 class NoHunks(PatchError): | |
16 pass | 19 pass |
17 | 20 |
18 # helper functions | 21 # helper functions |
19 | 22 |
20 def copyfile(src, dst, basedir=None): | 23 def copyfile(src, dst, basedir=None): |
71 ui.debug('Subject: %s\n' % subject) | 74 ui.debug('Subject: %s\n' % subject) |
72 if user: | 75 if user: |
73 ui.debug('From: %s\n' % user) | 76 ui.debug('From: %s\n' % user) |
74 diffs_seen = 0 | 77 diffs_seen = 0 |
75 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch') | 78 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch') |
76 | 79 message = '' |
77 for part in msg.walk(): | 80 for part in msg.walk(): |
78 content_type = part.get_content_type() | 81 content_type = part.get_content_type() |
79 ui.debug('Content-Type: %s\n' % content_type) | 82 ui.debug('Content-Type: %s\n' % content_type) |
80 if content_type not in ok_types: | 83 if content_type not in ok_types: |
81 continue | 84 continue |
211 dopatch = GP_PATCH | 214 dopatch = GP_PATCH |
212 | 215 |
213 return (dopatch, gitpatches) | 216 return (dopatch, gitpatches) |
214 | 217 |
215 def patch(patchname, ui, strip=1, cwd=None, files={}): | 218 def patch(patchname, ui, strip=1, cwd=None, files={}): |
216 """apply the patch <patchname> to the working directory. | 219 """apply <patchname> to the working directory. |
217 a list of patched files is returned""" | 220 returns whether patch was applied with fuzz factor.""" |
221 patcher = ui.config('ui', 'patch') | |
222 args = [] | |
223 try: | |
224 if patcher: | |
225 return externalpatch(patcher, args, patchname, ui, strip, cwd, | |
226 files) | |
227 else: | |
228 try: | |
229 return internalpatch(patchname, ui, strip, cwd, files) | |
230 except NoHunks: | |
231 patcher = util.find_exe('gpatch') or util.find_exe('patch') | |
232 ui.debug('no valid hunks found; trying with %r instead\n' % | |
233 patcher) | |
234 if util.needbinarypatch(): | |
235 args.append('--binary') | |
236 return externalpatch(patcher, args, patchname, ui, strip, cwd, | |
237 files) | |
238 except PatchError, err: | |
239 s = str(err) | |
240 if s: | |
241 raise util.Abort(s) | |
242 else: | |
243 raise util.Abort(_('patch failed to apply')) | |
244 | |
245 def externalpatch(patcher, args, patchname, ui, strip, cwd, files): | |
246 """use <patcher> to apply <patchname> to the working directory. | |
247 returns whether patch was applied with fuzz factor.""" | |
248 | |
249 fuzz = False | |
250 if cwd: | |
251 args.append('-d %s' % util.shellquote(cwd)) | |
252 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, | |
253 util.shellquote(patchname))) | |
254 | |
255 for line in fp: | |
256 line = line.rstrip() | |
257 ui.note(line + '\n') | |
258 if line.startswith('patching file '): | |
259 pf = util.parse_patch_output(line) | |
260 printed_file = False | |
261 files.setdefault(pf, (None, None)) | |
262 elif line.find('with fuzz') >= 0: | |
263 fuzz = True | |
264 if not printed_file: | |
265 ui.warn(pf + '\n') | |
266 printed_file = True | |
267 ui.warn(line + '\n') | |
268 elif line.find('saving rejects to file') >= 0: | |
269 ui.warn(line + '\n') | |
270 elif line.find('FAILED') >= 0: | |
271 if not printed_file: | |
272 ui.warn(pf + '\n') | |
273 printed_file = True | |
274 ui.warn(line + '\n') | |
275 code = fp.close() | |
276 if code: | |
277 raise PatchError(_("patch command failed: %s") % | |
278 util.explain_exit(code)[0]) | |
279 return fuzz | |
280 | |
281 def internalpatch(patchname, ui, strip, cwd, files): | |
282 """use builtin patch to apply <patchname> to the working directory. | |
283 returns whether patch was applied with fuzz factor.""" | |
218 fp = file(patchname) | 284 fp = file(patchname) |
219 fuzz = False | |
220 if cwd: | 285 if cwd: |
221 curdir = os.getcwd() | 286 curdir = os.getcwd() |
222 os.chdir(cwd) | 287 os.chdir(cwd) |
223 try: | 288 try: |
224 ret = applydiff(ui, fp, files, strip=strip) | 289 ret = applydiff(ui, fp, files, strip=strip) |
225 except PatchError, err: | 290 finally: |
226 ui.debug(err) | 291 if cwd: |
227 raise util.Abort(_("patch failed to apply")) | 292 os.chdir(curdir) |
228 if cwd: | |
229 os.chdir(curdir) | |
230 if ret < 0: | 293 if ret < 0: |
231 raise util.Abort(_("patch failed to apply")) | 294 raise PatchError |
232 if ret > 0: | 295 return ret > 0 |
233 fuzz = True | |
234 return fuzz | |
235 | 296 |
236 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1 | 297 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1 |
237 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@') | 298 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@') |
238 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)') | 299 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)') |
239 | 300 |
934 if updatedir and git: | 995 if updatedir and git: |
935 updatedir(gitpatches) | 996 updatedir(gitpatches) |
936 if rejects: | 997 if rejects: |
937 return -1 | 998 return -1 |
938 if hunknum == 0 and dopatch and not gitworkdone: | 999 if hunknum == 0 and dopatch and not gitworkdone: |
939 raise PatchError(_("no valid hunks found")) | 1000 raise NoHunks |
940 return err | 1001 return err |
941 | 1002 |
942 def diffopts(ui, opts={}, untrusted=False): | 1003 def diffopts(ui, opts={}, untrusted=False): |
943 def get(key, name=None): | 1004 def get(key, name=None): |
944 return (opts.get(key) or | 1005 return (opts.get(key) or |