Check whether the patch name is applied or unapplied
[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'),
33e580e0
CM
56 make_option('-e', '--edit',
57 help = 'invoke an editor for the patch description',
58 action = 'store_true'),
6ad48e48
PBG
59 make_option('-s', '--showpatch',
60 help = 'show the patch content in the editor buffer',
61 action = 'store_true'),
0d2cd1e4
CM
62 make_option('-a', '--author', metavar = '"NAME <EMAIL>"',
63 help = 'use "NAME <EMAIL>" as the author details'),
64 make_option('--authname',
65 help = 'use AUTHNAME as the author name'),
66 make_option('--authemail',
67 help = 'use AUTHEMAIL as the author e-mail'),
68 make_option('--authdate',
69 help = 'use AUTHDATE as the author date'),
70 make_option('--commname',
71 help = 'use COMMNAME as the committer name'),
72 make_option('--commemail',
73 help = 'use COMMEMAIL as the committer e-mail')]
74
75
76def __parse_mail(filename = None):
77 """Parse the input file in a mail format and return (description,
78 authname, authemail, authdate)
79 """
80 if filename:
81 f = file(filename)
82 else:
83 f = sys.stdin
84
85 descr = authname = authemail = authdate = None
86
87 # parse the headers
88 for line in f:
89 line = line.strip()
90 if re.match('from:\s+', line, re.I):
91 auth = re.findall('^.*?:\s+(.*)$', line)[0]
92 authname, authemail = name_email(auth)
93 elif re.match('date:\s+', line, re.I):
94 authdate = re.findall('^.*?:\s+(.*)$', line)[0]
95 elif re.match('subject:\s+', line, re.I):
96 descr = re.findall('^.*?:\s+(.*)$', line)[0]
97 elif line == '':
98 # end of headers
99 break
100
186e6b6b 101 # remove the '[*PATCH*]' expression in the subject
0d2cd1e4 102 if descr:
7c02f338
CM
103 descr = re.findall('^(\[[^\s]*[Pp][Aa][Tt][Cc][Hh].*?\])?\s*(.*)$',
104 descr)[0][1]
0d2cd1e4
CM
105 descr += '\n\n'
106 else:
107 raise CmdException, 'Subject: line not found'
108
109 # the rest of the patch description
110 for line in f:
b2a0aaa8
PBG
111 if re.match('---\s*$', line) or re.match('diff -', line) or \
112 re.match('^Index: ', line):
0d2cd1e4
CM
113 break
114 else:
115 descr += line
116 descr.rstrip()
117
118 if filename:
119 f.close()
120
121 return (descr, authname, authemail, authdate)
122
123def __parse_patch(filename = None):
124 """Parse the input file and return (description, authname,
125 authemail, authdate)
126 """
127 if filename:
128 f = file(filename)
129 else:
130 f = sys.stdin
131
132 authname = authemail = authdate = None
133
134 descr = ''
135 for line in f:
136 # the first 'Signed-of-by:' is the author
137 if not authname and re.match('signed-off-by:\s+', line, re.I):
138 auth = re.findall('^.*?:\s+(.*)$', line)[0]
139 authname, authemail = name_email(auth)
140
70893e13 141 if re.match('---\s*$', line) or re.match('diff -', line):
0d2cd1e4
CM
142 break
143 else:
144 descr += line
145 descr.rstrip()
146
147 if descr == '':
148 descr = None
149
150 if filename:
151 f.close()
152
153 return (descr, authname, authemail, authdate)
154
37a4d1bf 155def import_file(parser, options, args):
0d2cd1e4
CM
156 """Import a GNU diff file as a new patch
157 """
158 if len(args) > 1:
159 parser.error('incorrect number of arguments')
37a4d1bf 160 elif len(args) == 1:
0d2cd1e4 161 filename = args[0]
5185abc1 162 else:
0d2cd1e4 163 filename = None
5185abc1
CM
164
165 if options.name:
0d2cd1e4 166 patch = options.name
5185abc1
CM
167 elif filename:
168 patch = os.path.basename(filename)
0d2cd1e4
CM
169 else:
170 raise CmdException, 'Unkown patch name'
171
172 # the defaults
173 message = author_name = author_email = author_date = committer_name = \
174 committer_email = None
175
176 if options.author:
177 options.authname, options.authemail = name_email(options.author)
178
179 if options.mail:
180 message, author_name, author_email, author_date = \
181 __parse_mail(filename)
182 else:
183 message, author_name, author_email, author_date = \
184 __parse_patch(filename)
185
95742cfc
PBG
186 # refresh_patch() will invoke the editor in this case, with correct
187 # patch content
9d15ccd8 188 if not message:
95742cfc 189 can_edit = False
9d15ccd8 190
0d2cd1e4
CM
191 # override the automatically parsed settings
192 if options.authname:
193 author_name = options.authname
194 if options.authemail:
195 author_email = options.authemail
196 if options.authdate:
197 author_date = options.authdate
198 if options.commname:
199 committer_name = options.commname
200 if options.commemail:
201 committer_email = options.commemail
202
95742cfc 203 crt_series.new_patch(patch, message = message, can_edit = False,
0d2cd1e4
CM
204 author_name = author_name,
205 author_email = author_email,
206 author_date = author_date,
207 committer_name = committer_name,
208 committer_email = committer_email)
209
210 print 'Importing patch %s...' % patch,
211 sys.stdout.flush()
212
213 git.apply_patch(filename)
6ad48e48
PBG
214 crt_series.refresh_patch(edit = options.edit,
215 show_patch = options.showpatch)
0d2cd1e4
CM
216
217 print 'done'
218 print_crt_patch()
37a4d1bf
CM
219
220def import_commit(parser, options, args):
221 """Import a commit object as a new patch
222 """
223 if len(args) != 1:
224 parser.error('incorrect number of arguments')
225
226 commit_id = args[0]
227
228 if options.name:
229 patch = options.name
230 else:
231 raise CmdException, 'Unkown patch name'
232
233 commit = git.Commit(commit_id)
234
235 if not options.reverse:
236 bottom = commit.get_parent()
237 top = commit_id
238 else:
239 bottom = commit_id
240 top = commit.get_parent()
241
242 message = commit.get_log()
243 author_name, author_email, author_date = \
244 name_email_date(commit.get_author())
245
246 print 'Importing commit %s...' % commit_id,
247 sys.stdout.flush()
248
249 crt_series.new_patch(patch, message = message, can_edit = False,
250 unapplied = True, bottom = bottom, top = top,
251 author_name = author_name,
252 author_email = author_email,
253 author_date = author_date)
254 crt_series.push_patch(patch)
255
256 print 'done'
257 print_crt_patch()
258
259def func(parser, options, args):
260 """Import a GNU diff file or a commit object as a new patch
261 """
262 check_local_changes()
263 check_conflicts()
264 check_head_top_equal()
265
266 if options.commit:
267 import_commit(parser, options, args)
268 else:
269 import_file(parser, options, args)