commands.import: refactor patch parsing into patch.extract.
authorVadim Gelfer <vadim.gelfer@gmail.com>
Sat, 12 Aug 2006 13:16:48 -0700
changeset 2866 2893e51407a4
parent 2865 71e78f2ca5ae
child 2867 b5f56d6c62c4
commands.import: refactor patch parsing into patch.extract.
mercurial/commands.py
mercurial/patch.py
--- a/mercurial/commands.py	Sat Aug 12 12:47:18 2006 -0700
+++ b/mercurial/commands.py	Sat Aug 12 13:16:48 2006 -0700
@@ -12,7 +12,7 @@
 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
 demandload(globals(), "fnmatch mdiff patch random signal tempfile time")
 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
-demandload(globals(), "archival cStringIO changegroup email.Parser")
+demandload(globals(), "archival cStringIO changegroup")
 demandload(globals(), "hgweb.server sshserver")
 
 class UnknownCommand(Exception):
@@ -1814,84 +1814,23 @@
     d = opts["base"]
     strip = opts["strip"]
 
-    mailre = re.compile(r'(?:From |[\w-]+:)')
-
-    # attempt to detect the start of a patch
-    # (this heuristic is borrowed from quilt)
-    diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
-                        'retrieving revision [0-9]+(\.[0-9]+)*$|' +
-                        '(---|\*\*\*)[ \t])', re.MULTILINE)
-
     wlock = repo.wlock()
     lock = repo.lock()
 
     for p in patches:
         pf = os.path.join(d, p)
 
-        message = None
-        user = None
-        date = None
-        hgpatch = False
-
-        parser = email.Parser.Parser()
         if pf == '-':
-            msg = parser.parse(sys.stdin)
             ui.status(_("applying patch from stdin\n"))
+            tmpname, message, user, date = patch.extract(ui, sys.stdin)
         else:
-            msg = parser.parse(file(pf))
             ui.status(_("applying %s\n") % p)
-
-        fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
-        tmpfp = os.fdopen(fd, 'w')
+            tmpname, message, user, date = patch.extract(ui, file(pf))
+
+        if tmpname is None:
+            raise util.Abort(_('no diffs found'))
+
         try:
-            message = msg['Subject']
-            if message:
-                message = message.replace('\n\t', ' ')
-                ui.debug('Subject: %s\n' % message)
-            user = msg['From']
-            if user:
-                ui.debug('From: %s\n' % user)
-            diffs_seen = 0
-            ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
-            for part in msg.walk():
-                content_type = part.get_content_type()
-                ui.debug('Content-Type: %s\n' % content_type)
-                if content_type not in ok_types:
-                    continue
-                payload = part.get_payload(decode=True)
-                m = diffre.search(payload)
-                if m:
-                    ui.debug(_('found patch at byte %d\n') % m.start(0))
-                    diffs_seen += 1
-                    hgpatch = False
-                    fp = cStringIO.StringIO()
-                    if message:
-                        fp.write(message)
-                        fp.write('\n')
-                    for line in payload[:m.start(0)].splitlines():
-                        if line.startswith('# HG changeset patch'):
-                            ui.debug(_('patch generated by hg export\n'))
-                            hgpatch = True
-                            # drop earlier commit message content
-                            fp.seek(0)
-                            fp.truncate()
-                        elif hgpatch:
-                            if line.startswith('# User '):
-                                user = line[7:]
-                                ui.debug('From: %s\n' % user)
-                            elif line.startswith("# Date "):
-                                date = line[7:]
-                        if not line.startswith('# '):
-                            fp.write(line)
-                            fp.write('\n')
-                    message = fp.getvalue()
-                    if tmpfp:
-                        tmpfp.write(payload)
-                        if not payload.endswith('\n'):
-                            tmpfp.write('\n')
-                elif not diffs_seen and message and content_type == 'text/plain':
-                    message += '\n' + payload
-
             if opts['message']:
                 # pickup the cmdline msg
                 message = opts['message']
@@ -1903,10 +1842,6 @@
                 message = None
             ui.debug(_('message:\n%s\n') % message)
 
-            tmpfp.close()
-            if not diffs_seen:
-                raise util.Abort(_('no diffs found'))
-
             files = patch.patch(strip, tmpname, ui, cwd=repo.root)
             removes = []
             if len(files) > 0:
--- a/mercurial/patch.py	Sat Aug 12 12:47:18 2006 -0700
+++ b/mercurial/patch.py	Sat Aug 12 13:16:48 2006 -0700
@@ -6,8 +6,92 @@
 # of the GNU General Public License, incorporated herein by reference.
 
 from demandload import demandload
+from i18n import gettext as _
 demandload(globals(), "util")
-demandload(globals(), "os re shutil tempfile")
+demandload(globals(), "cStringIO email.Parser os re shutil tempfile")
+
+def extract(ui, fileobj):
+    '''extract patch from data read from fileobj.
+
+    patch can be normal patch or contained in email message.
+
+    return tuple (filename, message, user, date). any item in returned
+    tuple can be None.  if filename is None, fileobj did not contain
+    patch. caller must unlink filename when done.'''
+
+    # attempt to detect the start of a patch
+    # (this heuristic is borrowed from quilt)
+    diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
+                        'retrieving revision [0-9]+(\.[0-9]+)*$|' +
+                        '(---|\*\*\*)[ \t])', re.MULTILINE)
+
+    fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
+    tmpfp = os.fdopen(fd, 'w')
+    try:
+        hgpatch = False
+
+        msg = email.Parser.Parser().parse(fileobj)
+
+        message = msg['Subject']
+        user = msg['From']
+        # should try to parse msg['Date']
+        date = None
+
+        if message:
+            message = message.replace('\n\t', ' ')
+            ui.debug('Subject: %s\n' % message)
+        if user:
+            ui.debug('From: %s\n' % user)
+        diffs_seen = 0
+        ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
+
+        for part in msg.walk():
+            content_type = part.get_content_type()
+            ui.debug('Content-Type: %s\n' % content_type)
+            if content_type not in ok_types:
+                continue
+            payload = part.get_payload(decode=True)
+            m = diffre.search(payload)
+            if m:
+                ui.debug(_('found patch at byte %d\n') % m.start(0))
+                diffs_seen += 1
+                cfp = cStringIO.StringIO()
+                if message:
+                    cfp.write(message)
+                    cfp.write('\n')
+                for line in payload[:m.start(0)].splitlines():
+                    if line.startswith('# HG changeset patch'):
+                        ui.debug(_('patch generated by hg export\n'))
+                        hgpatch = True
+                        # drop earlier commit message content
+                        cfp.seek(0)
+                        cfp.truncate()
+                    elif hgpatch:
+                        if line.startswith('# User '):
+                            user = line[7:]
+                            ui.debug('From: %s\n' % user)
+                        elif line.startswith("# Date "):
+                            date = line[7:]
+                    if not line.startswith('# '):
+                        cfp.write(line)
+                        cfp.write('\n')
+                message = cfp.getvalue()
+                if tmpfp:
+                    tmpfp.write(payload)
+                    if not payload.endswith('\n'):
+                        tmpfp.write('\n')
+            elif not diffs_seen and message and content_type == 'text/plain':
+                message += '\n' + payload
+    except:
+        tmpfp.close()
+        os.unlink(tmpname)
+        raise
+
+    tmpfp.close()
+    if not diffs_seen:
+        os.unlink(tmpname)
+        return None, message, user, date
+    return tmpname, message, user, date
 
 def readgitpatch(patchname):
     """extract git-style metadata about patches from <patchname>"""