279 class kwfilelog(filelog.filelog): |
279 class kwfilelog(filelog.filelog): |
280 ''' |
280 ''' |
281 Subclass of filelog to hook into its read, add, cmp methods. |
281 Subclass of filelog to hook into its read, add, cmp methods. |
282 Keywords are "stored" unexpanded, and processed on reading. |
282 Keywords are "stored" unexpanded, and processed on reading. |
283 ''' |
283 ''' |
284 def __init__(self, opener, path, kwtemplater): |
284 def __init__(self, opener, path): |
285 super(kwfilelog, self).__init__(opener, path) |
285 super(kwfilelog, self).__init__(opener, path) |
286 self.kwtemplater = kwtemplater |
286 _kwtemplater.path = path |
287 self.kwtemplater.path = path |
|
288 |
287 |
289 def kwctread(self, node, expand): |
288 def kwctread(self, node, expand): |
290 '''Reads expanding and counting keywords |
289 '''Reads expanding and counting keywords |
291 (only called from kwtemplater.overwrite).''' |
290 (only called from kwtemplater.overwrite).''' |
292 data = super(kwfilelog, self).read(node) |
291 data = super(kwfilelog, self).read(node) |
293 return self.kwtemplater.process(node, data, expand) |
292 return _kwtemplater.process(node, data, expand) |
294 |
293 |
295 def read(self, node): |
294 def read(self, node): |
296 '''Expands keywords when reading filelog.''' |
295 '''Expands keywords when reading filelog.''' |
297 data = super(kwfilelog, self).read(node) |
296 data = super(kwfilelog, self).read(node) |
298 return self.kwtemplater.expand(node, data) |
297 return _kwtemplater.expand(node, data) |
299 |
298 |
300 def add(self, text, meta, tr, link, p1=None, p2=None): |
299 def add(self, text, meta, tr, link, p1=None, p2=None): |
301 '''Removes keyword substitutions when adding to filelog.''' |
300 '''Removes keyword substitutions when adding to filelog.''' |
302 text = self.kwtemplater.shrink(text) |
301 text = _kwtemplater.shrink(text) |
303 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) |
302 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) |
304 |
303 |
305 def cmp(self, node, text): |
304 def cmp(self, node, text): |
306 '''Removes keyword substitutions for comparison.''' |
305 '''Removes keyword substitutions for comparison.''' |
307 text = self.kwtemplater.shrink(text) |
306 text = _kwtemplater.shrink(text) |
308 if self.renamed(node): |
307 if self.renamed(node): |
309 t2 = super(kwfilelog, self).read(node) |
308 t2 = super(kwfilelog, self).read(node) |
310 return t2 != text |
309 return t2 != text |
311 return revlog.revlog.cmp(self, node, text) |
310 return revlog.revlog.cmp(self, node, text) |
312 |
311 |
313 def _iskwfile(f, kwtemplater, link): |
312 def _iskwfile(f, link): |
314 return not link(f) and kwtemplater.matcher(f) |
313 return not link(f) and _kwtemplater.matcher(f) |
315 |
314 |
316 def _status(ui, repo, kwtemplater, *pats, **opts): |
315 def _status(ui, repo, *pats, **opts): |
317 '''Bails out if [keyword] configuration is not active. |
316 '''Bails out if [keyword] configuration is not active. |
318 Returns status of working directory.''' |
317 Returns status of working directory.''' |
319 if kwtemplater: |
318 if _kwtemplater: |
320 files, match, anypats = cmdutil.matchpats(repo, pats, opts) |
319 files, match, anypats = cmdutil.matchpats(repo, pats, opts) |
321 return repo.status(files=files, match=match, list_clean=True) |
320 return repo.status(files=files, match=match, list_clean=True) |
322 if ui.configitems('keyword'): |
321 if ui.configitems('keyword'): |
323 raise util.Abort(_('[keyword] patterns cannot match')) |
322 raise util.Abort(_('[keyword] patterns cannot match')) |
324 raise util.Abort(_('no [keyword] patterns configured')) |
323 raise util.Abort(_('no [keyword] patterns configured')) |
325 |
324 |
326 def _overwrite(ui, repo, kwtemplater, node=None, expand=True, files=None): |
325 def _overwrite(ui, repo, node=None, expand=True, files=None): |
327 '''Overwrites selected files expanding/shrinking keywords.''' |
326 '''Overwrites selected files expanding/shrinking keywords.''' |
328 ctx = repo.changectx(node) |
327 ctx = repo.changectx(node) |
329 mf = ctx.manifest() |
328 mf = ctx.manifest() |
330 if files is None: |
329 if files is None: |
331 notify = ui.debug # commit |
330 notify = ui.debug # commit |
332 files = [f for f in ctx.files() if mf.has_key(f)] |
331 files = [f for f in ctx.files() if mf.has_key(f)] |
333 else: |
332 else: |
334 notify = ui.note # kwexpand/kwshrink |
333 notify = ui.note # kwexpand/kwshrink |
335 candidates = [f for f in files if _iskwfile(f, kwtemplater, mf.linkf)] |
334 candidates = [f for f in files if _iskwfile(f, mf.linkf)] |
336 if candidates: |
335 if candidates: |
337 overwritten = [] |
336 overwritten = [] |
338 candidates.sort() |
337 candidates.sort() |
339 action = expand and 'expanding' or 'shrinking' |
338 action = expand and 'expanding' or 'shrinking' |
340 kwtemplater.node = node or ctx.node() |
339 _kwtemplater.node = node or ctx.node() |
341 for f in candidates: |
340 for f in candidates: |
342 fp = repo.file(f, kwmatch=True) |
341 fp = repo.file(f, kwmatch=True) |
343 data, kwfound = fp.kwctread(mf[f], expand) |
342 data, kwfound = fp.kwctread(mf[f], expand) |
344 if kwfound: |
343 if kwfound: |
345 notify(_('overwriting %s %s keywords\n') % (f, action)) |
344 notify(_('overwriting %s %s keywords\n') % (f, action)) |
347 overwritten.append(f) |
346 overwritten.append(f) |
348 _normal(repo, overwritten) |
347 _normal(repo, overwritten) |
349 |
348 |
350 def _kwfwrite(ui, repo, expand, *pats, **opts): |
349 def _kwfwrite(ui, repo, expand, *pats, **opts): |
351 '''Selects files and passes them to _overwrite.''' |
350 '''Selects files and passes them to _overwrite.''' |
352 global _kwtemplater |
351 status = _status(ui, repo, *pats, **opts) |
353 status = _status(ui, repo, _kwtemplater, *pats, **opts) |
|
354 modified, added, removed, deleted, unknown, ignored, clean = status |
352 modified, added, removed, deleted, unknown, ignored, clean = status |
355 if modified or added or removed or deleted: |
353 if modified or added or removed or deleted: |
356 raise util.Abort(_('outstanding uncommitted changes in given files')) |
354 raise util.Abort(_('outstanding uncommitted changes in given files')) |
357 wlock = lock = None |
355 wlock = lock = None |
358 try: |
356 try: |
359 wlock = repo.wlock() |
357 wlock = repo.wlock() |
360 lock = repo.lock() |
358 lock = repo.lock() |
361 _overwrite(ui, repo, _kwtemplater, expand=expand, files=clean) |
359 _overwrite(ui, repo, expand=expand, files=clean) |
362 finally: |
360 finally: |
363 del wlock, lock |
361 del wlock, lock |
364 |
362 |
365 |
363 |
366 def demo(ui, repo, *args, **opts): |
364 def demo(ui, repo, *args, **opts): |
388 branchname = 'demobranch' |
386 branchname = 'demobranch' |
389 tmpdir = tempfile.mkdtemp('', 'kwdemo.') |
387 tmpdir = tempfile.mkdtemp('', 'kwdemo.') |
390 ui.note(_('creating temporary repo at %s\n') % tmpdir) |
388 ui.note(_('creating temporary repo at %s\n') % tmpdir) |
391 repo = localrepo.localrepository(ui, path=tmpdir, create=True) |
389 repo = localrepo.localrepository(ui, path=tmpdir, create=True) |
392 ui.setconfig('keyword', fn, '') |
390 ui.setconfig('keyword', fn, '') |
393 if args or opts['rcfile']: |
391 if args or opts.get('rcfile'): |
394 kwstatus = 'custom' |
392 kwstatus = 'custom' |
395 if opts['rcfile']: |
393 if opts.get('rcfile'): |
396 ui.readconfig(opts['rcfile']) |
394 ui.readconfig(opts.get('rcfile')) |
397 if opts['default']: |
395 if opts.get('default'): |
398 kwstatus = 'default' |
396 kwstatus = 'default' |
399 kwmaps = kwtemplater.templates |
397 kwmaps = kwtemplater.templates |
400 if ui.configitems('keywordmaps'): |
398 if ui.configitems('keywordmaps'): |
401 # override maps from optional rcfile |
399 # override maps from optional rcfile |
402 for k, v in kwmaps.items(): |
400 for k, v in kwmaps.items(): |
459 |
457 |
460 Crosscheck which files in working directory are potential targets for |
458 Crosscheck which files in working directory are potential targets for |
461 keyword expansion. |
459 keyword expansion. |
462 That is, files matched by [keyword] config patterns but not symlinks. |
460 That is, files matched by [keyword] config patterns but not symlinks. |
463 ''' |
461 ''' |
464 global _kwtemplater |
462 status = _status(ui, repo, *pats, **opts) |
465 status = _status(ui, repo, _kwtemplater, *pats, **opts) |
|
466 modified, added, removed, deleted, unknown, ignored, clean = status |
463 modified, added, removed, deleted, unknown, ignored, clean = status |
467 if opts['untracked']: |
464 if opts.get('untracked'): |
468 files = modified + added + unknown + clean |
465 files = modified + added + unknown + clean |
469 else: |
466 else: |
470 files = modified + added + clean |
467 files = modified + added + clean |
471 files.sort() |
468 files.sort() |
472 # use the full definition of repo._link for backwards compatibility |
469 # use the full definition of repo._link for backwards compatibility |
473 kwfiles = [f for f in files if _kwtemplater.matcher(f) |
470 kwfiles = [f for f in files if _kwtemplater.matcher(f) |
474 and not os.path.islink(repo.wjoin(f))] |
471 and not os.path.islink(repo.wjoin(f))] |
475 cwd = pats and repo.getcwd() or '' |
472 cwd = pats and repo.getcwd() or '' |
476 allf = opts['all'] |
473 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or () |
477 ignore = opts['ignore'] |
474 if opts.get('all') or opts.get('ignore'): |
478 if ignore: |
|
479 kwfstats = () |
|
480 else: |
|
481 kwfstats = (('K', kwfiles),) |
|
482 if allf or ignore: |
|
483 kwfstats += (('I', [f for f in files if f not in kwfiles]),) |
475 kwfstats += (('I', [f for f in files if f not in kwfiles]),) |
484 for char, filenames in kwfstats: |
476 for char, filenames in kwfstats: |
485 format = (allf or ui.verbose) and '%s %%s\n' % char or '%s\n' |
477 format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n' |
486 for f in filenames: |
478 for f in filenames: |
487 ui.write(format % _pathto(repo, f, cwd)) |
479 ui.write(format % _pathto(repo, f, cwd)) |
488 |
480 |
489 def shrink(ui, repo, *pats, **opts): |
481 def shrink(ui, repo, *pats, **opts): |
490 '''revert expanded keywords in working directory |
482 '''revert expanded keywords in working directory |
505 Wraps commit to overwrite configured files with updated |
497 Wraps commit to overwrite configured files with updated |
506 keyword substitutions. |
498 keyword substitutions. |
507 This is done for local repos only, and only if there are |
499 This is done for local repos only, and only if there are |
508 files configured at all for keyword substitution.''' |
500 files configured at all for keyword substitution.''' |
509 |
501 |
510 nokwcommands = ('add', 'addremove', 'bundle', 'clone', 'copy', 'export', |
502 def kwbailout(): |
511 'grep', 'identify', 'incoming', 'init', 'outgoing', 'push', |
503 '''Obtains command via simplified cmdline parsing, |
512 'remove', 'rename', 'rollback', 'convert') |
504 returns True if keyword expansion not needed.''' |
513 |
505 nokwcommands = ('add', 'addremove', 'bundle', 'clone', 'copy', |
514 def _getcmd(): |
506 'export', 'grep', 'identify', 'incoming', 'init', |
515 '''Simplified argument parsing as we are only interested in command.''' |
507 'outgoing', 'push', 'remove', 'rename', 'rollback', |
|
508 'convert') |
516 args = fancyopts(sys.argv[1:], commands.globalopts, {}) |
509 args = fancyopts(sys.argv[1:], commands.globalopts, {}) |
517 if args: |
510 if args: |
518 aliases, i = findcmd(ui, args[0], commands.table) |
511 aliases, i = findcmd(ui, args[0], commands.table) |
519 return aliases[0] |
512 return aliases[0] in nokwcommands |
520 |
513 |
521 if not repo.local() or _getcmd() in nokwcommands: |
514 if not repo.local() or kwbailout(): |
522 return |
515 return |
523 |
516 |
524 inc, exc = [], ['.hgtags'] |
517 inc, exc = [], ['.hgtags'] |
525 for pat, opt in ui.configitems('keyword'): |
518 for pat, opt in ui.configitems('keyword'): |
526 if opt != 'ignore': |
519 if opt != 'ignore': |
536 class kwrepo(repo.__class__): |
529 class kwrepo(repo.__class__): |
537 def file(self, f, kwmatch=False): |
530 def file(self, f, kwmatch=False): |
538 if f[0] == '/': |
531 if f[0] == '/': |
539 f = f[1:] |
532 f = f[1:] |
540 if kwmatch or _kwtemplater.matcher(f): |
533 if kwmatch or _kwtemplater.matcher(f): |
541 return kwfilelog(self.sopener, f, _kwtemplater) |
534 return kwfilelog(self.sopener, f) |
542 return filelog.filelog(self.sopener, f) |
535 return filelog.filelog(self.sopener, f) |
543 |
536 |
544 def _commit(self, files, text, user, date, match, force, lock, wlock, |
537 def _commit(self, files, text, user, date, match, force, lock, wlock, |
545 force_editor, p1, p2, extra): |
538 force_editor, p1, p2, extra): |
546 '''Private commit wrapper for backwards compatibility.''' |
539 '''Private commit wrapper for backwards compatibility.''' |