Cut verbosity of new fast-forward merging
[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
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
125def __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 157def 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
238def 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
277def 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)