comparison mercurial/commands.py @ 2504:158d3d2ae070

import: parse email messages
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Tue, 27 Jun 2006 00:09:13 -0700
parents 73ac95671788
children cbff06469488
comparison
equal deleted inserted replaced
2502:18cf95ad3666 2504:158d3d2ae070
10 from i18n import gettext as _ 10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb") 11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
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):
19 """Exception raised if command is not in the command table.""" 19 """Exception raised if command is not in the command table."""
20 class AmbiguousCommand(Exception): 20 class AmbiguousCommand(Exception):
1717 Import a list of patches and commit them individually. 1717 Import a list of patches and commit them individually.
1718 1718
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 """
1730 patches = (patch1,) + patches 1734 patches = (patch1,) + patches
1731 1735
1737 1741
1738 mailre = re.compile(r'(?:From |[\w-]+:)') 1742 mailre = re.compile(r'(?:From |[\w-]+:)')
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
1818 1836
1819 Show new changesets found in the specified path/URL or the default 1837 Show new changesets found in the specified path/URL or the default