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 |