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