##// END OF EJS Templates
keyword: compact setting of optional arguments
Christian Ebert -
r6504:1be53f93 default
parent child Browse files
Show More
@@ -1,560 +1,556 b''
1 1 # keyword.py - $Keyword$ expansion for Mercurial
2 2 #
3 3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7 #
8 8 # $Id$
9 9 #
10 10 # Keyword expansion hack against the grain of a DSCM
11 11 #
12 12 # There are many good reasons why this is not needed in a distributed
13 13 # SCM, still it may be useful in very small projects based on single
14 14 # files (like LaTeX packages), that are mostly addressed to an audience
15 15 # not running a version control system.
16 16 #
17 17 # For in-depth discussion refer to
18 18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
19 19 #
20 20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 21 #
22 22 # Binary files are not touched.
23 23 #
24 24 # Setup in hgrc:
25 25 #
26 26 # [extensions]
27 27 # # enable extension
28 28 # hgext.keyword =
29 29 #
30 30 # Files to act upon/ignore are specified in the [keyword] section.
31 31 # Customized keyword template mappings in the [keywordmaps] section.
32 32 #
33 33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34 34
35 35 '''keyword expansion in local repositories
36 36
37 37 This extension expands RCS/CVS-like or self-customized $Keywords$
38 38 in tracked text files selected by your configuration.
39 39
40 40 Keywords are only expanded in local repositories and not stored in
41 41 the change history. The mechanism can be regarded as a convenience
42 42 for the current user or for archive distribution.
43 43
44 44 Configuration is done in the [keyword] and [keywordmaps] sections
45 45 of hgrc files.
46 46
47 47 Example:
48 48
49 49 [keyword]
50 50 # expand keywords in every python file except those matching "x*"
51 51 **.py =
52 52 x* = ignore
53 53
54 54 Note: the more specific you are in your filename patterns
55 55 the less you lose speed in huge repos.
56 56
57 57 For [keywordmaps] template mapping and expansion demonstration and
58 58 control run "hg kwdemo".
59 59
60 60 An additional date template filter {date|utcdate} is provided.
61 61
62 62 The default template mappings (view with "hg kwdemo -d") can be replaced
63 63 with customized keywords and templates.
64 64 Again, run "hg kwdemo" to control the results of your config changes.
65 65
66 66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 67 the risk of inadvertedly storing expanded keywords in the change history.
68 68
69 69 To force expansion after enabling it, or a configuration change, run
70 70 "hg kwexpand".
71 71
72 72 Also, when committing with the record extension or using mq's qrecord, be aware
73 73 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
74 74 question to update keyword expansions after all changes have been checked in.
75 75
76 76 Expansions spanning more than one line and incremental expansions,
77 77 like CVS' $Log$, are not supported. A keyword template map
78 78 "Log = {desc}" expands to the first line of the changeset description.
79 79 '''
80 80
81 81 from mercurial import commands, cmdutil, context, dispatch, filelog, revlog
82 82 from mercurial import patch, localrepo, templater, templatefilters, util
83 83 from mercurial.hgweb import webcommands
84 84 from mercurial.node import nullid, hex
85 85 from mercurial.i18n import _
86 86 import re, shutil, tempfile, time
87 87
88 88 commands.optionalrepo += ' kwdemo'
89 89
90 90 # hg commands that do not act on keywords
91 91 nokwcommands = ('add addremove bundle copy export grep incoming init'
92 92 ' log outgoing push rename rollback tip'
93 93 ' convert email glog')
94 94
95 95 # hg commands that trigger expansion only when writing to working dir,
96 96 # not when reading filelog, and unexpand when reading from working dir
97 97 restricted = 'record qfold qimport qnew qpush qrefresh qrecord'
98 98
99 99 def utcdate(date):
100 100 '''Returns hgdate in cvs-like UTC format.'''
101 101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
102 102
103 103 # make keyword tools accessible
104 104 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
105 105
106 106
107 107 class kwtemplater(object):
108 108 '''
109 109 Sets up keyword templates, corresponding keyword regex, and
110 110 provides keyword substitution functions.
111 111 '''
112 112 templates = {
113 113 'Revision': '{node|short}',
114 114 'Author': '{author|user}',
115 115 'Date': '{date|utcdate}',
116 116 'RCSFile': '{file|basename},v',
117 117 'Source': '{root}/{file},v',
118 118 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
119 119 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
120 120 }
121 121
122 122 def __init__(self, ui, repo):
123 123 self.ui = ui
124 124 self.repo = repo
125 125 self.matcher = util.matcher(repo.root,
126 126 inc=kwtools['inc'], exc=kwtools['exc'])[1]
127 127 self.restrict = kwtools['hgcmd'] in restricted.split()
128 128
129 129 kwmaps = self.ui.configitems('keywordmaps')
130 130 if kwmaps: # override default templates
131 kwmaps = [(k, templater.parsestring(v, quoted=False))
131 kwmaps = [(k, templater.parsestring(v, False))
132 132 for (k, v) in kwmaps]
133 133 self.templates = dict(kwmaps)
134 134 escaped = map(re.escape, self.templates.keys())
135 135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
136 136 self.re_kw = re.compile(kwpat)
137 137
138 138 templatefilters.filters['utcdate'] = utcdate
139 139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
140 140 False, '', False)
141 141
142 142 def getnode(self, path, fnode):
143 143 '''Derives changenode from file path and filenode.'''
144 144 # used by kwfilelog.read and kwexpand
145 145 c = context.filectx(self.repo, path, fileid=fnode)
146 146 return c.node()
147 147
148 148 def substitute(self, data, path, node, subfunc):
149 149 '''Replaces keywords in data with expanded template.'''
150 150 def kwsub(mobj):
151 151 kw = mobj.group(1)
152 152 self.ct.use_template(self.templates[kw])
153 153 self.ui.pushbuffer()
154 154 self.ct.show(changenode=node, root=self.repo.root, file=path)
155 155 ekw = templatefilters.firstline(self.ui.popbuffer())
156 156 return '$%s: %s $' % (kw, ekw)
157 157 return subfunc(kwsub, data)
158 158
159 159 def expand(self, path, node, data):
160 160 '''Returns data with keywords expanded.'''
161 161 if not self.restrict and self.matcher(path) and not util.binary(data):
162 162 changenode = self.getnode(path, node)
163 163 return self.substitute(data, path, changenode, self.re_kw.sub)
164 164 return data
165 165
166 166 def iskwfile(self, path, islink):
167 167 '''Returns true if path matches [keyword] pattern
168 168 and is not a symbolic link.
169 169 Caveat: localrepository._link fails on Windows.'''
170 170 return self.matcher(path) and not islink(path)
171 171
172 172 def overwrite(self, node=None, expand=True, files=None):
173 173 '''Overwrites selected files expanding/shrinking keywords.'''
174 174 ctx = self.repo.changectx(node)
175 175 mf = ctx.manifest()
176 176 if node is not None: # commit
177 177 files = [f for f in ctx.files() if f in mf]
178 178 notify = self.ui.debug
179 179 else: # kwexpand/kwshrink
180 180 notify = self.ui.note
181 181 candidates = [f for f in files if self.iskwfile(f, mf.linkf)]
182 182 if candidates:
183 183 self.restrict = True # do not expand when reading
184 184 candidates.sort()
185 185 action = expand and 'expanding' or 'shrinking'
186 186 for f in candidates:
187 187 fp = self.repo.file(f)
188 188 data = fp.read(mf[f])
189 189 if util.binary(data):
190 190 continue
191 191 if expand:
192 192 changenode = node or self.getnode(f, mf[f])
193 193 data, found = self.substitute(data, f, changenode,
194 194 self.re_kw.subn)
195 195 else:
196 196 found = self.re_kw.search(data)
197 197 if found:
198 198 notify(_('overwriting %s %s keywords\n') % (f, action))
199 199 self.repo.wwrite(f, data, mf.flags(f))
200 200 self.repo.dirstate.normal(f)
201 201 self.restrict = False
202 202
203 203 def shrinktext(self, text):
204 204 '''Unconditionally removes all keyword substitutions from text.'''
205 205 return self.re_kw.sub(r'$\1$', text)
206 206
207 207 def shrink(self, fname, text):
208 208 '''Returns text with all keyword substitutions removed.'''
209 209 if self.matcher(fname) and not util.binary(text):
210 210 return self.shrinktext(text)
211 211 return text
212 212
213 213 def shrinklines(self, fname, lines):
214 214 '''Returns lines with keyword substitutions removed.'''
215 215 if self.matcher(fname):
216 216 text = ''.join(lines)
217 217 if not util.binary(text):
218 218 return self.shrinktext(text).splitlines(True)
219 219 return lines
220 220
221 221 def wread(self, fname, data):
222 222 '''If in restricted mode returns data read from wdir with
223 223 keyword substitutions removed.'''
224 224 return self.restrict and self.shrink(fname, data) or data
225 225
226 226 class kwfilelog(filelog.filelog):
227 227 '''
228 228 Subclass of filelog to hook into its read, add, cmp methods.
229 229 Keywords are "stored" unexpanded, and processed on reading.
230 230 '''
231 231 def __init__(self, opener, kwt, path):
232 232 super(kwfilelog, self).__init__(opener, path)
233 233 self.kwt = kwt
234 234 self.path = path
235 235
236 236 def read(self, node):
237 237 '''Expands keywords when reading filelog.'''
238 238 data = super(kwfilelog, self).read(node)
239 239 return self.kwt.expand(self.path, node, data)
240 240
241 241 def add(self, text, meta, tr, link, p1=None, p2=None):
242 242 '''Removes keyword substitutions when adding to filelog.'''
243 243 text = self.kwt.shrink(self.path, text)
244 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
244 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
245 245
246 246 def cmp(self, node, text):
247 247 '''Removes keyword substitutions for comparison.'''
248 248 text = self.kwt.shrink(self.path, text)
249 249 if self.renamed(node):
250 250 t2 = super(kwfilelog, self).read(node)
251 251 return t2 != text
252 252 return revlog.revlog.cmp(self, node, text)
253 253
254 254 def _status(ui, repo, kwt, *pats, **opts):
255 255 '''Bails out if [keyword] configuration is not active.
256 256 Returns status of working directory.'''
257 257 if kwt:
258 258 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
259 259 return repo.status(files=files, match=match, list_clean=True)
260 260 if ui.configitems('keyword'):
261 261 raise util.Abort(_('[keyword] patterns cannot match'))
262 262 raise util.Abort(_('no [keyword] patterns configured'))
263 263
264 264 def _kwfwrite(ui, repo, expand, *pats, **opts):
265 265 '''Selects files and passes them to kwtemplater.overwrite.'''
266 266 kwt = kwtools['templater']
267 267 status = _status(ui, repo, kwt, *pats, **opts)
268 268 modified, added, removed, deleted, unknown, ignored, clean = status
269 269 if modified or added or removed or deleted:
270 270 raise util.Abort(_('outstanding uncommitted changes in given files'))
271 271 wlock = lock = None
272 272 try:
273 273 wlock = repo.wlock()
274 274 lock = repo.lock()
275 275 kwt.overwrite(expand=expand, files=clean)
276 276 finally:
277 277 del wlock, lock
278 278
279 279
280 280 def demo(ui, repo, *args, **opts):
281 281 '''print [keywordmaps] configuration and an expansion example
282 282
283 283 Show current, custom, or default keyword template maps
284 284 and their expansion.
285 285
286 286 Extend current configuration by specifying maps as arguments
287 287 and optionally by reading from an additional hgrc file.
288 288
289 289 Override current keyword template maps with "default" option.
290 290 '''
291 291 def demostatus(stat):
292 292 ui.status(_('\n\t%s\n') % stat)
293 293
294 294 def demoitems(section, items):
295 295 ui.write('[%s]\n' % section)
296 296 for k, v in items:
297 297 ui.write('%s = %s\n' % (k, v))
298 298
299 299 msg = 'hg keyword config and expansion example'
300 300 kwstatus = 'current'
301 301 fn = 'demo.txt'
302 302 branchname = 'demobranch'
303 303 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
304 304 ui.note(_('creating temporary repo at %s\n') % tmpdir)
305 repo = localrepo.localrepository(ui, path=tmpdir, create=True)
305 repo = localrepo.localrepository(ui, tmpdir, True)
306 306 ui.setconfig('keyword', fn, '')
307 307 if args or opts.get('rcfile'):
308 308 kwstatus = 'custom'
309 309 if opts.get('rcfile'):
310 310 ui.readconfig(opts.get('rcfile'))
311 311 if opts.get('default'):
312 312 kwstatus = 'default'
313 313 kwmaps = kwtemplater.templates
314 314 if ui.configitems('keywordmaps'):
315 315 # override maps from optional rcfile
316 316 for k, v in kwmaps.iteritems():
317 317 ui.setconfig('keywordmaps', k, v)
318 318 elif args:
319 319 # simulate hgrc parsing
320 320 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
321 321 fp = repo.opener('hgrc', 'w')
322 322 fp.writelines(rcmaps)
323 323 fp.close()
324 324 ui.readconfig(repo.join('hgrc'))
325 325 if not opts.get('default'):
326 326 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
327 327 uisetup(ui)
328 328 reposetup(ui, repo)
329 329 for k, v in ui.configitems('extensions'):
330 330 if k.endswith('keyword'):
331 331 extension = '%s = %s' % (k, v)
332 332 break
333 333 demostatus('config using %s keyword template maps' % kwstatus)
334 334 ui.write('[extensions]\n%s\n' % extension)
335 335 demoitems('keyword', ui.configitems('keyword'))
336 336 demoitems('keywordmaps', kwmaps.iteritems())
337 337 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
338 338 repo.wopener(fn, 'w').write(keywords)
339 339 repo.add([fn])
340 340 path = repo.wjoin(fn)
341 341 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
342 342 ui.note(keywords)
343 343 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
344 344 # silence branch command if not verbose
345 345 quiet = ui.quiet
346 346 ui.quiet = not ui.verbose
347 347 commands.branch(ui, repo, branchname)
348 348 ui.quiet = quiet
349 349 for name, cmd in ui.configitems('hooks'):
350 350 if name.split('.', 1)[0].find('commit') > -1:
351 351 repo.ui.setconfig('hooks', name, '')
352 352 ui.note(_('unhooked all commit hooks\n'))
353 353 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
354 354 repo.commit(text=msg)
355 355 format = ui.verbose and ' in %s' % path or ''
356 356 demostatus('%s keywords expanded%s' % (kwstatus, format))
357 357 ui.write(repo.wread(fn))
358 358 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
359 359 shutil.rmtree(tmpdir, ignore_errors=True)
360 360
361 361 def expand(ui, repo, *pats, **opts):
362 362 '''expand keywords in working directory
363 363
364 364 Run after (re)enabling keyword expansion.
365 365
366 366 kwexpand refuses to run if given files contain local changes.
367 367 '''
368 368 # 3rd argument sets expansion to True
369 369 _kwfwrite(ui, repo, True, *pats, **opts)
370 370
371 371 def files(ui, repo, *pats, **opts):
372 372 '''print files currently configured for keyword expansion
373 373
374 374 Crosscheck which files in working directory are potential targets for
375 375 keyword expansion.
376 376 That is, files matched by [keyword] config patterns but not symlinks.
377 377 '''
378 378 kwt = kwtools['templater']
379 379 status = _status(ui, repo, kwt, *pats, **opts)
380 380 modified, added, removed, deleted, unknown, ignored, clean = status
381 381 files = modified + added + clean
382 382 if opts.get('untracked'):
383 383 files += unknown
384 384 files.sort()
385 385 wctx = repo.workingctx()
386 386 islink = lambda p: 'l' in wctx.fileflags(p)
387 387 kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
388 388 cwd = pats and repo.getcwd() or ''
389 389 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
390 390 if opts.get('all') or opts.get('ignore'):
391 391 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
392 392 for char, filenames in kwfstats:
393 393 format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
394 394 for f in filenames:
395 395 ui.write(format % repo.pathto(f, cwd))
396 396
397 397 def shrink(ui, repo, *pats, **opts):
398 398 '''revert expanded keywords in working directory
399 399
400 400 Run before changing/disabling active keywords
401 401 or if you experience problems with "hg import" or "hg merge".
402 402
403 403 kwshrink refuses to run if given files contain local changes.
404 404 '''
405 405 # 3rd argument sets expansion to False
406 406 _kwfwrite(ui, repo, False, *pats, **opts)
407 407
408 408
409 409 def uisetup(ui):
410 410 '''Collects [keyword] config in kwtools.
411 411 Monkeypatches dispatch._parse if needed.'''
412 412
413 413 for pat, opt in ui.configitems('keyword'):
414 414 if opt != 'ignore':
415 415 kwtools['inc'].append(pat)
416 416 else:
417 417 kwtools['exc'].append(pat)
418 418
419 419 if kwtools['inc']:
420 420 def kwdispatch_parse(ui, args):
421 421 '''Monkeypatch dispatch._parse to obtain running hg command.'''
422 422 cmd, func, args, options, cmdoptions = dispatch_parse(ui, args)
423 423 kwtools['hgcmd'] = cmd
424 424 return cmd, func, args, options, cmdoptions
425 425
426 426 dispatch_parse = dispatch._parse
427 427 dispatch._parse = kwdispatch_parse
428 428
429 429 def reposetup(ui, repo):
430 430 '''Sets up repo as kwrepo for keyword substitution.
431 431 Overrides file method to return kwfilelog instead of filelog
432 432 if file matches user configuration.
433 433 Wraps commit to overwrite configured files with updated
434 434 keyword substitutions.
435 435 Monkeypatches patch and webcommands.'''
436 436
437 437 try:
438 438 if (not repo.local() or not kwtools['inc']
439 439 or kwtools['hgcmd'] in nokwcommands.split()
440 440 or '.hg' in util.splitpath(repo.root)
441 441 or repo._url.startswith('bundle:')):
442 442 return
443 443 except AttributeError:
444 444 pass
445 445
446 446 kwtools['templater'] = kwt = kwtemplater(ui, repo)
447 447
448 448 class kwrepo(repo.__class__):
449 449 def file(self, f):
450 450 if f[0] == '/':
451 451 f = f[1:]
452 452 return kwfilelog(self.sopener, kwt, f)
453 453
454 454 def wread(self, filename):
455 455 data = super(kwrepo, self).wread(filename)
456 456 return kwt.wread(filename, data)
457 457
458 458 def commit(self, files=None, text='', user=None, date=None,
459 459 match=util.always, force=False, force_editor=False,
460 460 p1=None, p2=None, extra={}, empty_ok=False):
461 461 wlock = lock = None
462 462 _p1 = _p2 = None
463 463 try:
464 464 wlock = self.wlock()
465 465 lock = self.lock()
466 466 # store and postpone commit hooks
467 467 commithooks = {}
468 468 for name, cmd in ui.configitems('hooks'):
469 469 if name.split('.', 1)[0] == 'commit':
470 470 commithooks[name] = cmd
471 471 ui.setconfig('hooks', name, None)
472 472 if commithooks:
473 473 # store parents for commit hook environment
474 474 if p1 is None:
475 475 _p1, _p2 = repo.dirstate.parents()
476 476 else:
477 477 _p1, _p2 = p1, p2 or nullid
478 478 _p1 = hex(_p1)
479 479 if _p2 == nullid:
480 480 _p2 = ''
481 481 else:
482 482 _p2 = hex(_p2)
483 483
484 node = super(kwrepo,
485 self).commit(files=files, text=text, user=user,
486 date=date, match=match, force=force,
487 force_editor=force_editor,
488 p1=p1, p2=p2, extra=extra,
489 empty_ok=empty_ok)
484 n = super(kwrepo, self).commit(files, text, user, date, match,
485 force, force_editor, p1, p2,
486 extra, empty_ok)
490 487
491 488 # restore commit hooks
492 489 for name, cmd in commithooks.iteritems():
493 490 ui.setconfig('hooks', name, cmd)
494 if node is not None:
495 kwt.overwrite(node=node)
496 repo.hook('commit', node=node, parent1=_p1, parent2=_p2)
497 return node
491 if n is not None:
492 kwt.overwrite(node=n)
493 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
494 return n
498 495 finally:
499 496 del wlock, lock
500 497
501 498 # monkeypatches
502 499 def kwpatchfile_init(self, ui, fname, missing=False):
503 500 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
504 501 rejects or conflicts due to expanded keywords in working dir.'''
505 patchfile_init(self, ui, fname, missing=missing)
502 patchfile_init(self, ui, fname, missing)
506 503 # shrink keywords read from working dir
507 504 self.lines = kwt.shrinklines(self.fname, self.lines)
508 505
509 506 def kw_diff(repo, node1=None, node2=None, files=None, match=util.always,
510 507 fp=None, changes=None, opts=None):
511 508 '''Monkeypatch patch.diff to avoid expansion except when
512 509 comparing against working dir.'''
513 510 if node2 is not None:
514 511 kwt.matcher = util.never
515 512 elif node1 is not None and node1 != repo.changectx().node():
516 513 kwt.restrict = True
517 patch_diff(repo, node1=node1, node2=node2, files=files, match=match,
518 fp=fp, changes=changes, opts=opts)
514 patch_diff(repo, node1, node2, files, match, fp, changes, opts)
519 515
520 516 def kwweb_changeset(web, req, tmpl):
521 517 '''Wraps webcommands.changeset turning off keyword expansion.'''
522 518 kwt.matcher = util.never
523 519 return webcommands_changeset(web, req, tmpl)
524 520
525 521 def kwweb_filediff(web, req, tmpl):
526 522 '''Wraps webcommands.filediff turning off keyword expansion.'''
527 523 kwt.matcher = util.never
528 524 return webcommands_filediff(web, req, tmpl)
529 525
530 526 repo.__class__ = kwrepo
531 527
532 528 patchfile_init = patch.patchfile.__init__
533 529 patch_diff = patch.diff
534 530 webcommands_changeset = webcommands.changeset
535 531 webcommands_filediff = webcommands.filediff
536 532
537 533 patch.patchfile.__init__ = kwpatchfile_init
538 534 patch.diff = kw_diff
539 535 webcommands.changeset = webcommands.rev = kwweb_changeset
540 536 webcommands.filediff = webcommands.diff = kwweb_filediff
541 537
542 538
543 539 cmdtable = {
544 540 'kwdemo':
545 541 (demo,
546 542 [('d', 'default', None, _('show default keyword template maps')),
547 543 ('f', 'rcfile', [], _('read maps from rcfile'))],
548 544 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
549 545 'kwexpand': (expand, commands.walkopts,
550 546 _('hg kwexpand [OPTION]... [FILE]...')),
551 547 'kwfiles':
552 548 (files,
553 549 [('a', 'all', None, _('show keyword status flags of all files')),
554 550 ('i', 'ignore', None, _('show files excluded from expansion')),
555 551 ('u', 'untracked', None, _('additionally show untracked files')),
556 552 ] + commands.walkopts,
557 553 _('hg kwfiles [OPTION]... [FILE]...')),
558 554 'kwshrink': (shrink, commands.walkopts,
559 555 _('hg kwshrink [OPTION]... [FILE]...')),
560 556 }
General Comments 0
You need to be logged in to leave comments. Login now