chiark / gitweb /
14bf0b6702dcb544aaf639e0440fc97ef76bdb09
[stgit] / stgit / commands / imprt.py
1 __copyright__ = """
2 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License version 2 as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 """
17
18 import sys, os
19 from optparse import OptionParser, make_option
20
21 from stgit.commands.common import *
22 from stgit.utils import *
23 from stgit import stack, git
24
25
26 help = 'import a GNU diff file as a new patch'
27 usage = """%prog [options] [<file>]
28
29 Create a new patch and apply the given GNU diff file (or the standard
30 input). By default, the file name is used as the patch name but this
31 can be overriden with the '--name' option. The patch can either be a
32 normal file with the description at the top or it can have standard
33 mail format, the Subject, From and Date headers being used for
34 generating the patch information.
35
36 The patch description has to be separated from the data with a '---'
37 line. For a normal file, if no author information is given, the first
38 'Signed-off-by:' line is used."""
39
40 options = [make_option('-m', '--mail',
41                        help = 'import the patch from a standard e-mail file',
42                        action = 'store_true'),
43            make_option('-n', '--name',
44                        help = 'use NAME as the patch name'),
45            make_option('-b', '--base',
46                        help = 'use BASE instead of HEAD for file importing'),
47            make_option('-e', '--edit',
48                        help = 'invoke an editor for the patch description',
49                        action = 'store_true'),
50            make_option('-s', '--showpatch',
51                        help = 'show the patch content in the editor buffer',
52                        action = 'store_true'),
53            make_option('-a', '--author', metavar = '"NAME <EMAIL>"',
54                        help = 'use "NAME <EMAIL>" as the author details'),
55            make_option('--authname',
56                        help = 'use AUTHNAME as the author name'),
57            make_option('--authemail',
58                        help = 'use AUTHEMAIL as the author e-mail'),
59            make_option('--authdate',
60                        help = 'use AUTHDATE as the author date'),
61            make_option('--commname',
62                        help = 'use COMMNAME as the committer name'),
63            make_option('--commemail',
64                        help = 'use COMMEMAIL as the committer e-mail')]
65
66
67 def __end_descr(line):
68     return re.match('---\s*$', line) or re.match('diff -', line) or \
69             re.match('Index: ', line)
70     
71 def __parse_mail(filename = None):
72     """Parse the input file in a mail format and return (description,
73     authname, authemail, authdate)
74     """
75     if filename:
76         f = file(filename)
77     else:
78         f = sys.stdin
79
80     descr = authname = authemail = authdate = None
81
82     # parse the headers
83     while True:
84         line = f.readline()
85         if not line:
86             break
87         line = line.strip()
88         if re.match('from:\s+', line, re.I):
89             auth = re.findall('^.*?:\s+(.*)$', line)[0]
90             authname, authemail = name_email(auth)
91         elif re.match('date:\s+', line, re.I):
92             authdate = re.findall('^.*?:\s+(.*)$', line)[0]
93         elif re.match('subject:\s+', line, re.I):
94             descr = re.findall('^.*?:\s+(.*)$', line)[0]
95         elif line == '':
96             # end of headers
97             break
98
99     # remove the '[*PATCH*]' expression in the subject
100     if descr:
101         descr = re.findall('^(\[[^\s]*[Pp][Aa][Tt][Cc][Hh].*?\])?\s*(.*)$',
102                            descr)[0][1]
103         descr += '\n\n'
104     else:
105         raise CmdException, 'Subject: line not found'
106
107     # the rest of the patch description
108     while True:
109         line = f.readline()
110         if not line:
111             break
112         if __end_descr(line):
113             break
114         else:
115             descr += line
116     descr.rstrip()
117
118     if filename:
119         f.close()
120
121     return (descr, authname, authemail, authdate)
122
123 def __parse_patch(filename = None):
124     """Parse the input file and return (description, authname,
125     authemail, authdate)
126     """
127     if filename:
128         f = file(filename)
129     else:
130         f = sys.stdin
131
132     authname = authemail = authdate = None
133
134     descr = ''
135     while True:
136         line = f.readline()
137         if not line:
138             break
139
140         # the first 'Signed-of-by:' is the author
141         if not authname and re.match('signed-off-by:\s+', line, re.I):
142             auth = re.findall('^.*?:\s+(.*)$', line)[0]
143             authname, authemail = name_email(auth)
144
145         if __end_descr(line):
146             break
147         else:
148             descr += line
149     descr.rstrip()
150
151     if descr == '':
152         descr = None
153
154     if filename:
155         f.close()
156
157     return (descr, authname, authemail, authdate)
158
159 def func(parser, options, args):
160     """Import a GNU diff file as a new patch
161     """
162     if len(args) > 1:
163         parser.error('incorrect number of arguments')
164
165     check_local_changes()
166     check_conflicts()
167     check_head_top_equal()
168
169     if len(args) == 1:
170         filename = args[0]
171     else:
172         filename = None
173
174     if options.name:
175         patch = options.name
176     elif filename:
177         patch = os.path.basename(filename)
178     else:
179         raise CmdException, 'Unkown patch name'
180
181     # the defaults
182     message = author_name = author_email = author_date = committer_name = \
183               committer_email = None
184
185     if options.author:
186         options.authname, options.authemail = name_email(options.author)
187
188     if options.mail:
189         message, author_name, author_email, author_date = \
190                  __parse_mail(filename)
191     else:
192         message, author_name, author_email, author_date = \
193                  __parse_patch(filename)
194
195     # refresh_patch() will invoke the editor in this case, with correct
196     # patch content
197     if not message:
198         can_edit = False
199
200     # override the automatically parsed settings
201     if options.authname:
202         author_name = options.authname
203     if options.authemail:
204         author_email = options.authemail
205     if options.authdate:
206         author_date = options.authdate
207     if options.commname:
208         committer_name = options.commname
209     if options.commemail:
210         committer_email = options.commemail
211
212     crt_series.new_patch(patch, message = message, can_edit = False,
213                          author_name = author_name,
214                          author_email = author_email,
215                          author_date = author_date,
216                          committer_name = committer_name,
217                          committer_email = committer_email)
218
219     print 'Importing patch %s...' % patch,
220     sys.stdout.flush()
221
222     if options.base:
223         git.apply_patch(filename, git_id(options.base))
224     else:
225         git.apply_patch(filename)
226
227     crt_series.refresh_patch(edit = options.edit,
228                              show_patch = options.showpatch)
229
230     print 'done'
231     print_crt_patch()