chiark / gitweb /
Fix up the help text for "stg edit"
[stgit] / stgit / commands / edit.py
CommitLineData
ed60fdae
CM
1"""Patch editing command
2"""
3
4__copyright__ = """
5Copyright (C) 2007, Catalin Marinas <catalin.marinas@gmail.com>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License version 2 as
9published by the Free Software Foundation.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19"""
20
21from optparse import OptionParser, make_option
22from email.Utils import formatdate
23
24from stgit.commands.common import *
25from stgit.utils import *
26from stgit.out import *
27from stgit import stack, git
28
29
30help = 'edit a patch description or diff'
31usage = """%prog [options] [<patch>]
32
b52f3780
KH
33Edit the description and author information of the given patch (or the
34current patch if no patch name was given). With --diff, also edit the
35diff.
ed60fdae 36
b52f3780 37The editor is invoked with the following contents:
ed60fdae 38
b52f3780
KH
39 Patch short description
40
41 From: A U Thor <author@example.com>
ed60fdae
CM
42 Date: creation date
43
b52f3780
KH
44 Patch long description
45
46If --diff was specified, the diff appears at the bottom, after a
47separator:
48
49 ---
ed60fdae 50
b52f3780 51 Diff text
ed60fdae
CM
52
53Command-line options can be used to modify specific information
54without invoking the editor.
55
56If the patch diff is edited but the patch application fails, the
57rejected patch is stored in the .stgit-failed.patch file (and also in
58.stgit-edit.{diff,txt}). The edited patch can be replaced with one of
59these files using the '--file' and '--diff' options.
60"""
61
62options = [make_option('-d', '--diff',
b52f3780 63 help = 'edit the patch diff',
ed60fdae
CM
64 action = 'store_true'),
65 make_option('-f', '--file',
66 help = 'use FILE instead of invoking the editor'),
67 make_option('-O', '--diff-opts',
68 help = 'options to pass to git-diff'),
69 make_option('--undo',
70 help = 'revert the commit generated by the last edit',
71 action = 'store_true'),
72 make_option('-a', '--annotate', metavar = 'NOTE',
73 help = 'annotate the patch log entry'),
74 make_option('-m', '--message',
75 help = 'replace the patch description with MESSAGE'),
76 make_option('--author', metavar = '"NAME <EMAIL>"',
77 help = 'replae the author details with "NAME <EMAIL>"'),
78 make_option('--authname',
79 help = 'replace the author name with AUTHNAME'),
80 make_option('--authemail',
81 help = 'replace the author e-mail with AUTHEMAIL'),
82 make_option('--authdate',
83 help = 'replace the author date with AUTHDATE'),
84 make_option('--commname',
85 help = 'replace the committer name with COMMNAME'),
86 make_option('--commemail',
87 help = 'replace the committer e-mail with COMMEMAIL')
88 ] + make_sign_options()
89
90def __update_patch(pname, fname, options):
91 """Update the current patch from the given file.
92 """
93 patch = crt_series.get_patch(pname)
94
95 bottom = patch.get_bottom()
96 top = patch.get_top()
97
98 f = open(fname)
99 message, author_name, author_email, author_date, diff = parse_patch(f)
100 f.close()
101
ead8e892
CM
102 out.start('Updating patch "%s"' % pname)
103
ed60fdae
CM
104 if options.diff:
105 git.switch(bottom)
106 try:
107 git.apply_patch(fname)
108 except:
109 # avoid inconsistent repository state
110 git.switch(top)
111 raise
112
ed60fdae
CM
113 crt_series.refresh_patch(message = message,
114 author_name = author_name,
115 author_email = author_email,
116 author_date = author_date,
117 backup = True, log = 'edit')
ead8e892 118
ed60fdae
CM
119 if crt_series.empty_patch(pname):
120 out.done('empty patch')
121 else:
122 out.done()
123
124def __edit_update_patch(pname, options):
125 """Edit the given patch interactively.
126 """
127 patch = crt_series.get_patch(pname)
128
129 if options.diff_opts:
130 if not options.diff:
131 raise CmdException, '--diff-opts only available with --diff'
132 diff_flags = options.diff_opts.split()
133 else:
134 diff_flags = []
135
136 # generate the file to be edited
137 descr = patch.get_description().strip()
138 descr_lines = descr.split('\n')
139 authdate = patch.get_authdate()
140
141 short_descr = descr_lines[0].rstrip()
142 long_descr = reduce(lambda x, y: x + '\n' + y,
143 descr_lines[1:], '').strip()
144
145 tmpl = '%(shortdescr)s\n\n' \
146 'From: %(authname)s <%(authemail)s>\n'
147 if authdate:
148 tmpl += 'Date: %(authdate)s\n'
149 tmpl += '\n%(longdescr)s\n'
150
151 tmpl_dict = {
152 'shortdescr': short_descr,
153 'longdescr': long_descr,
154 'authname': patch.get_authname(),
155 'authemail': patch.get_authemail(),
156 'authdate': patch.get_authdate()
157 }
158
159 if options.diff:
160 # add the patch diff to the edited file
161 bottom = patch.get_bottom()
162 top = patch.get_top()
163
164 tmpl += '---\n\n' \
165 '%(diffstat)s\n' \
166 '%(diff)s'
167
168 tmpl_dict['diffstat'] = git.diffstat(rev1 = bottom, rev2 = top)
169 tmpl_dict['diff'] = git.diff(rev1 = bottom, rev2 = top,
170 diff_flags = diff_flags)
171
172 for key in tmpl_dict:
173 # make empty strings if key is not available
174 if tmpl_dict[key] is None:
175 tmpl_dict[key] = ''
176
177 text = tmpl % tmpl_dict
178
179 if options.diff:
180 fname = '.stgit-edit.diff'
181 else:
182 fname = '.stgit-edit.txt'
183
184 # write the file to be edited
185 f = open(fname, 'w+')
186 f.write(text)
187 f.close()
188
189 # invoke the editor
190 call_editor(fname)
191
192 __update_patch(pname, fname, options)
193
194def func(parser, options, args):
195 """Edit the given patch or the current one.
196 """
197 crt_pname = crt_series.get_current()
198
199 if not args:
200 pname = crt_pname
201 if not pname:
202 raise CmdException, 'No patches applied'
203 elif len(args) == 1:
204 pname = args[0]
205 if crt_series.patch_unapplied(pname) or crt_series.patch_hidden(pname):
206 raise CmdException, 'Cannot edit unapplied or hidden patches'
207 elif not crt_series.patch_applied(pname):
208 raise CmdException, 'Unknown patch "%s"' % pname
209 else:
210 parser.error('incorrect number of arguments')
211
212 check_local_changes()
213 check_conflicts()
214 check_head_top_equal()
215
216 if pname != crt_pname:
217 # Go to the patch to be edited
218 applied = crt_series.get_applied()
219 between = applied[:applied.index(pname):-1]
220 pop_patches(between)
221
222 if options.author:
223 options.authname, options.authemail = name_email(options.author)
224
225 if options.undo:
226 out.start('Undoing the editing of "%s"' % pname)
227 crt_series.undo_refresh()
228 out.done()
229 elif options.message or options.authname or options.authemail \
230 or options.authdate or options.commname or options.commemail \
231 or options.sign_str:
232 # just refresh the patch with the given information
233 out.start('Updating patch "%s"' % pname)
234 crt_series.refresh_patch(message = options.message,
235 author_name = options.authname,
236 author_email = options.authemail,
237 author_date = options.authdate,
238 committer_name = options.commname,
239 committer_email = options.commemail,
240 backup = True, sign_str = options.sign_str,
241 log = 'edit',
242 notes = options.annotate)
243 out.done()
244 elif options.file:
245 __update_patch(pname, options.file, options)
246 else:
247 __edit_update_patch(pname, options)
248
249 if pname != crt_pname:
250 # Push the patches back
251 between.reverse()
252 push_patches(between)