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