mercurial/patch.py
changeset 3100 09e8aecd8016
parent 3093 f422c8265ae5
child 3159 e43fd1623fe1
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -9,7 +9,8 @@ from demandload import demandload
 from i18n import gettext as _
 from node import *
 demandload(globals(), "cmdutil mdiff util")
-demandload(globals(), "cStringIO email.Parser errno os re shutil sys tempfile")
+demandload(globals(), '''cStringIO email.Parser errno os re shutil sys tempfile
+                         popen2''')
 
 # helper functions
 
@@ -182,7 +183,7 @@ def readgitpatch(patchname):
 
     return (dopatch, gitpatches)
 
-def dogitpatch(patchname, gitpatches):
+def dogitpatch(patchname, gitpatches, cwd=None):
     """Preprocess git patch so that vanilla patch can handle it"""
     pf = file(patchname)
     pfline = 1
@@ -196,7 +197,7 @@ def dogitpatch(patchname, gitpatches):
             if not p.copymod:
                 continue
 
-            copyfile(p.oldpath, p.path)
+            copyfile(p.oldpath, p.path, basedir=cwd)
 
             # rewrite patch hunk
             while pfline < p.lineno:
@@ -227,23 +228,20 @@ def patch(patchname, ui, strip=1, cwd=No
     """apply the patch <patchname> to the working directory.
     a list of patched files is returned"""
 
-    (dopatch, gitpatches) = readgitpatch(patchname)
+    # helper function
+    def __patch(patchname):
+        """patch and updates the files and fuzz variables"""
+        files = {}
+        fuzz = False
 
-    files = {}
-    fuzz = False
-    if dopatch:
-        if dopatch == 'filter':
-            patchname = dogitpatch(patchname, gitpatches)
-        patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
+        patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
+                                    'patch')
         args = []
         if cwd:
             args.append('-d %s' % util.shellquote(cwd))
         fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
                                            util.shellquote(patchname)))
 
-        if dopatch == 'filter':
-            False and os.unlink(patchname)
-
         for line in fp:
             line = line.rstrip()
             ui.note(line + '\n')
@@ -264,11 +262,24 @@ def patch(patchname, ui, strip=1, cwd=No
                     ui.warn(pf + '\n')
                     printed_file = True
                 ui.warn(line + '\n')
-            
         code = fp.close()
         if code:
             raise util.Abort(_("patch command failed: %s") %
                              util.explain_exit(code)[0])
+        return files, fuzz
+
+    (dopatch, gitpatches) = readgitpatch(patchname)
+
+    if dopatch:
+        if dopatch == 'filter':
+            patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
+        try:
+            files, fuzz = __patch(patchname)
+        finally:
+            if dopatch == 'filter':
+                os.unlink(patchname)
+    else:
+        files, fuzz = {}, False
 
     for gp in gitpatches:
         files[gp.path] = (gp.op, gp)
@@ -492,7 +503,10 @@ def diff(repo, node1=None, node2=None, f
                     header.append('deleted file mode %s\n' % mode)
             else:
                 omode = gitmode(mmap.execf(f))
-                nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
+                if node2:
+                    nmode = gitmode(mmap2.execf(f))
+                else:
+                    nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
                 addmodehdr(header, omode, nmode)
             r = None
             if dodiff:
@@ -537,3 +551,24 @@ def export(repo, revs, template='hg-%h.p
 
     for seqno, cset in enumerate(revs):
         single(cset, seqno, fp)
+
+def diffstat(patchlines):
+    fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
+    try:
+        p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
+        try:
+            for line in patchlines: print >> p.tochild, line
+            p.tochild.close()
+            if p.wait(): return
+            fp = os.fdopen(fd, 'r')
+            stat = []
+            for line in fp: stat.append(line.lstrip())
+            last = stat.pop()
+            stat.insert(0, last)
+            stat = ''.join(stat)
+            if stat.startswith('0 files'): raise ValueError
+            return stat
+        except: raise
+    finally:
+        try: os.unlink(name)
+        except: pass