2 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
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.
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.
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
19 from optparse import OptionParser, make_option
21 from stgit.commands.common import *
22 from stgit.utils import *
23 from stgit import stack, git
26 help = 'import a GNU diff file as a new patch'
27 usage = """%prog [options] [<file>|<commit>]
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
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.
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."""
45 options = [make_option('-m', '--mail',
46 help = 'import the patch from a standard e-mail file',
47 action = 'store_true'),
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'),
54 make_option('-n', '--name',
55 help = 'use NAME as the patch name'),
57 help = 'use BASE instead of HEAD for file importing'),
58 make_option('-e', '--edit',
59 help = 'invoke an editor for the patch description',
60 action = 'store_true'),
61 make_option('-s', '--showpatch',
62 help = 'show the patch content in the editor buffer',
63 action = 'store_true'),
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')]
78 def __parse_mail(filename = None):
79 """Parse the input file in a mail format and return (description,
80 authname, authemail, authdate)
87 descr = authname = authemail = authdate = None
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]
106 # remove the '[*PATCH*]' expression in the subject
108 descr = re.findall('^(\[[^\s]*[Pp][Aa][Tt][Cc][Hh].*?\])?\s*(.*)$',
112 raise CmdException, 'Subject: line not found'
114 # the rest of the patch description
119 if re.match('---\s*$', line) or re.match('diff -', line) or \
120 re.match('^Index: ', line):
129 return (descr, authname, authemail, authdate)
131 def __parse_patch(filename = None):
132 """Parse the input file and return (description, authname,
140 authname = authemail = authdate = None
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)
153 if re.match('---\s*$', line) or re.match('diff -', line):
165 return (descr, authname, authemail, authdate)
167 def import_file(parser, options, args):
168 """Import a GNU diff file as a new patch
171 parser.error('incorrect number of arguments')
180 patch = os.path.basename(filename)
182 raise CmdException, 'Unkown patch name'
185 message = author_name = author_email = author_date = committer_name = \
186 committer_email = None
189 options.authname, options.authemail = name_email(options.author)
192 message, author_name, author_email, author_date = \
193 __parse_mail(filename)
195 message, author_name, author_email, author_date = \
196 __parse_patch(filename)
198 # refresh_patch() will invoke the editor in this case, with correct
203 # override the automatically parsed settings
205 author_name = options.authname
206 if options.authemail:
207 author_email = options.authemail
209 author_date = options.authdate
211 committer_name = options.commname
212 if options.commemail:
213 committer_email = options.commemail
215 crt_series.new_patch(patch, message = message, can_edit = False,
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)
222 print 'Importing patch %s...' % patch,
226 orig_head = git.get_head()
227 git.switch(options.base)
230 git.apply_patch(filename)
231 except git.GitException, ex:
232 print >> sys.stderr, '"git apply" failed'
233 git.switch(orig_head)
236 top = crt_series.refresh_patch(commit_only = True)
237 git.switch(orig_head)
238 git.merge(options.base, orig_head, top)
240 git.apply_patch(filename)
242 crt_series.refresh_patch(edit = options.edit,
243 show_patch = options.showpatch)
248 def import_commit(parser, options, args):
249 """Import a commit object as a new patch
252 parser.error('incorrect number of arguments')
259 raise CmdException, 'Unkown patch name'
261 commit = git.Commit(commit_id)
263 if not options.reverse:
264 bottom = commit.get_parent()
268 top = commit.get_parent()
270 message = commit.get_log()
271 author_name, author_email, author_date = \
272 name_email_date(commit.get_author())
274 print 'Importing commit %s...' % commit_id,
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)
287 def func(parser, options, args):
288 """Import a GNU diff file or a commit object as a new patch
290 check_local_changes()
292 check_head_top_equal()
295 import_commit(parser, options, args)
297 import_file(parser, options, args)