# HG changeset patch # User Vadim Gelfer # Date 1155413808 25200 # Node ID 2893e51407a48956533117b08b4c92791c288492 # Parent 71e78f2ca5ae4c7fb06551caf5e4fc4e4d977498 commands.import: refactor patch parsing into patch.extract. diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -12,7 +12,7 @@ demandload(globals(), "os re sys signal 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 @@ def import_(ui, repo, patch1, *patches, 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 @@ def import_(ui, repo, patch1, *patches, 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: diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -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 """