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