1. TortoiseHg
  2. TortoiseHg
  3. thg

Commits

Steve Borho  committed 0146adf

remove unused tortoisehg/util/prej.py

  • Participants
  • Parent commits e1ad148
  • Branches default

Comments (0)

Files changed (1)

File tortoisehg/util/prej.py

  • Ignore whitespace
-# prej.py - mpatch functionality hacked into TortoiseHg
-#
-# Copyright 2006 Brendan Cully <brendan@kublai.com>
-# Copyright 2006 Chris Mason <chris.mason@oracle.com>
-# Copyright 2010 Steve Borho <steve@borho.org>
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-
-# This file is an amalgam of two files from the mpatch tool source:
-# mpatch/patch.py - which started life as Mercurial's patch.py
-# cmd/mpatch      - mpatch's run script
-#
-# Where possible, it has been tied back into the current Mercurial
-# sources.
-
-from mercurial.i18n import _
-from mercurial import demandimport
-demandimport.disable()
-from mercurial import base85, util, diffhelpers
-from mercurial.patch import PatchError, copyfile
-demandimport.enable()
-
-import cStringIO, os, re, zlib
-import bisect
-
-# public functions
-
-GP_PATCH  = 1 << 0  # we have to run patch
-GP_FILTER = 1 << 1  # there's some copy/rename operation
-GP_BINARY = 1 << 2  # there's a binary patch
-
-def readgitpatch(fp, firstline):
-    """extract git-style metadata about patches from <patchname>"""
-    class gitpatch:
-        "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
-        def __init__(self, path):
-            self.path = path
-            self.oldpath = None
-            self.mode = None
-            self.op = 'MODIFY'
-            self.copymod = False
-            self.lineno = 0
-            self.binary = False
-
-    def reader(fp, firstline):
-        yield firstline
-        for line in fp:
-            yield line
-
-    # Filter patch for git information
-    gitre = re.compile('diff --git a/(.*) b/(.*)')
-    gitpatches = []
-    # Can have a git patch with only metadata, causing patch to complain
-    dopatch = 0
-    gp = None
-
-    lineno = 0
-    for line in reader(fp, firstline):
-        lineno += 1
-        if line.startswith('diff --git'):
-            m = gitre.match(line)
-            if m:
-                if gp:
-                    gitpatches.append(gp)
-                src, dst = m.group(1, 2)
-                gp = gitpatch(dst)
-                gp.lineno = lineno
-        elif gp:
-            if line.startswith('--- '):
-                if gp.op in ('COPY', 'RENAME'):
-                    gp.copymod = True
-                    dopatch |= GP_FILTER
-                gitpatches.append(gp)
-                gp = None
-                dopatch |= GP_PATCH
-                continue
-            if line.startswith('rename from '):
-                gp.op = 'RENAME'
-                gp.oldpath = line[12:].rstrip()
-            elif line.startswith('rename to '):
-                gp.path = line[10:].rstrip()
-            elif line.startswith('copy from '):
-                gp.op = 'COPY'
-                gp.oldpath = line[10:].rstrip()
-            elif line.startswith('copy to '):
-                gp.path = line[8:].rstrip()
-            elif line.startswith('deleted file'):
-                gp.op = 'DELETE'
-            elif line.startswith('new file mode '):
-                gp.op = 'ADD'
-                gp.mode = int(line.rstrip()[-3:], 8)
-            elif line.startswith('new mode '):
-                gp.mode = int(line.rstrip()[-3:], 8)
-            elif line.startswith('GIT binary patch'):
-                dopatch |= GP_BINARY
-                gp.binary = True
-    if gp:
-        gitpatches.append(gp)
-
-    if not gitpatches:
-        dopatch = GP_PATCH
-
-    return (dopatch, gitpatches)
-
-# @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
-unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
-contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
-
-class patchfile:
-    def __init__(self, ui, fname):
-        self.fname = fname
-        self.ui = ui
-        try:
-            fp = file(fname, 'rb')
-            self.lines = fp.readlines()
-            self.exists = True
-        except IOError:
-            dirname = os.path.dirname(fname)
-            if dirname and not os.path.isdir(dirname):
-                dirs = dirname.split(os.path.sep)
-                d = ""
-                for x in dirs:
-                    d = os.path.join(d, x)
-                    if not os.path.isdir(d):
-                        os.mkdir(d)
-            self.lines = []
-            self.exists = False
-            
-        self.hash = {}
-        self.dirty = 0
-        self.offset = 0
-        self.rej = []
-        self.fileprinted = False
-        self.printfile(False)
-        self.hunks = 0
-
-    def printfile(self, warn):
-        if self.fileprinted:
-            return
-        if warn or self.ui.verbose:
-            self.fileprinted = True
-        s = _("patching file %s\n") % self.fname
-        if warn:
-            self.ui.warn(s)
-        else:
-            self.ui.note(s)
-
-
-    def findlines(self, l, linenum):
-        # looks through the hash and finds candidate lines.  The
-        # result is a list of line numbers sorted based on distance
-        # from linenum
-        def sorter(a, b):
-            vala = abs(a - linenum)
-            valb = abs(b - linenum)
-            return cmp(vala, valb)
-            
-        try:
-            cand = self.hash[l]
-        except:
-            return []
-
-        if len(cand) > 1:
-            # resort our list of potentials forward then back.
-            cand.sort(cmp=sorter)
-        return cand
-
-    def hashlines(self):
-        self.hash = {}
-        for x in xrange(len(self.lines)):
-            s = self.lines[x]
-            self.hash.setdefault(s, []).append(x)
-
-    def write_rej(self):
-        # our rejects are a little different from patch(1).  This always
-        # creates rejects in the same form as the original patch.  A file
-        # header is inserted so that you can run the reject through patch again
-        # without having to type the filename.
-
-        if not self.rej:
-            return
-
-        fname = self.fname + ".rej"
-        self.ui.warn(
-            _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
-            (len(self.rej), self.hunks, fname))
-        try: os.unlink(fname)
-        except:
-            pass
-        fp = file(fname, 'wb')
-        base = os.path.basename(self.fname)
-        fp.write("--- %s\n+++ %s\n" % (base, base))
-        for x in self.rej:
-            for l in x.hunk:
-                fp.write(l)
-                if l[-1] != '\n':
-                    fp.write("\n\ No newline at end of file\n")
-
-    def write(self, dest=None):
-        if self.dirty:
-            if not dest:
-                dest = self.fname
-            st = None
-            try:
-                st = os.lstat(dest)
-                if st.st_nlink > 1:
-                    os.unlink(dest)
-            except: pass
-            fp = file(dest, 'wb')
-            if st:
-                os.chmod(dest, st.st_mode)
-            fp.writelines(self.lines)
-            fp.close()
-
-    def close(self):
-        self.write()
-        self.write_rej()
-
-    def apply(self, h, reverse):
-        if not h.complete():
-            raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
-                            (h.number, h.desc, len(h.a), h.lena, len(h.b),
-                            h.lenb))
-
-        self.hunks += 1
-        if reverse:
-            h.reverse()
-
-        if self.exists and h.createfile():
-            self.ui.warn(_("file %s already exists\n") % self.fname)
-            self.rej.append(h)
-            return -1
-
-        if isinstance(h, binhunk):
-            if h.rmfile():
-                os.unlink(self.fname)
-            else:
-                self.lines[:] = h.new()
-                self.offset += len(h.new())
-                self.dirty = 1
-            return 0
-
-        # fast case first, no offsets, no fuzz
-        old = h.old()
-        # patch starts counting at 1 unless we are adding the file
-        if h.starta == 0:
-            start = 0
-        else:
-            start = h.starta + self.offset - 1
-        orig_start = start
-        if diffhelpers.testhunk(old, self.lines, start) == 0:
-            if h.rmfile():
-                os.unlink(self.fname)
-            else:
-                self.lines[start : start + h.lena] = h.new()
-                self.offset += h.lenb - h.lena
-                self.dirty = 1
-            return 0
-
-        # ok, we couldn't match the hunk.  Lets look for offsets and fuzz it
-        self.hashlines()
-        if h.hunk[-1][0] != ' ':
-            # if the hunk tried to put something at the bottom of the file
-            # override the start line and use eof here
-            search_start = len(self.lines)
-        else:
-            search_start = orig_start
-
-        for fuzzlen in xrange(3):
-            for toponly in [ True, False ]:
-                old = h.old(fuzzlen, toponly)
-
-                cand = self.findlines(old[0][1:], search_start)
-                for l in cand:
-                    if diffhelpers.testhunk(old, self.lines, l) == 0:
-                        newlines = h.new(fuzzlen, toponly)
-                        self.lines[l : l + len(old)] = newlines
-                        self.offset += len(newlines) - len(old)
-                        self.dirty = 1
-                        offset = l - orig_start - fuzzlen
-                        if fuzzlen:
-                            msg = _("Hunk #%d succeeded at %d "
-                                    "with fuzz %d "
-                                    "(offset %d lines).\n")
-                            self.printfile(True)
-                            self.ui.warn(msg %
-                                (h.number, l + 1, fuzzlen, offset))
-                        else:
-                            msg = _("Hunk #%d succeeded at %d "
-                                    "(offset %d lines).\n")
-                            self.ui.note(msg % (h.number, l + 1, offset))
-                        return fuzzlen
-        self.printfile(True)
-        self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
-        self.rej.append(h)
-        return -1
-
-class hunk:
-    def __init__(self, desc, num, lr, context):
-        self.number = num
-        self.desc = desc
-        self.hunk = [ desc ]
-        self.a = []
-        self.b = []
-        if context:
-            self.read_context_hunk(lr)
-        else:
-            self.read_unified_hunk(lr)
-
-    def read_unified_hunk(self, lr):
-        m = unidesc.match(self.desc)
-        if not m:
-            raise PatchError(_("bad hunk #%d") % self.number)
-        self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
-        if self.lena == None:
-            self.lena = 1
-        else:
-            self.lena = int(self.lena)
-        if self.lenb == None:
-            self.lenb = 1
-        else:
-            self.lenb = int(self.lenb)
-        self.starta = int(self.starta)
-        self.startb = int(self.startb)
-        diffhelpers.addlines(lr.fp, self.hunk, self.lena, self.lenb, self.a, self.b)
-        # if we hit eof before finishing out the hunk, the last line will
-        # be zero length.  Lets try to fix it up.
-        while len(self.hunk[-1]) == 0:
-                del self.hunk[-1]
-                del self.a[-1]
-                del self.b[-1]
-                self.lena -= 1
-                self.lenb -= 1
-
-    def read_context_hunk(self, lr):
-        self.desc = lr.readline()
-        m = contextdesc.match(self.desc)
-        if not m:
-            raise PatchError(_("bad hunk #%d") % self.number)
-        foo, self.starta, foo2, aend, foo3 = m.groups()
-        self.starta = int(self.starta)
-        if aend == None:
-            aend = self.starta
-        self.lena = int(aend) - self.starta
-        if self.starta:
-            self.lena += 1
-        for x in xrange(self.lena):
-            l = lr.readline()
-            if l.startswith('---'):
-                lr.push(l)
-                break
-            s = l[2:]
-            if l.startswith('- ') or l.startswith('! '):
-                u = '-' + s
-            elif l.startswith('  '):
-                u = ' ' + s
-            else:
-                raise PatchError(_("bad hunk #%d old text line %d") %
-                                 (self.number, x))
-            self.a.append(u)
-            self.hunk.append(u)
-
-        l = lr.readline()
-        if l.startswith('\ '):
-            s = self.a[-1][:-1]
-            self.a[-1] = s
-            self.hunk[-1] = s
-            l = lr.readline()
-        m = contextdesc.match(l)
-        if not m:
-            raise PatchError(_("bad hunk #%d") % self.number)
-        foo, self.startb, foo2, bend, foo3 = m.groups()
-        self.startb = int(self.startb)
-        if bend == None:
-            bend = self.startb
-        self.lenb = int(bend) - self.startb
-        if self.startb:
-            self.lenb += 1
-        hunki = 1
-        for x in xrange(self.lenb):
-            l = lr.readline()
-            if l.startswith('\ '):
-                s = self.b[-1][:-1]
-                self.b[-1] = s
-                self.hunk[hunki-1] = s
-                continue
-            if not l:
-                lr.push(l)
-                break
-            s = l[2:]
-            if l.startswith('+ ') or l.startswith('! '):
-                u = '+' + s
-            elif l.startswith('  '):
-                u = ' ' + s
-            elif len(self.b) == 0:
-                # this can happen when the hunk does not add any lines
-                lr.push(l)
-                break
-            else:
-                raise PatchError(_("bad hunk #%d old text line %d") %
-                                 (self.number, x))
-            self.b.append(s)
-            while True:
-                if hunki >= len(self.hunk):
-                    h = ""
-                else:
-                    h = self.hunk[hunki]
-                hunki += 1
-                if h == u:
-                    break
-                elif h.startswith('-'):
-                    continue
-                else:
-                    self.hunk.insert(hunki-1, u)
-                    break
-
-        if not self.a:
-            # this happens when lines were only added to the hunk
-            for x in self.hunk:
-                if x.startswith('-') or x.startswith(' '):
-                    self.a.append(x)
-        if not self.b:
-            # this happens when lines were only deleted from the hunk
-            for x in self.hunk:
-                if x.startswith('+') or x.startswith(' '):
-                    self.b.append(x[1:])
-        # @@ -start,len +start,len @@
-        self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
-                                             self.startb, self.lenb)
-        self.hunk[0] = self.desc
-
-    def reverse(self):
-        origlena = self.lena
-        origstarta = self.starta
-        self.lena = self.lenb
-        self.starta = self.startb
-        self.lenb = origlena
-        self.startb = origstarta
-        self.a = []
-        self.b = []
-        # self.hunk[0] is the @@ description
-        for x in xrange(1, len(self.hunk)):
-            o = self.hunk[x]
-            if o.startswith('-'):
-                n = '+' + o[1:]
-                self.b.append(o[1:])
-            elif o.startswith('+'):
-                n = '-' + o[1:]
-                self.a.append(n)
-            else:
-                n = o
-                self.b.append(o[1:])
-                self.a.append(o)
-            self.hunk[x] = o
-
-    def fix_newline(self):
-        diffhelpers.fix_newline(self.hunk, self.a, self.b)
-
-    def complete(self):
-        return len(self.a) == self.lena and len(self.b) == self.lenb
-
-    def createfile(self):
-        return self.starta == 0 and self.lena == 0
-
-    def rmfile(self):
-        return self.startb == 0 and self.lenb == 0
-
-    def fuzzit(self, l, fuzz, toponly):
-        # this removes context lines from the top and bottom of list 'l'.  It
-        # checks the hunk to make sure only context lines are removed, and then
-        # returns a new shortened list of lines.
-        fuzz = min(fuzz, len(l)-1)
-        if fuzz:
-            top = 0
-            bot = 0
-            hlen = len(self.hunk)
-            for x in xrange(hlen-1):
-                # the hunk starts with the @@ line, so use x+1
-                if self.hunk[x+1][0] == ' ':
-                    top += 1
-                else:
-                    break
-            if not toponly:
-                for x in xrange(hlen-1):
-                    if self.hunk[hlen-bot-1][0] == ' ':
-                        bot += 1
-                    else:
-                        break
-
-            # top and bot now count context in the hunk
-            # adjust them if either one is short
-            context = max(top, bot, 3)
-            if bot < context:
-                bot = max(0, fuzz - (context - bot))
-            else:
-                bot = min(fuzz, bot)
-            if top < context:
-                top = max(0, fuzz - (context - top))
-            else:
-                top = min(fuzz, top)
-
-            return l[top:len(l)-bot]
-        return l
-
-    def old(self, fuzz=0, toponly=False):
-        return self.fuzzit(self.a, fuzz, toponly)
-        
-    def newctrl(self):
-        res = []
-        for x in self.hunk:
-            c = x[0]
-            if c == ' ' or c == '+':
-                res.append(x)
-        return res
-
-    def new(self, fuzz=0, toponly=False):
-        return self.fuzzit(self.b, fuzz, toponly)
-
-class binhunk:
-    'A binary patch file. Only understands literals so far.'
-    def __init__(self, gitpatch):
-        self.gitpatch = gitpatch
-        self.text = None
-        self.hunk = ['GIT binary patch\n']
-
-    def createfile(self):
-        return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
-
-    def rmfile(self):
-        return self.gitpatch.op == 'DELETE'
-
-    def complete(self):
-        return self.text is not None
-
-    def new(self):
-        return [self.text]
-
-    def extract(self, fp):
-        line = fp.readline()
-        self.hunk.append(line)
-        while line and not line.startswith('literal '):
-            line = fp.readline()
-            self.hunk.append(line)
-        if not line:
-            raise PatchError(_('could not extract binary patch'))
-        size = int(line[8:].rstrip())
-        dec = []
-        line = fp.readline()
-        self.hunk.append(line)
-        while len(line) > 1:
-            l = line[0]
-            if l <= 'Z' and l >= 'A':
-                l = ord(l) - ord('A') + 1
-            else:
-                l = ord(l) - ord('a') + 27
-            dec.append(base85.b85decode(line[1:-1])[:l])
-            line = fp.readline()
-            self.hunk.append(line)
-        text = zlib.decompress(''.join(dec))
-        if len(text) != size:
-            raise PatchError(_('binary patch is %d bytes, not %d') %
-                             len(text), size)
-        self.text = text
-
-def parsefilename(str, git=False):
-    # --- filename \t|space stuff
-    if git:
-        return s
-    s = str[4:]
-    i = s.find('\t')
-    if i < 0:
-        i = s.find(' ')
-        if i < 0:
-            return s
-    return s[:i]
-
-def selectfile(afile_orig, bfile_orig, hunk, strip, reverse):
-    def pathstrip(path, count=1):
-        pathlen = len(path)
-        i = 0
-        if count == 0:
-            return path.rstrip()
-        while count > 0:
-            i = path.find('/', i)
-            if i == -1:
-                raise PatchError(_("unable to strip away %d dirs from %s") %
-                                 (count, path))
-            i += 1
-            # consume '//' in the path
-            while i < pathlen - 1 and path[i] == '/':
-                i += 1
-            count -= 1
-        return path[i:].rstrip()
-
-    nulla = afile_orig == "/dev/null"
-    nullb = bfile_orig == "/dev/null"
-    afile = pathstrip(afile_orig, strip)
-    gooda = os.path.exists(afile) and not nulla
-    bfile = pathstrip(bfile_orig, strip)
-    if afile == bfile:
-        goodb = gooda
-    else:
-        goodb = os.path.exists(bfile) and not nullb
-    createfunc = hunk.createfile
-    if reverse:
-        createfunc = hunk.rmfile
-    if not goodb and not gooda and not createfunc():
-        raise PatchError(_("unable to find %s or %s for patching") %
-                         (afile, bfile))
-    if gooda and goodb:
-        fname = bfile
-        if afile in bfile:
-            fname = afile
-    elif gooda:
-        fname = afile
-    elif not nullb:
-        fname = bfile
-        if afile in bfile:
-            fname = afile
-    elif not nulla:
-        fname = afile
-    return fname
-
-class linereader:
-    # simple class to allow pushing lines back into the input stream
-    def __init__(self, fp):
-        self.fp = fp
-        self.buf = []
-
-    def push(self, line):
-        self.buf.append(line)
-
-    def readline(self):
-        if self.buf:
-            l = self.buf[0]
-            del self.buf[0]
-            return l
-        return self.fp.readline()
-
-def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
-              updatedir=None):
-    """reads a patch from fp and tries to apply it.  The dict 'changed' is
-       filled in with all of the filenames changed by the patch.  Returns 0
-       for a clean patch, -1 if any rejects were found and 1 if there was
-       any fuzz.""" 
-
-    def scangitpatch(fp, firstline, cwd=None):
-        '''git patches can modify a file, then copy that file to
-        a new file, but expect the source to be the unmodified form.
-        So we scan the patch looking for that case so we can do
-        the copies ahead of time.'''
-
-        pos = 0
-        try:
-            pos = fp.tell()
-        except IOError:
-            fp = cStringIO.StringIO(fp.read())
-
-        (dopatch, gitpatches) = readgitpatch(fp, firstline)
-        for gp in gitpatches:
-            if gp.copymod:
-                copyfile(gp.oldpath, gp.path, basedir=cwd)
-
-        fp.seek(pos)
-
-        return fp, dopatch, gitpatches
-
-    current_hunk = None
-    current_file = None
-    afile = ""
-    bfile = ""
-    state = None
-    hunknum = 0
-    rejects = 0
-
-    git = False
-    gitre = re.compile('diff --git (a/.*) (b/.*)')
-
-    # our states
-    BFILE = 1
-    err = 0
-    context = None
-    lr = linereader(fp)
-    dopatch = True
-    gitworkdone = False
-
-    while True:
-        newfile = False
-        x = lr.readline()
-        if not x:
-            break
-        if current_hunk:
-            if x.startswith('\ '):
-                current_hunk.fix_newline()
-            ret = current_file.apply(current_hunk, reverse)
-            if ret > 0:
-                err = 1
-            current_hunk = None
-            gitworkdone = False
-        if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
-            ((context or context == None) and x.startswith('***************')))):
-            try:
-                if context == None and x.startswith('***************'):
-                    context = True
-                current_hunk = hunk(x, hunknum + 1, lr, context)
-            except PatchError:
-                current_hunk = None
-                continue
-            hunknum += 1
-            if not current_file:
-                if sourcefile:
-                    current_file = patchfile(ui, sourcefile)
-                else:
-                    current_file = selectfile(afile, bfile, current_hunk,
-                                              strip, reverse)
-                    current_file = patchfile(ui, current_file)
-                changed.setdefault(current_file.fname, (None, None))
-        elif state == BFILE and x.startswith('GIT binary patch'):
-            current_hunk = binhunk(changed[bfile[2:]][1])
-            if not current_file:
-                if sourcefile:
-                    current_file = patchfile(ui, sourcefile)
-                else:
-                    current_file = selectfile(afile, bfile, current_hunk,
-                                              strip, reverse)
-                    current_file = patchfile(ui, current_file)
-            hunknum += 1
-            current_hunk.extract(fp)
-        elif x.startswith('diff --git'):
-            # check for git diff, scanning the whole patch file if needed
-            m = gitre.match(x)
-            if m:
-                afile, bfile = m.group(1, 2)
-                if not git:
-                    git = True
-                    fp, dopatch, gitpatches = scangitpatch(fp, x)
-                    for gp in gitpatches:
-                        changed[gp.path] = (gp.op, gp)
-                # else error?
-                # copy/rename + modify should modify target, not source
-                if changed.get(bfile[2:], (None, None))[0] in ('COPY',
-                                                               'RENAME'):
-                    afile = bfile
-                    gitworkdone = True
-            newfile = True
-        elif x.startswith('---'):
-            # check for a unified diff
-            l2 = lr.readline()
-            if not l2.startswith('+++'):
-                lr.push(l2)
-                continue
-            newfile = True
-            context = False
-            afile = parsefilename(x, git)
-            bfile = parsefilename(l2, git)
-        elif x.startswith('***'):
-            # check for a context diff
-            l2 = lr.readline()
-            if not l2.startswith('---'):
-                lr.push(l2)
-                continue
-            l3 = lr.readline()
-            lr.push(l3)
-            if not l3.startswith("***************"):
-                lr.push(l2)
-                continue
-            newfile = True
-            context = True
-            afile = parsefilename(x, git)
-            bfile = parsefilename(l2, git)
-
-        if newfile:
-            if current_file:
-                current_file.close()
-                rejmerge(current_file)
-                rejects += len(current_file.rej)
-            state = BFILE
-            current_file = None
-            hunknum = 0
-    if current_hunk:
-        if current_hunk.complete():
-            ret = current_file.apply(current_hunk, reverse)
-            if ret > 0:
-                err = 1
-        else:
-            fname = current_file and current_file.fname or None
-            raise PatchError(_("malformed patch %s %s") % (fname,
-                             current_hunk.desc))
-    if current_file:
-        current_file.close()
-        rejmerge(current_file)
-        rejects += len(current_file.rej)
-    if updatedir and git:
-        updatedir(gitpatches)
-    if rejects:
-        return -1
-    if hunknum == 0 and dopatch and not gitworkdone:
-        raise PatchError(_("No valid hunks found"))
-    return err
-
-
-# This portion of the code came from the mpatch script
-
-global_conflicts = 0
-global_rejects = 0
-
-wsre = re.compile('[ \r\t\n]+')
-
-class rejhunk:
-    def __init__(self, h, f):
-        self.hunk = h
-        self.match = []
-        self.score = 0
-        self.start = 0
-        self.conficts = 0
-        self.file = f
-        self.direction = False
-
-    def findline(self, l, min):
-        # look for line l in the file's hash.  min is the minimum line number
-        # to return
-        try:
-            res = self.file.hash[l]
-        except KeyError:
-            return None
-        i = bisect.bisect_right(res, min)
-        if i < len(res):
-            return res[i]
-        return None
-        
-    def findforward(self):
-        # search for the new text of the hunk already in the file.
-        # the file should already be hashed
-        hlines = self.hunk.newctrl()
-        orig_start = self.hunk.startb
-        if len(hlines) < 6:
-            # only guess about applied hunks when there is some context.
-            return False
-        for fuzzlen in xrange(3):
-            lines = self.hunk.fuzzit(hlines, fuzzlen, False)
-            cand = self.file.findlines(lines[0][1:], orig_start)
-            for l in cand:
-                if diffhelpers.testhunk(lines, self.file.lines, l) == 0:
-                    self.file.ui.warn(
-                             _("hunk %d already applied at line %d (fuzz %d)\n"
-                             % (self.hunk.number, l+1, fuzzlen)))
-                    return True
-        return False
-
-    def search(self):
-        # search through the file for a good place to put our hunk.
-        # on return the rejhunk is setup for a call to apply.  We will
-        # either have found a suitable location or have given up and
-        # set things up to put the hunk at the start of the file.
-        self.file.hashlines()
-        if self.findforward():
-            # this hunk was already applied
-            self.score = -2
-            return
-
-        scan = ((False, self.hunk.old(), self.hunk.starta),
-                 (True, self.hunk.newctrl(), self.hunk.startb))
-        last = (-1, -1, 0, [], None)
-        for direction, hlines, hstart in scan:
-            maxlines = min(len(hlines)/2+1, 15)
-            # don't look forward if we already have a conflict free
-            # match of the old text
-            if direction and last[1] > 3 and last[2] == 0:
-                break
-            for i in xrange(maxlines):
-                try:
-                    res = self.file.hash[hlines[i][1:]]
-                except KeyError:
-                    continue
-                for s in res:
-                    start, score, conflicts, match = self.align(s-1, i, hlines)
-                    
-                    # new text matches are more likely to have false positives.
-                    # use a higher bar for them.
-                    if direction and score < 7:
-                        score = -1
-
-                    if score > last[1]:
-                        update = True
-                        # more special checks for replacing an match of the
-                        # old text with the new.  Check for conflicts
-                        # and the size of the total match.
-                        if direction and last[4] == False:
-                            dist = len(match) - len(hlines)
-                            if conflicts and dist > ((len(hlines) * 3) / 2):
-                                update = False
-                        if update:
-                            last = (start, score, conflicts, match, direction)
-                    elif score == last[1]:
-                        distold = abs(last[0] - hstart)
-                        distnew = abs(start - hstart)
-                        if direction == False or last[4] == True:
-                            # we prefer to match the old text, so if
-                            # we don't replace a match of the old text
-                            # with a match of the new
-                            if distnew < distold:
-                                last = (start, score, conflicts, match,
-                                        direction)
-        if last[1] > 3:
-            (self.start, self.score, self.conflicts,
-             self.match, self.direction) = last
-        else:
-            # no good locations found, lets just put it at the start of the
-            # file.
-            self.conflicts = 1
-            
-    def scoreline(self, l):
-        ws = wsre.sub('', l)
-        if len(ws) == 0:
-            return .25
-        return 1
-
-    def apply(self):
-        # after calling search, call apply to merge the hunk into the file.
-        # on return the file is dirtied but not written
-        if self.direction:
-            # when merging into already applied data, the match array
-            # has it all
-            new = []
-        else:
-            new = self.hunk.newctrl()
-        newi = 0
-        filei = self.start
-        lines = []
-        i = 0
-        while i < len(self.match) or newi < len(new):
-            # the order is a little strange.
-            # ctrl of '|' means the file had lines the hunk did not.  These
-            # need to go before any '+' lines from the new hunk
-            if i < len(self.match):
-                l = self.match[i]
-                ctrl = l[0]
-                if ctrl == '|':
-                    lines.append(l[1:])
-                    filei += 1
-                    i += 1
-                    continue
-
-            # find lines added by the hunk
-            if newi < len(new):
-                l = new[newi]
-                if l[0] == '+':
-                    lines.append(l[1:])
-                    newi += 1
-                    continue
-                elif i >= len(self.match):
-                    # if we've gone through the whole match array,
-                    # the new hunk may have context that didn't exist
-                    # in the file.  Just skip past it.
-                    newi += 1
-                    continue
-
-            l = self.match[i]
-            i += 1
-            ctrl = l[0]
-            if ctrl == '-':
-                # deleted by the hunk, skip over it in the file
-                filei += 1
-                pass
-            elif ctrl == '^':
-                # in the hunk but missing from the file.  skip over it in
-                # the hunk
-                newi += 1
-            elif ctrl == '<':
-                # deleted by the hunk but missing from the file.  Let the
-                # user know by inserting the deletion marker
-                lines.append(l)
-            elif ctrl == '+':
-                # only happens when self.direction == True
-                lines.append(l[1:])
-                continue
-            elif ctrl == ' ':
-                # context from the hunk found in the file.  Add it
-                lines.append(l[1:])
-                newi += 1
-                filei += 1
-            else:
-                raise PatchError("unknown control char %s" % l)
-
-        self.file.lines[self.start:filei] = lines
-        self.file.dirty = 1
-
-    def align(self, fstart, hstart, hlines):
-        hcur = hstart
-        fcur = fstart
-        flines = self.file.lines
-        retstart = None
-        match = []
-        score = 0
-        conflicts = 0
-
-        # add deletion markers for any lines removed by the parts of the
-        # hunk we could not find (between 0 and hstart)
-        for i in xrange(hstart):
-            if hlines[i][0] == '-':
-                match.append('<<<<del ' + hlines[i][1:])
-            elif hlines[i][0] == '+':
-                match.append(hlines[i])
-        consec = False
-        last_append = None
-        while hcur < len(hlines):
-            if hcur > len(hlines)/2 and score <= 0:
-                return (-1, -1, 1, [])
-            fnext = self.findline(hlines[hcur][1:], fcur)
-            ctrl = hlines[hcur][0]
-            if fnext == None or (fcur >=0 and fnext - fcur > 20):
-                consec = False
-                fnext = None
-                if ctrl == '-':
-                    # we've failed to find a line the patch wanted to delete.
-                    # record it as a conflict.
-                    match.append('<<<<del ' + hlines[hcur][1:])
-                    conflicts += 1
-                elif ctrl == '+':
-                    match.append(hlines[hcur])
-                    conflicts += 1
-                else:
-                    match.append('^' + hlines[hcur][1:])
-            else:
-                if fcur >= 0 and retstart:
-                    # any lines between fcur and fnext were in the file but
-                    # not the hunk.
-                    # decrement our score for a big block of missing lines
-                    dist = fnext - fcur
-                    while dist > 5:
-                        score -= 1
-                        dist -= 5
-                    for x in xrange(fcur+1, fnext):
-                        match.append('|' + flines[x])
-                if retstart == None:
-                    retstart = fnext
-                # plus one just for finding a match
-                # plus one more if it was a consecutive match
-                # plus one more if it was a match on a line we're deleting
-                # or adding.  But, only do it for non-ws lines and for
-                # lines relatlively close to the last match
-                inc = self.scoreline(hlines[hcur][1:])
-                score += inc
-                if consec and fnext == fcur + 1:
-                    score += inc
-                if ctrl == '-' or ctrl == '+':
-                    ws = hlines[hcur].rstrip()
-                    if len(ws) > 1 and fnext - fcur < 5:
-                        score += inc
-                consec = True
-                # record the line from the hunk we did find
-                if ctrl == '+':
-                    # we matched a line added by the hunk.  The code that
-                    # merges needs to know to inc the file line number to
-                    # include this line, so instead of adding a + ctrl
-                    # use |
-                    match.append('|' + hlines[hcur][1:])
-                else:
-                    match.append(hlines[hcur])
-
-            hcur += 1
-            if fnext != None:
-                fcur = fnext
-        return (retstart, score, conflicts, match)
-
-def rejmerge(pfile):
-    def backup(orig):
-        fname = orig + ".mergebackup"
-        try: os.unlink(fname)
-        except: pass
-        old = file(orig)
-        new = file(fname, 'w')
-        for x in old:
-            new.write(x)
-        return fname
-
-    rej = pfile.rej
-    if not rej:
-        return
-    backupf = None
-    badrej = []
-    for h in rej:
-        r = rejhunk(h, pfile)
-        r.search()
-        if r.score >= 0:
-            if not backupf:
-                backupf = backup(pfile.fname)
-            global global_conflicts
-            global global_rejects
-            if r.direction:
-                s = "warning file %s hunk %d: merging " % (
-                     pfile.fname, h.number)
-                pfile.ui.warn(s + "with changes already applied\n")
-            r.apply()
-            if r.conflicts > 0:
-                global_conflicts += 1
-            global_rejects += 1
-    if backupf:
-        global merge_func
-        pfile.rej = badrej
-        pfile.write()
-        merge_func(pfile.ui, pfile.fname, backupf)
-        try:
-            os.unlink(backupf)
-        except:
-            pass
-
-def updatedir(patches):
-    l = patches.keys()
-    l.sort()
-    for f in l:
-        ctype, gp = patches[f]
-        if gp.mode != None:
-            x = gp.mode & 0100 != 0
-            util.set_exec(gp.path, x)
-
-global merge_func
-def run(ui, rejfile, sourcefile, mergefunc):
-    '''
-    Attempt to apply the patch hunks in rejfile,  When patch fails,
-    run merge_func to launch user's preferred visual diff tool to
-    resolve conflicts.
-    '''
-    global merge_func
-    merge_func = mergefunc
-    diffp = file(rejfile)
-    changed = {}
-    try:
-        ret = applydiff(ui, diffp, changed, strip=1,
-                          sourcefile=sourcefile,
-                          updatedir=updatedir,
-                          reverse=False)
-    except PatchError, inst:
-        sys.stderr.write("Error: %s\n" % inst)