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