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' | |
37a4d1bf | 27 | usage = """%prog [options] [<file>|<commit>] |
0d2cd1e4 | 28 | |
37a4d1bf CM |
29 | Create a new patch and import the given GNU diff file (defaulting to |
30 | the standard input) or a given commit object into it. By default, the | |
31 | file name is used as the patch name but this can be overriden with the | |
32 | '--name' option. | |
0d2cd1e4 | 33 | |
37a4d1bf CM |
34 | The patch file can either be a normal file with the description at the |
35 | top or it can have standard mail format, the Subject, From and Date | |
36 | headers being used for generating the patch information. The patch | |
37 | description has to be separated from the data with a '---' line. For a | |
38 | normal file, if no author information is given, the first | |
39 | 'Signed-off-by:' line is used. | |
40 | ||
41 | When a commit object is imported, the log and author information are | |
42 | those of the commit object. Passing the '--reverse' option will cancel | |
43 | an existing commit object.""" | |
0d2cd1e4 CM |
44 | |
45 | options = [make_option('-m', '--mail', | |
46 | help = 'import the patch from a standard e-mail file', | |
47 | action = 'store_true'), | |
37a4d1bf CM |
48 | make_option('-c', '--commit', |
49 | help = 'import a commit object as a patch', | |
50 | action = 'store_true'), | |
51 | make_option('--reverse', | |
52 | help = 'reverse the commit object before importing', | |
53 | action = 'store_true'), | |
0d2cd1e4 CM |
54 | make_option('-n', '--name', |
55 | help = 'use NAME as the patch name'), | |
35344f86 CM |
56 | make_option('--base', |
57 | help = 'use BASE instead of HEAD for file importing'), | |
33e580e0 CM |
58 | make_option('-e', '--edit', |
59 | help = 'invoke an editor for the patch description', | |
60 | action = 'store_true'), | |
6ad48e48 PBG |
61 | make_option('-s', '--showpatch', |
62 | help = 'show the patch content in the editor buffer', | |
63 | action = 'store_true'), | |
0d2cd1e4 CM |
64 | make_option('-a', '--author', metavar = '"NAME <EMAIL>"', |
65 | help = 'use "NAME <EMAIL>" as the author details'), | |
66 | make_option('--authname', | |
67 | help = 'use AUTHNAME as the author name'), | |
68 | make_option('--authemail', | |
69 | help = 'use AUTHEMAIL as the author e-mail'), | |
70 | make_option('--authdate', | |
71 | help = 'use AUTHDATE as the author date'), | |
72 | make_option('--commname', | |
73 | help = 'use COMMNAME as the committer name'), | |
74 | make_option('--commemail', | |
75 | help = 'use COMMEMAIL as the committer e-mail')] | |
76 | ||
77 | ||
78 | def __parse_mail(filename = None): | |
79 | """Parse the input file in a mail format and return (description, | |
80 | authname, authemail, authdate) | |
81 | """ | |
82 | if filename: | |
83 | f = file(filename) | |
84 | else: | |
85 | f = sys.stdin | |
86 | ||
87 | descr = authname = authemail = authdate = None | |
88 | ||
89 | # parse the headers | |
90 | for line in f: | |
91 | line = line.strip() | |
92 | if re.match('from:\s+', line, re.I): | |
93 | auth = re.findall('^.*?:\s+(.*)$', line)[0] | |
94 | authname, authemail = name_email(auth) | |
95 | elif re.match('date:\s+', line, re.I): | |
96 | authdate = re.findall('^.*?:\s+(.*)$', line)[0] | |
97 | elif re.match('subject:\s+', line, re.I): | |
98 | descr = re.findall('^.*?:\s+(.*)$', line)[0] | |
99 | elif line == '': | |
100 | # end of headers | |
101 | break | |
102 | ||
186e6b6b | 103 | # remove the '[*PATCH*]' expression in the subject |
0d2cd1e4 | 104 | if descr: |
7c02f338 CM |
105 | descr = re.findall('^(\[[^\s]*[Pp][Aa][Tt][Cc][Hh].*?\])?\s*(.*)$', |
106 | descr)[0][1] | |
0d2cd1e4 CM |
107 | descr += '\n\n' |
108 | else: | |
109 | raise CmdException, 'Subject: line not found' | |
110 | ||
111 | # the rest of the patch description | |
112 | for line in f: | |
b2a0aaa8 PBG |
113 | if re.match('---\s*$', line) or re.match('diff -', line) or \ |
114 | re.match('^Index: ', line): | |
0d2cd1e4 CM |
115 | break |
116 | else: | |
117 | descr += line | |
118 | descr.rstrip() | |
119 | ||
120 | if filename: | |
121 | f.close() | |
122 | ||
123 | return (descr, authname, authemail, authdate) | |
124 | ||
125 | def __parse_patch(filename = None): | |
126 | """Parse the input file and return (description, authname, | |
127 | authemail, authdate) | |
128 | """ | |
129 | if filename: | |
130 | f = file(filename) | |
131 | else: | |
132 | f = sys.stdin | |
133 | ||
134 | authname = authemail = authdate = None | |
135 | ||
136 | descr = '' | |
137 | for line in f: | |
138 | # the first 'Signed-of-by:' is the author | |
139 | if not authname and re.match('signed-off-by:\s+', line, re.I): | |
140 | auth = re.findall('^.*?:\s+(.*)$', line)[0] | |
141 | authname, authemail = name_email(auth) | |
142 | ||
70893e13 | 143 | if re.match('---\s*$', line) or re.match('diff -', line): |
0d2cd1e4 CM |
144 | break |
145 | else: | |
146 | descr += line | |
147 | descr.rstrip() | |
148 | ||
149 | if descr == '': | |
150 | descr = None | |
151 | ||
152 | if filename: | |
153 | f.close() | |
154 | ||
155 | return (descr, authname, authemail, authdate) | |
156 | ||
37a4d1bf | 157 | def import_file(parser, options, args): |
0d2cd1e4 CM |
158 | """Import a GNU diff file as a new patch |
159 | """ | |
160 | if len(args) > 1: | |
161 | parser.error('incorrect number of arguments') | |
37a4d1bf | 162 | elif len(args) == 1: |
0d2cd1e4 | 163 | filename = args[0] |
5185abc1 | 164 | else: |
0d2cd1e4 | 165 | filename = None |
5185abc1 CM |
166 | |
167 | if options.name: | |
0d2cd1e4 | 168 | patch = options.name |
5185abc1 CM |
169 | elif filename: |
170 | patch = os.path.basename(filename) | |
0d2cd1e4 CM |
171 | else: |
172 | raise CmdException, 'Unkown patch name' | |
173 | ||
174 | # the defaults | |
175 | message = author_name = author_email = author_date = committer_name = \ | |
176 | committer_email = None | |
177 | ||
178 | if options.author: | |
179 | options.authname, options.authemail = name_email(options.author) | |
180 | ||
181 | if options.mail: | |
182 | message, author_name, author_email, author_date = \ | |
183 | __parse_mail(filename) | |
184 | else: | |
185 | message, author_name, author_email, author_date = \ | |
186 | __parse_patch(filename) | |
187 | ||
95742cfc PBG |
188 | # refresh_patch() will invoke the editor in this case, with correct |
189 | # patch content | |
9d15ccd8 | 190 | if not message: |
95742cfc | 191 | can_edit = False |
9d15ccd8 | 192 | |
0d2cd1e4 CM |
193 | # override the automatically parsed settings |
194 | if options.authname: | |
195 | author_name = options.authname | |
196 | if options.authemail: | |
197 | author_email = options.authemail | |
198 | if options.authdate: | |
199 | author_date = options.authdate | |
200 | if options.commname: | |
201 | committer_name = options.commname | |
202 | if options.commemail: | |
203 | committer_email = options.commemail | |
204 | ||
95742cfc | 205 | crt_series.new_patch(patch, message = message, can_edit = False, |
0d2cd1e4 CM |
206 | author_name = author_name, |
207 | author_email = author_email, | |
208 | author_date = author_date, | |
209 | committer_name = committer_name, | |
210 | committer_email = committer_email) | |
211 | ||
212 | print 'Importing patch %s...' % patch, | |
213 | sys.stdout.flush() | |
214 | ||
35344f86 CM |
215 | if options.base: |
216 | orig_head = git.get_head() | |
217 | git.switch(options.base) | |
218 | ||
219 | try: | |
220 | git.apply_patch(filename) | |
221 | except git.GitException, ex: | |
222 | print >> sys.stderr, '"git apply" failed' | |
223 | git.switch(orig_head) | |
224 | raise | |
225 | ||
226 | top = crt_series.refresh_patch(commit_only = True) | |
227 | git.switch(orig_head) | |
228 | git.merge(options.base, orig_head, top) | |
229 | else: | |
230 | git.apply_patch(filename) | |
231 | ||
6ad48e48 PBG |
232 | crt_series.refresh_patch(edit = options.edit, |
233 | show_patch = options.showpatch) | |
0d2cd1e4 CM |
234 | |
235 | print 'done' | |
236 | print_crt_patch() | |
37a4d1bf CM |
237 | |
238 | def import_commit(parser, options, args): | |
239 | """Import a commit object as a new patch | |
240 | """ | |
241 | if len(args) != 1: | |
242 | parser.error('incorrect number of arguments') | |
243 | ||
244 | commit_id = args[0] | |
245 | ||
246 | if options.name: | |
247 | patch = options.name | |
248 | else: | |
249 | raise CmdException, 'Unkown patch name' | |
250 | ||
251 | commit = git.Commit(commit_id) | |
252 | ||
253 | if not options.reverse: | |
254 | bottom = commit.get_parent() | |
255 | top = commit_id | |
256 | else: | |
257 | bottom = commit_id | |
258 | top = commit.get_parent() | |
259 | ||
260 | message = commit.get_log() | |
261 | author_name, author_email, author_date = \ | |
262 | name_email_date(commit.get_author()) | |
263 | ||
264 | print 'Importing commit %s...' % commit_id, | |
265 | sys.stdout.flush() | |
266 | ||
267 | crt_series.new_patch(patch, message = message, can_edit = False, | |
268 | unapplied = True, bottom = bottom, top = top, | |
269 | author_name = author_name, | |
270 | author_email = author_email, | |
271 | author_date = author_date) | |
272 | crt_series.push_patch(patch) | |
273 | ||
274 | print 'done' | |
275 | print_crt_patch() | |
276 | ||
277 | def func(parser, options, args): | |
278 | """Import a GNU diff file or a commit object as a new patch | |
279 | """ | |
280 | check_local_changes() | |
281 | check_conflicts() | |
282 | check_head_top_equal() | |
283 | ||
284 | if options.commit: | |
285 | import_commit(parser, options, args) | |
286 | else: | |
287 | import_file(parser, options, args) |