chiark / gitweb /
Don't split long and short description in "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 39 From: A U Thor <author@example.com>
ed60fdae
CM
40 Date: creation date
41
7e301a73 42 Patch description
b52f3780
KH
43
44If --diff was specified, the diff appears at the bottom, after a
45separator:
46
47 ---
ed60fdae 48
b52f3780 49 Diff text
ed60fdae
CM
50
51Command-line options can be used to modify specific information
52without invoking the editor.
53
54If the patch diff is edited but the patch application fails, the
55rejected patch is stored in the .stgit-failed.patch file (and also in
56.stgit-edit.{diff,txt}). The edited patch can be replaced with one of
57these files using the '--file' and '--diff' options.
58"""
59
60options = [make_option('-d', '--diff',
b52f3780 61 help = 'edit the patch diff',
ed60fdae
CM
62 action = 'store_true'),
63 make_option('-f', '--file',
64 help = 'use FILE instead of invoking the editor'),
65 make_option('-O', '--diff-opts',
66 help = 'options to pass to git-diff'),
67 make_option('--undo',
68 help = 'revert the commit generated by the last edit',
69 action = 'store_true'),
70 make_option('-a', '--annotate', metavar = 'NOTE',
71 help = 'annotate the patch log entry'),
72 make_option('-m', '--message',
73 help = 'replace the patch description with MESSAGE'),
74 make_option('--author', metavar = '"NAME <EMAIL>"',
75 help = 'replae the author details with "NAME <EMAIL>"'),
76 make_option('--authname',
77 help = 'replace the author name with AUTHNAME'),
78 make_option('--authemail',
79 help = 'replace the author e-mail with AUTHEMAIL'),
80 make_option('--authdate',
81 help = 'replace the author date with AUTHDATE'),
82 make_option('--commname',
83 help = 'replace the committer name with COMMNAME'),
84 make_option('--commemail',
85 help = 'replace the committer e-mail with COMMEMAIL')
86 ] + make_sign_options()
87
88def __update_patch(pname, fname, options):
89 """Update the current patch from the given file.
90 """
91 patch = crt_series.get_patch(pname)
92
93 bottom = patch.get_bottom()
94 top = patch.get_top()
95
96 f = open(fname)
97 message, author_name, author_email, author_date, diff = parse_patch(f)
98 f.close()
99
ead8e892
CM
100 out.start('Updating patch "%s"' % pname)
101
ed60fdae
CM
102 if options.diff:
103 git.switch(bottom)
104 try:
105 git.apply_patch(fname)
106 except:
107 # avoid inconsistent repository state
108 git.switch(top)
109 raise
110
ed60fdae
CM
111 crt_series.refresh_patch(message = message,
112 author_name = author_name,
113 author_email = author_email,
114 author_date = author_date,
115 backup = True, log = 'edit')
ead8e892 116
ed60fdae
CM
117 if crt_series.empty_patch(pname):
118 out.done('empty patch')
119 else:
120 out.done()
121
122def __edit_update_patch(pname, options):
123 """Edit the given patch interactively.
124 """
125 patch = crt_series.get_patch(pname)
126
127 if options.diff_opts:
128 if not options.diff:
129 raise CmdException, '--diff-opts only available with --diff'
130 diff_flags = options.diff_opts.split()
131 else:
132 diff_flags = []
133
134 # generate the file to be edited
135 descr = patch.get_description().strip()
ed60fdae
CM
136 authdate = patch.get_authdate()
137
7e301a73 138 tmpl = 'From: %(authname)s <%(authemail)s>\n'
ed60fdae
CM
139 if authdate:
140 tmpl += 'Date: %(authdate)s\n'
7e301a73 141 tmpl += '\n%(descr)s\n'
ed60fdae
CM
142
143 tmpl_dict = {
7e301a73 144 'descr': descr,
ed60fdae
CM
145 'authname': patch.get_authname(),
146 'authemail': patch.get_authemail(),
147 'authdate': patch.get_authdate()
148 }
149
150 if options.diff:
151 # add the patch diff to the edited file
152 bottom = patch.get_bottom()
153 top = patch.get_top()
154
155 tmpl += '---\n\n' \
156 '%(diffstat)s\n' \
157 '%(diff)s'
158
159 tmpl_dict['diffstat'] = git.diffstat(rev1 = bottom, rev2 = top)
160 tmpl_dict['diff'] = git.diff(rev1 = bottom, rev2 = top,
161 diff_flags = diff_flags)
162
163 for key in tmpl_dict:
164 # make empty strings if key is not available
165 if tmpl_dict[key] is None:
166 tmpl_dict[key] = ''
167
168 text = tmpl % tmpl_dict
169
170 if options.diff:
171 fname = '.stgit-edit.diff'
172 else:
173 fname = '.stgit-edit.txt'
174
175 # write the file to be edited
176 f = open(fname, 'w+')
177 f.write(text)
178 f.close()
179
180 # invoke the editor
181 call_editor(fname)
182
183 __update_patch(pname, fname, options)
184
185def func(parser, options, args):
186 """Edit the given patch or the current one.
187 """
188 crt_pname = crt_series.get_current()
189
190 if not args:
191 pname = crt_pname
192 if not pname:
193 raise CmdException, 'No patches applied'
194 elif len(args) == 1:
195 pname = args[0]
196 if crt_series.patch_unapplied(pname) or crt_series.patch_hidden(pname):
197 raise CmdException, 'Cannot edit unapplied or hidden patches'
198 elif not crt_series.patch_applied(pname):
199 raise CmdException, 'Unknown patch "%s"' % pname
200 else:
201 parser.error('incorrect number of arguments')
202
203 check_local_changes()
204 check_conflicts()
205 check_head_top_equal()
206
207 if pname != crt_pname:
208 # Go to the patch to be edited
209 applied = crt_series.get_applied()
210 between = applied[:applied.index(pname):-1]
211 pop_patches(between)
212
213 if options.author:
214 options.authname, options.authemail = name_email(options.author)
215
216 if options.undo:
217 out.start('Undoing the editing of "%s"' % pname)
218 crt_series.undo_refresh()
219 out.done()
220 elif options.message or options.authname or options.authemail \
221 or options.authdate or options.commname or options.commemail \
222 or options.sign_str:
223 # just refresh the patch with the given information
224 out.start('Updating patch "%s"' % pname)
225 crt_series.refresh_patch(message = options.message,
226 author_name = options.authname,
227 author_email = options.authemail,
228 author_date = options.authdate,
229 committer_name = options.commname,
230 committer_email = options.commemail,
231 backup = True, sign_str = options.sign_str,
232 log = 'edit',
233 notes = options.annotate)
234 out.done()
235 elif options.file:
236 __update_patch(pname, options.file, options)
237 else:
238 __edit_update_patch(pname, options)
239
240 if pname != crt_pname:
241 # Push the patches back
242 between.reverse()
243 push_patches(between)