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