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