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