--- a/mercurial/commands.py Mon Jun 26 22:44:48 2006 +0200
+++ b/mercurial/commands.py Tue Jun 27 00:09:13 2006 -0700
@@ -12,7 +12,7 @@
demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
demandload(globals(), "fnmatch mdiff random signal tempfile time")
demandload(globals(), "traceback errno socket version struct atexit sets bz2")
-demandload(globals(), "archival changegroup")
+demandload(globals(), "archival cStringIO changegroup email.Parser")
demandload(globals(), "hgweb.server sshserver")
class UnknownCommand(Exception):
@@ -1719,11 +1719,15 @@
If there are outstanding changes in the working directory, import
will abort unless given the -f flag.
- If a patch looks like a mail message (its first line starts with
- "From " or looks like an RFC822 header), it will not be applied
- unless the -f option is used. The importer neither parses nor
- discards mail headers, so use -f only to override the "mailness"
- safety check, not to import a real mail message.
+ You can import a patch straight from a mail message. Even patches
+ as attachments work (body part must be type text/plain or
+ text/x-patch to be used). Sender and subject line of email
+ message are used as default committer and commit message. Any
+ text/plain body part before first diff is added to commit message.
+
+ If imported patch was generated by hg export, user and description
+ from patch override values from message headers and body. Values
+ given on command line with -m and -u override these.
To read a patch from standard input, use patch name "-".
"""
@@ -1739,79 +1743,93 @@
# attempt to detect the start of a patch
# (this heuristic is borrowed from quilt)
- diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
+ diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
'retrieving revision [0-9]+(\.[0-9]+)*$|' +
- '(---|\*\*\*)[ \t])')
+ '(---|\*\*\*)[ \t])', re.MULTILINE)
for patch in patches:
pf = os.path.join(d, patch)
- message = []
+ message = None
user = None
date = None
hgpatch = False
+
+ p = email.Parser.Parser()
if pf == '-':
- f = sys.stdin
- fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
- pf = tmpname
- tmpfp = os.fdopen(fd, 'w')
+ msg = p.parse(sys.stdin)
ui.status(_("applying patch from stdin\n"))
else:
- f = open(pf)
- tmpfp, tmpname = None, None
+ msg = p.parse(file(pf))
ui.status(_("applying %s\n") % patch)
+
+ fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
+ tmpfp = os.fdopen(fd, 'w')
try:
- while True:
- line = f.readline()
- if not line: break
- if tmpfp: tmpfp.write(line)
- line = line.rstrip()
- if (not message and not hgpatch and
- mailre.match(line) and not opts['force']):
- if len(line) > 35:
- line = line[:32] + '...'
- raise util.Abort(_('first line looks like a '
- 'mail header: ') + line)
- if diffre.match(line):
+ 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-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()
+ for line in payload[:m.start(0)].splitlines():
+ if line.startswith('# HG changeset patch'):
+ ui.debug(_('patch generated by hg export\n'))
+ hgpatch = True
+ 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')
+ hgpatch = False
+ message = fp.getvalue() or message
if tmpfp:
- for chunk in util.filechunkiter(f):
- tmpfp.write(chunk)
- break
- elif hgpatch:
- # parse values when importing the result of an hg export
- if line.startswith("# User "):
- user = line[7:]
- ui.debug(_('User: %s\n') % user)
- elif line.startswith("# Date "):
- date = line[7:]
- elif not line.startswith("# ") and line:
- message.append(line)
- hgpatch = False
- elif line == '# HG changeset patch':
- hgpatch = True
- message = [] # We may have collected garbage
- elif message or line:
- message.append(line)
+ 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']
elif message:
# pickup the patch msg
- message = '\n'.join(message).rstrip()
+ message = message.strip()
else:
# launch the editor
message = None
ui.debug(_('message:\n%s\n') % message)
- if tmpfp: tmpfp.close()
- files = util.patch(strip, pf, ui)
-
+ tmpfp.close()
+ if not diffs_seen:
+ raise util.Abort(_('no diffs found'))
+
+ files = util.patch(strip, tmpname, ui)
if len(files) > 0:
addremove_lock(ui, repo, files, {})
repo.commit(files, message, user, date)
finally:
- if tmpname: os.unlink(tmpname)
+ os.unlink(tmpname)
def incoming(ui, repo, source="default", **opts):
"""show new changesets found in source