##// END OF EJS Templates
import: parse email messages
Vadim Gelfer -
r2504:158d3d2a default
parent child Browse files
Show More
@@ -12,7 +12,7 b' demandload(globals(), "os re sys signal '
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "archival changegroup")
15 demandload(globals(), "archival cStringIO changegroup email.Parser")
16 demandload(globals(), "hgweb.server sshserver")
16 demandload(globals(), "hgweb.server sshserver")
17
17
18 class UnknownCommand(Exception):
18 class UnknownCommand(Exception):
@@ -1719,11 +1719,15 b' def import_(ui, repo, patch1, *patches, '
1719 If there are outstanding changes in the working directory, import
1719 If there are outstanding changes in the working directory, import
1720 will abort unless given the -f flag.
1720 will abort unless given the -f flag.
1721
1721
1722 If a patch looks like a mail message (its first line starts with
1722 You can import a patch straight from a mail message. Even patches
1723 "From " or looks like an RFC822 header), it will not be applied
1723 as attachments work (body part must be type text/plain or
1724 unless the -f option is used. The importer neither parses nor
1724 text/x-patch to be used). Sender and subject line of email
1725 discards mail headers, so use -f only to override the "mailness"
1725 message are used as default committer and commit message. Any
1726 safety check, not to import a real mail message.
1726 text/plain body part before first diff is added to commit message.
1727
1728 If imported patch was generated by hg export, user and description
1729 from patch override values from message headers and body. Values
1730 given on command line with -m and -u override these.
1727
1731
1728 To read a patch from standard input, use patch name "-".
1732 To read a patch from standard input, use patch name "-".
1729 """
1733 """
@@ -1739,79 +1743,93 b' def import_(ui, repo, patch1, *patches, '
1739
1743
1740 # attempt to detect the start of a patch
1744 # attempt to detect the start of a patch
1741 # (this heuristic is borrowed from quilt)
1745 # (this heuristic is borrowed from quilt)
1742 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1746 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1743 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1747 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1744 '(---|\*\*\*)[ \t])')
1748 '(---|\*\*\*)[ \t])', re.MULTILINE)
1745
1749
1746 for patch in patches:
1750 for patch in patches:
1747 pf = os.path.join(d, patch)
1751 pf = os.path.join(d, patch)
1748
1752
1749 message = []
1753 message = None
1750 user = None
1754 user = None
1751 date = None
1755 date = None
1752 hgpatch = False
1756 hgpatch = False
1757
1758 p = email.Parser.Parser()
1753 if pf == '-':
1759 if pf == '-':
1754 f = sys.stdin
1760 msg = p.parse(sys.stdin)
1755 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1756 pf = tmpname
1757 tmpfp = os.fdopen(fd, 'w')
1758 ui.status(_("applying patch from stdin\n"))
1761 ui.status(_("applying patch from stdin\n"))
1759 else:
1762 else:
1760 f = open(pf)
1763 msg = p.parse(file(pf))
1761 tmpfp, tmpname = None, None
1762 ui.status(_("applying %s\n") % patch)
1764 ui.status(_("applying %s\n") % patch)
1765
1766 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1767 tmpfp = os.fdopen(fd, 'w')
1763 try:
1768 try:
1764 while True:
1769 message = msg['Subject']
1765 line = f.readline()
1770 if message:
1766 if not line: break
1771 message = message.replace('\n\t', ' ')
1767 if tmpfp: tmpfp.write(line)
1772 ui.debug('Subject: %s\n' % message)
1768 line = line.rstrip()
1773 user = msg['From']
1769 if (not message and not hgpatch and
1774 if user:
1770 mailre.match(line) and not opts['force']):
1775 ui.debug('From: %s\n' % user)
1771 if len(line) > 35:
1776 diffs_seen = 0
1772 line = line[:32] + '...'
1777 ok_types = ('text/plain', 'text/x-patch')
1773 raise util.Abort(_('first line looks like a '
1778 for part in msg.walk():
1774 'mail header: ') + line)
1779 content_type = part.get_content_type()
1775 if diffre.match(line):
1780 ui.debug('Content-Type: %s\n' % content_type)
1781 if content_type not in ok_types:
1782 continue
1783 payload = part.get_payload(decode=True)
1784 m = diffre.search(payload)
1785 if m:
1786 ui.debug(_('found patch at byte %d\n') % m.start(0))
1787 diffs_seen += 1
1788 hgpatch = False
1789 fp = cStringIO.StringIO()
1790 for line in payload[:m.start(0)].splitlines():
1791 if line.startswith('# HG changeset patch'):
1792 ui.debug(_('patch generated by hg export\n'))
1793 hgpatch = True
1794 elif hgpatch:
1795 if line.startswith('# User '):
1796 user = line[7:]
1797 ui.debug('From: %s\n' % user)
1798 elif line.startswith("# Date "):
1799 date = line[7:]
1800 if not line.startswith('# '):
1801 fp.write(line)
1802 fp.write('\n')
1803 hgpatch = False
1804 message = fp.getvalue() or message
1776 if tmpfp:
1805 if tmpfp:
1777 for chunk in util.filechunkiter(f):
1806 tmpfp.write(payload)
1778 tmpfp.write(chunk)
1807 if not payload.endswith('\n'):
1779 break
1808 tmpfp.write('\n')
1780 elif hgpatch:
1809 elif not diffs_seen and message and content_type == 'text/plain':
1781 # parse values when importing the result of an hg export
1810 message += '\n' + payload
1782 if line.startswith("# User "):
1783 user = line[7:]
1784 ui.debug(_('User: %s\n') % user)
1785 elif line.startswith("# Date "):
1786 date = line[7:]
1787 elif not line.startswith("# ") and line:
1788 message.append(line)
1789 hgpatch = False
1790 elif line == '# HG changeset patch':
1791 hgpatch = True
1792 message = [] # We may have collected garbage
1793 elif message or line:
1794 message.append(line)
1795
1811
1796 if opts['message']:
1812 if opts['message']:
1797 # pickup the cmdline msg
1813 # pickup the cmdline msg
1798 message = opts['message']
1814 message = opts['message']
1799 elif message:
1815 elif message:
1800 # pickup the patch msg
1816 # pickup the patch msg
1801 message = '\n'.join(message).rstrip()
1817 message = message.strip()
1802 else:
1818 else:
1803 # launch the editor
1819 # launch the editor
1804 message = None
1820 message = None
1805 ui.debug(_('message:\n%s\n') % message)
1821 ui.debug(_('message:\n%s\n') % message)
1806
1822
1807 if tmpfp: tmpfp.close()
1823 tmpfp.close()
1808 files = util.patch(strip, pf, ui)
1824 if not diffs_seen:
1809
1825 raise util.Abort(_('no diffs found'))
1826
1827 files = util.patch(strip, tmpname, ui)
1810 if len(files) > 0:
1828 if len(files) > 0:
1811 addremove_lock(ui, repo, files, {})
1829 addremove_lock(ui, repo, files, {})
1812 repo.commit(files, message, user, date)
1830 repo.commit(files, message, user, date)
1813 finally:
1831 finally:
1814 if tmpname: os.unlink(tmpname)
1832 os.unlink(tmpname)
1815
1833
1816 def incoming(ui, repo, source="default", **opts):
1834 def incoming(ui, repo, source="default", **opts):
1817 """show new changesets found in source
1835 """show new changesets found in source
General Comments 0
You need to be logged in to leave comments. Login now