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