remove formatting of git log entry on an imported patch
[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."""
38
39 options = [make_option('-m', '--mail',
40 help = 'import the patch from a standard e-mail file',
41 action = 'store_true'),
42 make_option('-n', '--name',
43 help = 'use NAME as the patch name'),
44 make_option('-b', '--base',
45 help = 'use BASE instead of HEAD for file importing'),
46 make_option('-e', '--edit',
47 help = 'invoke an editor for the patch description',
48 action = 'store_true'),
49 make_option('-s', '--showpatch',
50 help = 'show the patch content in the editor buffer',
51 action = 'store_true'),
52 make_option('-a', '--author', metavar = '"NAME <EMAIL>"',
53 help = 'use "NAME <EMAIL>" as the author details'),
54 make_option('--authname',
55 help = 'use AUTHNAME as the author name'),
56 make_option('--authemail',
57 help = 'use AUTHEMAIL as the author e-mail'),
58 make_option('--authdate',
59 help = 'use AUTHDATE as the author date'),
60 make_option('--commname',
61 help = 'use COMMNAME as the committer name'),
62 make_option('--commemail',
63 help = 'use COMMEMAIL as the committer e-mail')]
64
65
66 def __end_descr(line):
67 return re.match('---\s*$', line) or re.match('diff -', line) or \
68 re.match('Index: ', line)
69
70 def __parse_description(descr):
71 """Parse the patch description and return the new description and
72 author information (if any).
73 """
74 subject = body = ''
75 authname = authemail = authdate = None
76
77 descr_lines = [line.rstrip() for line in descr.split('\n')]
78 if not descr_lines:
79 raise CmdException, "Empty patch description"
80
81 pos = -1
82 lasthdr = 0
83 end = len(descr_lines)
84
85 # Parse the patch header
86 while pos < end:
87 pos += 1
88 if not descr_lines[pos]:
89 continue
90 # check for a "From|Author:" line
91 if re.match('\s*(?:from|author):\s+', descr_lines[pos], re.I):
92 auth = re.findall('^.*?:\s+(.*)$', descr_lines[pos])[0]
93 authname, authemail = name_email(auth)
94 lasthdr = pos + 1
95 continue
96 # check for a "Date:" line
97 if re.match('\s*date:\s+', descr_lines[pos], re.I):
98 authdate = re.findall('^.*?:\s+(.*)$', descr_lines[pos])[0]
99 lasthdr = pos + 1
100 continue
101 if subject:
102 break
103 # get the subject
104 subject = descr_lines[pos]
105 lasthdr = pos + 1
106
107 # get the body
108 if lasthdr < end:
109 body = reduce(lambda x, y: x + '\n' + y, descr_lines[lasthdr:], '')
110
111 return (subject + body, authname, authemail, authdate)
112
113 def __parse_mail(filename = None):
114 """Parse the input file in a mail format and return (description,
115 authname, authemail, authdate)
116 """
117 if filename:
118 f = file(filename)
119 else:
120 f = sys.stdin
121
122 descr = authname = authemail = authdate = None
123
124 # parse the headers
125 while True:
126 line = f.readline()
127 if not line:
128 break
129 line = line.strip()
130 if re.match('from:\s+', line, re.I):
131 auth = re.findall('^.*?:\s+(.*)$', line)[0]
132 authname, authemail = name_email(auth)
133 elif re.match('date:\s+', line, re.I):
134 authdate = re.findall('^.*?:\s+(.*)$', line)[0]
135 elif re.match('subject:\s+', line, re.I):
136 descr = re.findall('^.*?:\s+(.*)$', line)[0]
137 elif line == '':
138 # end of headers
139 break
140
141 # remove the '[*PATCH*]' expression in the subject
142 if descr:
143 descr = re.findall('^(\[[^\s]*[Pp][Aa][Tt][Cc][Hh].*?\])?\s*(.*)$',
144 descr)[0][1]
145 descr += '\n\n'
146 else:
147 raise CmdException, 'Subject: line not found'
148
149 # the rest of the patch description
150 while True:
151 line = f.readline()
152 if not line:
153 break
154 if __end_descr(line):
155 break
156 else:
157 descr += line
158 descr.rstrip()
159
160 if filename:
161 f.close()
162
163 # parse the description for author information
164 descr, descr_authname, descr_authemail, descr_authdate = __parse_description(descr)
165 if descr_authname:
166 authname = descr_authname
167 if descr_authemail:
168 authemail = descr_authemail
169 if descr_authdate:
170 authdate = descr_authdate
171
172 return (descr, authname, authemail, authdate)
173
174 def __parse_patch(filename = None):
175 """Parse the input file and return (description, authname,
176 authemail, authdate)
177 """
178 if filename:
179 f = file(filename)
180 else:
181 f = sys.stdin
182
183 descr = ''
184 while True:
185 line = f.readline()
186 if not line:
187 break
188
189 if __end_descr(line):
190 break
191 else:
192 descr += line
193 descr.rstrip()
194
195 if filename:
196 f.close()
197
198 descr, authname, authemail, authdate = __parse_description(descr)
199
200 # we don't yet have an agreed place for the creation date.
201 # Just return None
202 return (descr, authname, authemail, authdate)
203
204 def func(parser, options, args):
205 """Import a GNU diff file as a new patch
206 """
207 if len(args) > 1:
208 parser.error('incorrect number of arguments')
209
210 check_local_changes()
211 check_conflicts()
212 check_head_top_equal()
213
214 if len(args) == 1:
215 filename = args[0]
216 else:
217 filename = None
218
219 if options.name:
220 patch = options.name
221 elif filename:
222 patch = os.path.basename(filename)
223 else:
224 raise CmdException, 'Unkown patch name'
225
226 # the defaults
227 message = author_name = author_email = author_date = committer_name = \
228 committer_email = None
229
230 if options.author:
231 options.authname, options.authemail = name_email(options.author)
232
233 if options.mail:
234 message, author_name, author_email, author_date = \
235 __parse_mail(filename)
236 else:
237 message, author_name, author_email, author_date = \
238 __parse_patch(filename)
239
240 # refresh_patch() will invoke the editor in this case, with correct
241 # patch content
242 if not message:
243 can_edit = False
244
245 # override the automatically parsed settings
246 if options.authname:
247 author_name = options.authname
248 if options.authemail:
249 author_email = options.authemail
250 if options.authdate:
251 author_date = options.authdate
252 if options.commname:
253 committer_name = options.commname
254 if options.commemail:
255 committer_email = options.commemail
256
257 crt_series.new_patch(patch, message = message, can_edit = False,
258 author_name = author_name,
259 author_email = author_email,
260 author_date = author_date,
261 committer_name = committer_name,
262 committer_email = committer_email)
263
264 print 'Importing patch %s...' % patch,
265 sys.stdout.flush()
266
267 if options.base:
268 git.apply_patch(filename, git_id(options.base))
269 else:
270 git.apply_patch(filename)
271
272 crt_series.refresh_patch(edit = options.edit,
273 show_patch = options.showpatch)
274
275 print 'done'
276 print_crt_patch()