Add the --strip option to import
[stgit] / stgit / commands / imprt.py
CommitLineData
0d2cd1e4
CM
1__copyright__ = """
2Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
3
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License version 2 as
6published by the Free Software Foundation.
7
8This program is distributed in the hope that it will be useful,
9but WITHOUT ANY WARRANTY; without even the implied warranty of
10MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11GNU General Public License for more details.
12
13You should have received a copy of the GNU General Public License
14along with this program; if not, write to the Free Software
15Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16"""
17
9417ece4 18import sys, os, re
0d2cd1e4
CM
19from optparse import OptionParser, make_option
20
21from stgit.commands.common import *
22from stgit.utils import *
23from stgit import stack, git
24
25
26help = 'import a GNU diff file as a new patch'
b8a0986f 27usage = """%prog [options] [<file>]
0d2cd1e4 28
b8a0986f
CM
29Create a new patch and apply the given GNU diff file (or the standard
30input). By default, the file name is used as the patch name but this
388f63b6 31can be overridden with the '--name' option. The patch can either be a
b8a0986f
CM
32normal file with the description at the top or it can have standard
33mail format, the Subject, From and Date headers being used for
34generating the patch information.
0d2cd1e4 35
b8a0986f 36The patch description has to be separated from the data with a '---'
99e73103 37line."""
0d2cd1e4
CM
38
39options = [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'),
b0cdad5e
CM
44 make_option('-t', '--strip',
45 help = 'strip numbering and extension from patch name',
46 action = 'store_true'),
9417ece4
CM
47 make_option('-s', '--series',
48 help = 'import a series of patches',
49 action = 'store_true'),
50 make_option('-i', '--ignore',
51 help = 'ignore the applied patches in the series',
52 action = 'store_true'),
b21bc8d1 53 make_option('-b', '--base',
35344f86 54 help = 'use BASE instead of HEAD for file importing'),
33e580e0
CM
55 make_option('-e', '--edit',
56 help = 'invoke an editor for the patch description',
57 action = 'store_true'),
9417ece4 58 make_option('-p', '--showpatch',
6ad48e48
PBG
59 help = 'show the patch content in the editor buffer',
60 action = 'store_true'),
0d2cd1e4
CM
61 make_option('-a', '--author', metavar = '"NAME <EMAIL>"',
62 help = 'use "NAME <EMAIL>" as the author details'),
63 make_option('--authname',
64 help = 'use AUTHNAME as the author name'),
65 make_option('--authemail',
66 help = 'use AUTHEMAIL as the author e-mail'),
67 make_option('--authdate',
68 help = 'use AUTHDATE as the author date'),
69 make_option('--commname',
70 help = 'use COMMNAME as the committer name'),
71 make_option('--commemail',
72 help = 'use COMMEMAIL as the committer e-mail')]
73
74
d4c43e19
PBG
75def __end_descr(line):
76 return re.match('---\s*$', line) or re.match('diff -', line) or \
77 re.match('Index: ', line)
99e73103 78
b0cdad5e
CM
79def __strip_patch_name(name):
80 return re.sub('^[0-9]+-(.*)\.(diff|patch)$', '\g<1>', name)
81
99e73103
CM
82def __parse_description(descr):
83 """Parse the patch description and return the new description and
84 author information (if any).
85 """
86 subject = body = ''
0543bc5f 87 authname = authemail = authdate = None
99e73103 88
0543bc5f 89 descr_lines = [line.rstrip() for line in descr.split('\n')]
99e73103
CM
90 if not descr_lines:
91 raise CmdException, "Empty patch description"
92
0543bc5f 93 lasthdr = 0
99e73103
CM
94 end = len(descr_lines)
95
0543bc5f 96 # Parse the patch header
61dabd0e 97 for pos in range(0, end):
0543bc5f
TM
98 if not descr_lines[pos]:
99 continue
100 # check for a "From|Author:" line
101 if re.match('\s*(?:from|author):\s+', descr_lines[pos], re.I):
102 auth = re.findall('^.*?:\s+(.*)$', descr_lines[pos])[0]
103 authname, authemail = name_email(auth)
104 lasthdr = pos + 1
105 continue
106 # check for a "Date:" line
107 if re.match('\s*date:\s+', descr_lines[pos], re.I):
108 authdate = re.findall('^.*?:\s+(.*)$', descr_lines[pos])[0]
109 lasthdr = pos + 1
110 continue
111 if subject:
112 break
113 # get the subject
114 subject = descr_lines[pos]
115 lasthdr = pos + 1
99e73103
CM
116
117 # get the body
0543bc5f
TM
118 if lasthdr < end:
119 body = reduce(lambda x, y: x + '\n' + y, descr_lines[lasthdr:], '')
99e73103 120
0543bc5f 121 return (subject + body, authname, authemail, authdate)
99e73103 122
0d2cd1e4
CM
123def __parse_mail(filename = None):
124 """Parse the input file in a mail format and return (description,
125 authname, authemail, authdate)
126 """
127 if filename:
128 f = file(filename)
129 else:
130 f = sys.stdin
131
132 descr = authname = authemail = authdate = None
133
134 # parse the headers
6fe6b1bd
CM
135 while True:
136 line = f.readline()
137 if not line:
138 break
0d2cd1e4
CM
139 line = line.strip()
140 if re.match('from:\s+', line, re.I):
141 auth = re.findall('^.*?:\s+(.*)$', line)[0]
142 authname, authemail = name_email(auth)
143 elif re.match('date:\s+', line, re.I):
144 authdate = re.findall('^.*?:\s+(.*)$', line)[0]
145 elif re.match('subject:\s+', line, re.I):
146 descr = re.findall('^.*?:\s+(.*)$', line)[0]
147 elif line == '':
148 # end of headers
149 break
150
186e6b6b 151 # remove the '[*PATCH*]' expression in the subject
0d2cd1e4 152 if descr:
7c02f338
CM
153 descr = re.findall('^(\[[^\s]*[Pp][Aa][Tt][Cc][Hh].*?\])?\s*(.*)$',
154 descr)[0][1]
0d2cd1e4
CM
155 descr += '\n\n'
156 else:
157 raise CmdException, 'Subject: line not found'
158
159 # the rest of the patch description
6fe6b1bd
CM
160 while True:
161 line = f.readline()
162 if not line:
163 break
d4c43e19 164 if __end_descr(line):
0d2cd1e4
CM
165 break
166 else:
167 descr += line
168 descr.rstrip()
169
170 if filename:
171 f.close()
172
99e73103 173 # parse the description for author information
0543bc5f 174 descr, descr_authname, descr_authemail, descr_authdate = __parse_description(descr)
99e73103
CM
175 if descr_authname:
176 authname = descr_authname
177 if descr_authemail:
178 authemail = descr_authemail
0543bc5f
TM
179 if descr_authdate:
180 authdate = descr_authdate
99e73103 181
0d2cd1e4
CM
182 return (descr, authname, authemail, authdate)
183
184def __parse_patch(filename = None):
185 """Parse the input file and return (description, authname,
186 authemail, authdate)
187 """
188 if filename:
189 f = file(filename)
190 else:
191 f = sys.stdin
192
0d2cd1e4 193 descr = ''
6fe6b1bd
CM
194 while True:
195 line = f.readline()
196 if not line:
197 break
198
d4c43e19 199 if __end_descr(line):
0d2cd1e4
CM
200 break
201 else:
202 descr += line
203 descr.rstrip()
204
0d2cd1e4
CM
205 if filename:
206 f.close()
207
0543bc5f 208 descr, authname, authemail, authdate = __parse_description(descr)
99e73103
CM
209
210 # we don't yet have an agreed place for the creation date.
211 # Just return None
0543bc5f 212 return (descr, authname, authemail, authdate)
0d2cd1e4 213
9417ece4
CM
214def __import_patch(patch, filename, options):
215 """Import a patch from a file or standard input
0d2cd1e4 216 """
0d2cd1e4
CM
217 # the defaults
218 message = author_name = author_email = author_date = committer_name = \
219 committer_email = None
220
221 if options.author:
222 options.authname, options.authemail = name_email(options.author)
223
224 if options.mail:
225 message, author_name, author_email, author_date = \
226 __parse_mail(filename)
227 else:
228 message, author_name, author_email, author_date = \
229 __parse_patch(filename)
230
95742cfc
PBG
231 # refresh_patch() will invoke the editor in this case, with correct
232 # patch content
9d15ccd8 233 if not message:
95742cfc 234 can_edit = False
9d15ccd8 235
0d2cd1e4
CM
236 # override the automatically parsed settings
237 if options.authname:
238 author_name = options.authname
239 if options.authemail:
240 author_email = options.authemail
241 if options.authdate:
242 author_date = options.authdate
243 if options.commname:
244 committer_name = options.commname
245 if options.commemail:
246 committer_email = options.commemail
247
95742cfc 248 crt_series.new_patch(patch, message = message, can_edit = False,
0d2cd1e4
CM
249 author_name = author_name,
250 author_email = author_email,
251 author_date = author_date,
252 committer_name = committer_name,
253 committer_email = committer_email)
254
9417ece4 255 print 'Importing patch "%s"...' % patch,
0d2cd1e4
CM
256 sys.stdout.flush()
257
35344f86 258 if options.base:
be3e6bd9 259 git.apply_patch(filename, git_id(options.base))
35344f86
CM
260 else:
261 git.apply_patch(filename)
262
6ad48e48
PBG
263 crt_series.refresh_patch(edit = options.edit,
264 show_patch = options.showpatch)
0d2cd1e4
CM
265
266 print 'done'
9417ece4
CM
267
268def __import_series(filename, options):
269 """Import a series of patches
270 """
271 applied = crt_series.get_applied()
272
273 if filename:
274 f = file(filename)
275 patchdir = os.path.dirname(filename)
276 else:
277 f = sys.stdin
278 patchdir = ''
279
280 for line in f:
281 patch = re.sub('#.*$', '', line).strip()
282 if not patch:
283 continue
b0cdad5e
CM
284 if options.strip:
285 patch = __strip_patch_name(patch)
9417ece4
CM
286 if options.ignore and patch in applied:
287 print 'Ignoring already applied patch "%s"' % patch
288 continue
289
290 patchfile = os.path.join(patchdir, patch)
291 __import_patch(patch, patchfile, options)
292
293def func(parser, options, args):
294 """Import a GNU diff file as a new patch
295 """
296 if len(args) > 1:
297 parser.error('incorrect number of arguments')
298
299 check_local_changes()
300 check_conflicts()
301 check_head_top_equal()
302
303 if len(args) == 1:
304 filename = args[0]
305 else:
306 filename = None
307
308 if options.series:
309 __import_series(filename, options)
310 else:
311 if options.name:
312 patch = options.name
313 elif filename:
314 patch = os.path.basename(filename)
315 else:
316 raise CmdException, 'Unknown patch name'
b0cdad5e
CM
317 if options.strip:
318 patch = __strip_patch_name(patch)
9417ece4
CM
319
320 __import_patch(patch, filename, options)
321
0d2cd1e4 322 print_crt_patch()