Merge branch 'master' of http://homepage.ntlworld.com/cmarinas/stgit
[stgit] / stgit / commands / imprt.py
index 9dccaf9..87fd5da 100644 (file)
@@ -28,23 +28,27 @@ usage = """%prog [options] [<file>]
 
 Create a new patch and apply the given GNU diff file (or the standard
 input). By default, the file name is used as the patch name but this
-can be overriden with the '--name' option. The patch can either be a
+can be overridden with the '--name' option. The patch can either be a
 normal file with the description at the top or it can have standard
 mail format, the Subject, From and Date headers being used for
 generating the patch information.
 
 The patch description has to be separated from the data with a '---'
-line. For a normal file, if no author information is given, the first
-'Signed-off-by:' line is used."""
+line."""
 
 options = [make_option('-m', '--mail',
                        help = 'import the patch from a standard e-mail file',
                        action = 'store_true'),
            make_option('-n', '--name',
                        help = 'use NAME as the patch name'),
+           make_option('-b', '--base',
+                       help = 'use BASE instead of HEAD for file importing'),
            make_option('-e', '--edit',
                        help = 'invoke an editor for the patch description',
                        action = 'store_true'),
+           make_option('-s', '--showpatch',
+                       help = 'show the patch content in the editor buffer',
+                       action = 'store_true'),
            make_option('-a', '--author', metavar = '"NAME <EMAIL>"',
                        help = 'use "NAME <EMAIL>" as the author details'),
            make_option('--authname',
@@ -59,6 +63,51 @@ options = [make_option('-m', '--mail',
                        help = 'use COMMEMAIL as the committer e-mail')]
 
 
+def __end_descr(line):
+    return re.match('---\s*$', line) or re.match('diff -', line) or \
+            re.match('Index: ', line)
+
+def __parse_description(descr):
+    """Parse the patch description and return the new description and
+    author information (if any).
+    """
+    subject = body = ''
+    authname = authemail = authdate = None
+
+    descr_lines = [line.rstrip() for line in  descr.split('\n')]
+    if not descr_lines:
+        raise CmdException, "Empty patch description"
+
+    lasthdr = 0
+    end = len(descr_lines)
+
+    # Parse the patch header
+    for pos in range(0, end):
+        if not descr_lines[pos]:
+           continue
+        # check for a "From|Author:" line
+        if re.match('\s*(?:from|author):\s+', descr_lines[pos], re.I):
+            auth = re.findall('^.*?:\s+(.*)$', descr_lines[pos])[0]
+            authname, authemail = name_email(auth)
+            lasthdr = pos + 1
+            continue
+        # check for a "Date:" line
+        if re.match('\s*date:\s+', descr_lines[pos], re.I):
+            authdate = re.findall('^.*?:\s+(.*)$', descr_lines[pos])[0]
+            lasthdr = pos + 1
+            continue
+        if subject:
+            break
+        # get the subject
+        subject = descr_lines[pos]
+        lasthdr = pos + 1
+
+    # get the body
+    if lasthdr < end:
+        body = reduce(lambda x, y: x + '\n' + y, descr_lines[lasthdr:], '')
+
+    return (subject + body, authname, authemail, authdate)
+
 def __parse_mail(filename = None):
     """Parse the input file in a mail format and return (description,
     authname, authemail, authdate)
@@ -71,7 +120,10 @@ def __parse_mail(filename = None):
     descr = authname = authemail = authdate = None
 
     # parse the headers
-    for line in f:
+    while True:
+        line = f.readline()
+        if not line:
+            break
         line = line.strip()
         if re.match('from:\s+', line, re.I):
             auth = re.findall('^.*?:\s+(.*)$', line)[0]
@@ -86,14 +138,18 @@ def __parse_mail(filename = None):
 
     # remove the '[*PATCH*]' expression in the subject
     if descr:
-        descr = re.findall('^(\[[^\s]*PATCH.*?\])?\s*(.*)$', descr)[0][1]
+        descr = re.findall('^(\[[^\s]*[Pp][Aa][Tt][Cc][Hh].*?\])?\s*(.*)$',
+                           descr)[0][1]
         descr += '\n\n'
     else:
         raise CmdException, 'Subject: line not found'
 
     # the rest of the patch description
-    for line in f:
-        if re.match('---\s*$', line) or re.match('diff -', line):
+    while True:
+        line = f.readline()
+        if not line:
+            break
+        if __end_descr(line):
             break
         else:
             descr += line
@@ -102,6 +158,15 @@ def __parse_mail(filename = None):
     if filename:
         f.close()
 
+    # parse the description for author information
+    descr, descr_authname, descr_authemail, descr_authdate = __parse_description(descr)
+    if descr_authname:
+        authname = descr_authname
+    if descr_authemail:
+        authemail = descr_authemail
+    if descr_authdate:
+       authdate = descr_authdate
+
     return (descr, authname, authemail, authdate)
 
 def __parse_patch(filename = None):
@@ -113,27 +178,25 @@ def __parse_patch(filename = None):
     else:
         f = sys.stdin
 
-    authname = authemail = authdate = None
-
     descr = ''
-    for line in f:
-        # the first 'Signed-of-by:' is the author
-        if not authname and re.match('signed-off-by:\s+', line, re.I):
-            auth = re.findall('^.*?:\s+(.*)$', line)[0]
-            authname, authemail = name_email(auth)
+    while True:
+        line = f.readline()
+        if not line:
+            break
 
-        if re.match('---\s*$', line) or re.match('diff -', line):
+        if __end_descr(line):
             break
         else:
             descr += line
     descr.rstrip()
 
-    if descr == '':
-        descr = None
-
     if filename:
         f.close()
 
+    descr, authname, authemail, authdate = __parse_description(descr)
+
+    # we don't yet have an agreed place for the creation date.
+    # Just return None
     return (descr, authname, authemail, authdate)
 
 def func(parser, options, args):
@@ -156,7 +219,7 @@ def func(parser, options, args):
     elif filename:
         patch = os.path.basename(filename)
     else:
-        raise CmdException, 'Unkown patch name'
+        raise CmdException, 'Unknown patch name'
 
     # the defaults
     message = author_name = author_email = author_date = committer_name = \
@@ -172,9 +235,10 @@ def func(parser, options, args):
         message, author_name, author_email, author_date = \
                  __parse_patch(filename)
 
-    # new_patch() will invoke the editor in this case
+    # refresh_patch() will invoke the editor in this case, with correct
+    # patch content
     if not message:
-        edit = False
+        can_edit = False
 
     # override the automatically parsed settings
     if options.authname:
@@ -188,7 +252,7 @@ def func(parser, options, args):
     if options.commemail:
         committer_email = options.commemail
 
-    crt_series.new_patch(patch, message = message,
+    crt_series.new_patch(patch, message = message, can_edit = False,
                          author_name = author_name,
                          author_email = author_email,
                          author_date = author_date,
@@ -198,8 +262,13 @@ def func(parser, options, args):
     print 'Importing patch %s...' % patch,
     sys.stdout.flush()
 
-    git.apply_patch(filename)
-    crt_series.refresh_patch(edit = options.edit)
+    if options.base:
+        git.apply_patch(filename, git_id(options.base))
+    else:
+        git.apply_patch(filename)
+
+    crt_series.refresh_patch(edit = options.edit,
+                             show_patch = options.showpatch)
 
     print 'done'
     print_crt_patch()