stg import now extracts Message-ID header
[stgit.git] / stgit / commands / series.py
blob6fffb4e0307d6703bc6141a6d8c1c26be0716385
1 from stgit.argparse import opt, patch_range
2 from stgit.commands.common import CmdException, DirectoryHasRepository, parse_patches
3 from stgit.config import config
4 from stgit.out import out
6 __copyright__ = """
7 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see http://www.gnu.org/licenses/.
20 """
22 help = 'Print the patch series'
23 kind = 'stack'
24 usage = ['[options] [--] [<patch-range>]']
25 description = """
26 Show all the patches in the series, or just those in the given range,
27 ordered from top to bottom.
29 The applied patches are prefixed with a +++ (except the current patch,
30 which is prefixed with a +>+), the unapplied patches with a +-+, and
31 the hidden patches with a +!+.
33 Empty patches are prefixed with a '0'."""
35 args = [patch_range('applied_patches', 'unapplied_patches', 'hidden_patches')]
36 options = [
37 opt(
38 '-b',
39 '--branch',
40 args=['stg_branches'],
41 short='Use BRANCH instead of the default branch',
43 opt(
44 '-a',
45 '--all',
46 action='store_true',
47 short='Show all patches, including the hidden ones',
49 opt(
50 '-A',
51 '--applied',
52 action='store_true',
53 short='Show the applied patches only',
55 opt(
56 '-U',
57 '--unapplied',
58 action='store_true',
59 short='Show the unapplied patches only',
61 opt(
62 '-H',
63 '--hidden',
64 action='store_true',
65 short='Show the hidden patches only',
67 opt(
68 '-m',
69 '--missing',
70 metavar='BRANCH',
71 args=['stg_branches'],
72 short='Show patches in BRANCH missing in current',
74 opt(
75 '-c',
76 '--count',
77 action='store_true',
78 short='Print the number of patches in the series',
80 opt(
81 '-d',
82 '--description',
83 action='store_true',
84 short='Show a short description for each patch',
86 opt(
87 '--no-description',
88 action='store_false',
89 short='Inverse of --description',
90 dest='description',
92 opt(
93 '--author',
94 action='store_true',
95 short='Show the author name for each patch',
97 opt(
98 '-e',
99 '--empty',
100 action='store_true',
101 short='Check whether patches are empty',
102 long="""
103 Before the +++, +>+, +-+, and +!+ prefixes, print a column
104 that contains either +0+ (for empty patches) or a space (for
105 non-empty patches).""",
107 opt(
108 '--showbranch',
109 action='store_true',
110 short='Append the branch name to the listed patches',
112 opt(
113 '--noprefix',
114 action='store_true',
115 short='Do not show the patch status prefix',
117 opt(
118 '-s',
119 '--short',
120 action='store_true',
121 short='List just the patches around the topmost patch',
125 directory = DirectoryHasRepository()
128 def __get_description(stack, patch):
129 """Extract and return a patch's short description"""
130 cd = stack.patches[patch].data
131 return cd.message_str.strip().split('\n', 1)[0].rstrip()
134 def __get_author(stack, patch):
135 """Extract and return a patch's short description"""
136 return stack.patches[patch].data.author.name
139 def __render_text(text, effects):
140 _effects = {
141 'none': 0,
142 'bright': 1,
143 'dim': 2,
144 'black_foreground': 30,
145 'red_foreground': 31,
146 'green_foreground': 32,
147 'yellow_foreground': 33,
148 'blue_foreground': 34,
149 'magenta_foreground': 35,
150 'cyan_foreground': 36,
151 'white_foreground': 37,
152 'black_background': 40,
153 'red_background': 41,
154 'green_background': 42,
155 'yellow_background': 44,
156 'blue_background': 44,
157 'magenta_background': 45,
158 'cyan_background': 46,
159 'white_background': 47,
161 start = [str(_effects[e]) for e in effects.split() if e in _effects]
162 start = '\033[' + ';'.join(start) + 'm'
163 stop = '\033[' + str(_effects['none']) + 'm'
164 return ''.join([start, text, stop])
167 def __print_patch(stack, patch, branch_str, prefix, length, options, effects):
168 """Print a patch name, description and various markers."""
169 if options.noprefix:
170 prefix = ''
171 elif options.empty:
172 if stack.patches[patch].data.is_nochange():
173 prefix = '0' + prefix
174 else:
175 prefix = ' ' + prefix
177 patch_str = branch_str + patch
179 if options.description or options.author:
180 patch_str = patch_str.ljust(length)
182 if options.description:
183 output = prefix + patch_str + ' # ' + __get_description(stack, patch)
184 elif options.author:
185 output = prefix + patch_str + ' # ' + __get_author(stack, patch)
186 else:
187 output = prefix + patch_str
188 if not effects or not out.isatty:
189 out.stdout(output)
190 else:
191 out.stdout(__render_text(output, effects))
194 def func(parser, options, args):
195 """Show the patch series"""
196 if options.all and options.short:
197 raise CmdException('combining --all and --short is meaningless')
199 stack = directory.repository.get_stack(options.branch)
200 if options.missing:
201 cmp_stack = stack
202 stack = directory.repository.get_stack(options.missing)
204 if options.description is None:
205 options.description = config.getbool('stgit.series.description')
207 # current series patches
208 applied = unapplied = hidden = ()
209 if options.applied or options.unapplied or options.hidden:
210 if options.all:
211 raise CmdException('--all cannot be used with --applied/unapplied/hidden')
212 if options.applied:
213 applied = stack.patchorder.applied
214 if options.unapplied:
215 unapplied = stack.patchorder.unapplied
216 if options.hidden:
217 hidden = stack.patchorder.hidden
218 elif options.all:
219 applied = stack.patchorder.applied
220 unapplied = stack.patchorder.unapplied
221 hidden = stack.patchorder.hidden
222 else:
223 applied = stack.patchorder.applied
224 unapplied = stack.patchorder.unapplied
226 if options.missing:
227 cmp_patches = cmp_stack.patchorder.all
228 else:
229 cmp_patches = ()
231 # the filtering range covers the whole series
232 if args:
233 show_patches = parse_patches(args, applied + unapplied + hidden, len(applied))
234 else:
235 show_patches = applied + unapplied + hidden
237 # missing filtering
238 show_patches = [p for p in show_patches if p not in cmp_patches]
240 # filter the patches
241 applied = [p for p in applied if p in show_patches]
242 unapplied = [p for p in unapplied if p in show_patches]
243 hidden = [p for p in hidden if p in show_patches]
245 if options.short:
246 nr = int(config.get('stgit.shortnr'))
247 if len(applied) > nr:
248 applied = applied[-(nr + 1) :]
249 n = len(unapplied)
250 if n > nr:
251 unapplied = unapplied[:nr]
252 elif n < nr:
253 hidden = hidden[: nr - n]
255 patches = applied + unapplied + hidden
257 if options.count:
258 out.stdout(len(patches))
259 return
261 if not patches:
262 return
264 if options.showbranch:
265 branch_str = stack.name + ':'
266 else:
267 branch_str = ''
269 max_len = len(branch_str) + max(len(p) for p in patches)
271 if applied:
272 for p in applied[:-1]:
273 __print_patch(
274 stack,
276 branch_str,
277 '+ ',
278 max_len,
279 options,
280 config.get("stgit.color.applied"),
282 __print_patch(
283 stack,
284 applied[-1],
285 branch_str,
286 '> ',
287 max_len,
288 options,
289 config.get("stgit.color.current"),
292 for p in unapplied:
293 __print_patch(
294 stack,
296 branch_str,
297 '- ',
298 max_len,
299 options,
300 config.get("stgit.color.unapplied"),
303 for p in hidden:
304 __print_patch(
305 stack,
307 branch_str,
308 '! ',
309 max_len,
310 options,
311 config.get("stgit.color.hidden"),