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