mercurial/patch.py
changeset 12671 1b4e3152da13
parent 12670 d82d40ff9860
child 12673 9ad16d1bce4b
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: