Mercurial > public > mercurial-scm > hg
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 |