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