mercurial/patch.py
changeset 4900 e56c7e05c7e6
parent 4899 1b7bbc4349e7
child 4903 020ee9c781cf
child 4961 126f527b3ba3
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