diff mercurial/patch.py @ 24265:dc655360bccb

record: move parsepatch from record to patch Part of a series of patches to move record from hgext to core
author Laurent Charignon <lcharignon@fb.com>
date Mon, 09 Mar 2015 13:16:25 -0700
parents c4205452f1b7
children cf7d252d8c30
line wrap: on
line diff
--- a/mercurial/patch.py	Mon Mar 09 13:14:31 2015 -0700
+++ b/mercurial/patch.py	Mon Mar 09 13:16:25 2015 -0700
@@ -1191,6 +1191,86 @@
             return s
     return s[:i]
 
+def parsepatch(fp):
+    """patch -> [] of headers -> [] of hunks """
+    class parser(object):
+        """patch parsing state machine"""
+        def __init__(self):
+            self.fromline = 0
+            self.toline = 0
+            self.proc = ''
+            self.header = None
+            self.context = []
+            self.before = []
+            self.hunk = []
+            self.headers = []
+
+        def addrange(self, limits):
+            fromstart, fromend, tostart, toend, proc = limits
+            self.fromline = int(fromstart)
+            self.toline = int(tostart)
+            self.proc = proc
+
+        def addcontext(self, context):
+            if self.hunk:
+                h = recordhunk(self.header, self.fromline, self.toline,
+                        self.proc, self.before, self.hunk, context)
+                self.header.hunks.append(h)
+                self.fromline += len(self.before) + h.removed
+                self.toline += len(self.before) + h.added
+                self.before = []
+                self.hunk = []
+                self.proc = ''
+            self.context = context
+
+        def addhunk(self, hunk):
+            if self.context:
+                self.before = self.context
+                self.context = []
+            self.hunk = hunk
+
+        def newfile(self, hdr):
+            self.addcontext([])
+            h = header(hdr)
+            self.headers.append(h)
+            self.header = h
+
+        def addother(self, line):
+            pass # 'other' lines are ignored
+
+        def finished(self):
+            self.addcontext([])
+            return self.headers
+
+        transitions = {
+            'file': {'context': addcontext,
+                     'file': newfile,
+                     'hunk': addhunk,
+                     'range': addrange},
+            'context': {'file': newfile,
+                        'hunk': addhunk,
+                        'range': addrange,
+                        'other': addother},
+            'hunk': {'context': addcontext,
+                     'file': newfile,
+                     'range': addrange},
+            'range': {'context': addcontext,
+                      'hunk': addhunk},
+            'other': {'other': addother},
+            }
+
+    p = parser()
+
+    state = 'context'
+    for newstate, data in scanpatch(fp):
+        try:
+            p.transitions[state][newstate](p, data)
+        except KeyError:
+            raise PatchError('unhandled transition: %s -> %s' %
+                                   (state, newstate))
+        state = newstate
+    return p.finished()
+
 def pathtransform(path, strip, prefix):
     '''turn a path from a patch into a path suitable for the repository