comparison mercurial/patch.py @ 12671:1b4e3152da13

patch: remove internal patcher fallback and NoHunk error By now the internal patcher is probably more reliable than anything we might find on the command line.
author Patrick Mezard <pmezard@gmail.com>
date Sat, 09 Oct 2010 15:13:08 -0500
parents d82d40ff9860
children 9ad16d1bce4b
comparison
equal deleted inserted replaced
12670:d82d40ff9860 12671:1b4e3152da13
14 import base85, mdiff, util, diffhelpers, copies, encoding 14 import base85, mdiff, util, diffhelpers, copies, encoding
15 15
16 gitre = re.compile('diff --git a/(.*) b/(.*)') 16 gitre = re.compile('diff --git a/(.*) b/(.*)')
17 17
18 class PatchError(Exception): 18 class PatchError(Exception):
19 pass
20
21 class NoHunks(PatchError):
22 pass 19 pass
23 20
24 # helper functions 21 # helper functions
25 22
26 def copyfile(src, dst, basedir): 23 def copyfile(src, dst, basedir):
995 lr = linereader(fp) 992 lr = linereader(fp)
996 # gitworkdone is True if a git operation (copy, rename, ...) was 993 # gitworkdone is True if a git operation (copy, rename, ...) was
997 # performed already for the current file. Useful when the file 994 # performed already for the current file. Useful when the file
998 # section may have no hunk. 995 # section may have no hunk.
999 gitworkdone = False 996 gitworkdone = False
1000 empty = None
1001 997
1002 while True: 998 while True:
1003 newfile = newgitfile = False 999 newfile = newgitfile = False
1004 x = lr.readline() 1000 x = lr.readline()
1005 if not x: 1001 if not x:
1007 if current_hunk: 1003 if current_hunk:
1008 if x.startswith('\ '): 1004 if x.startswith('\ '):
1009 current_hunk.fix_newline() 1005 current_hunk.fix_newline()
1010 yield 'hunk', current_hunk 1006 yield 'hunk', current_hunk
1011 current_hunk = None 1007 current_hunk = None
1012 empty = False
1013 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or 1008 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
1014 ((context is not False) and x.startswith('***************')))): 1009 ((context is not False) and x.startswith('***************')))):
1015 try: 1010 try:
1016 if context is None and x.startswith('***************'): 1011 if context is None and x.startswith('***************'):
1017 context = True 1012 context = True
1025 continue 1020 continue
1026 hunknum += 1 1021 hunknum += 1
1027 if emitfile: 1022 if emitfile:
1028 emitfile = False 1023 emitfile = False
1029 yield 'file', (afile, bfile, current_hunk) 1024 yield 'file', (afile, bfile, current_hunk)
1030 empty = False
1031 elif state == BFILE and x.startswith('GIT binary patch'): 1025 elif state == BFILE and x.startswith('GIT binary patch'):
1032 current_hunk = binhunk(changed[bfile]) 1026 current_hunk = binhunk(changed[bfile])
1033 hunknum += 1 1027 hunknum += 1
1034 if emitfile: 1028 if emitfile:
1035 emitfile = False 1029 emitfile = False
1036 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk) 1030 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
1037 empty = False
1038 current_hunk.extract(lr) 1031 current_hunk.extract(lr)
1039 elif x.startswith('diff --git'): 1032 elif x.startswith('diff --git'):
1040 # check for git diff, scanning the whole patch file if needed 1033 # check for git diff, scanning the whole patch file if needed
1041 m = gitre.match(x) 1034 m = gitre.match(x)
1042 gitworkdone = False 1035 gitworkdone = False
1081 context = True 1074 context = True
1082 afile = parsefilename(x) 1075 afile = parsefilename(x)
1083 bfile = parsefilename(l2) 1076 bfile = parsefilename(l2)
1084 1077
1085 if newfile: 1078 if newfile:
1086 if empty:
1087 raise NoHunks
1088 empty = not gitworkdone
1089 gitworkdone = False 1079 gitworkdone = False
1090 1080
1091 if newgitfile or newfile: 1081 if newgitfile or newfile:
1092 emitfile = True 1082 emitfile = True
1093 state = BFILE 1083 state = BFILE
1094 hunknum = 0 1084 hunknum = 0
1095 if current_hunk: 1085 if current_hunk:
1096 if current_hunk.complete(): 1086 if current_hunk.complete():
1097 yield 'hunk', current_hunk 1087 yield 'hunk', current_hunk
1098 empty = False
1099 else: 1088 else:
1100 raise PatchError(_("malformed patch %s %s") % (afile, 1089 raise PatchError(_("malformed patch %s %s") % (afile,
1101 current_hunk.desc)) 1090 current_hunk.desc))
1102
1103 if (empty is None and not gitworkdone) or empty:
1104 raise NoHunks
1105
1106 1091
1107 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'): 1092 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'):
1108 """Reads a patch from fp and tries to apply it. 1093 """Reads a patch from fp and tries to apply it.
1109 1094
1110 The dict 'changed' is filled in with all of the filenames changed 1095 The dict 'changed' is filled in with all of the filenames changed
1268 files = {} 1253 files = {}
1269 try: 1254 try:
1270 if patcher: 1255 if patcher:
1271 return externalpatch(patcher, args, patchname, ui, strip, cwd, 1256 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1272 files) 1257 files)
1273 else: 1258 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1274 try:
1275 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1276 except NoHunks:
1277 ui.warn(_('internal patcher failed\n'
1278 'please report details to '
1279 'http://mercurial.selenic.com/bts/\n'
1280 'or mercurial@selenic.com\n'))
1281 patcher = (util.find_exe('gpatch') or util.find_exe('patch')
1282 or 'patch')
1283 ui.debug('no valid hunks found; trying with %r instead\n' %
1284 patcher)
1285 if util.needbinarypatch():
1286 args.append('--binary')
1287 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1288 files)
1289 except PatchError, err: 1259 except PatchError, err:
1290 s = str(err) 1260 s = str(err)
1291 if s: 1261 if s:
1292 raise util.Abort(s) 1262 raise util.Abort(s)
1293 else: 1263 else: