comparison hgext/keyword.py @ 6115:8121e9d7bfd2

keyword: make main class and hg command accessible Switch from global vars to top level dictionary. Goal: make it easier for external tools (like tortoisehg) to hook into keyword extension.
author Christian Ebert <blacktrash@gmx.net>
date Fri, 15 Feb 2008 18:36:28 +0100
parents ee83510fe567
children c74f1d9092f8
comparison
equal deleted inserted replaced
6114:ee83510fe567 6115:8121e9d7bfd2
99 def utcdate(date): 99 def utcdate(date):
100 '''Returns hgdate in cvs-like UTC format.''' 100 '''Returns hgdate in cvs-like UTC format.'''
101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0])) 101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
102 102
103 103
104 _kwtemplater = _cmd = None 104 # make keyword tools accessible
105 kwtools = {'templater': None, 'hgcmd': None}
105 106
106 # store originals of monkeypatches 107 # store originals of monkeypatches
107 _patchfile_init = patch.patchfile.__init__ 108 _patchfile_init = patch.patchfile.__init__
108 _patch_diff = patch.diff 109 _patch_diff = patch.diff
109 _dispatch_parse = dispatch._parse 110 _dispatch_parse = dispatch._parse
111 def _kwpatchfile_init(self, ui, fname, missing=False): 112 def _kwpatchfile_init(self, ui, fname, missing=False):
112 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid 113 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
113 rejects or conflicts due to expanded keywords in working dir.''' 114 rejects or conflicts due to expanded keywords in working dir.'''
114 _patchfile_init(self, ui, fname, missing=missing) 115 _patchfile_init(self, ui, fname, missing=missing)
115 # shrink keywords read from working dir 116 # shrink keywords read from working dir
116 self.lines = _kwtemplater.shrinklines(self.fname, self.lines) 117 kwt = kwtools['templater']
118 self.lines = kwt.shrinklines(self.fname, self.lines)
117 119
118 def _kw_diff(repo, node1=None, node2=None, files=None, match=util.always, 120 def _kw_diff(repo, node1=None, node2=None, files=None, match=util.always,
119 fp=None, changes=None, opts=None): 121 fp=None, changes=None, opts=None):
120 # only expand if comparing against working dir 122 '''Monkeypatch patch.diff to avoid expansion except when
123 comparing against working dir.'''
121 if node2 is not None: 124 if node2 is not None:
122 _kwtemplater.matcher = util.never 125 kwtools['templater'].matcher = util.never
123 if node1 is not None and node1 != repo.changectx().node(): 126 if node1 is not None and node1 != repo.changectx().node():
124 _kwtemplater.restrict = True 127 kwtools['templater'].restrict = True
125 _patch_diff(repo, node1=node1, node2=node2, files=files, match=match, 128 _patch_diff(repo, node1=node1, node2=node2, files=files, match=match,
126 fp=fp, changes=changes, opts=opts) 129 fp=fp, changes=changes, opts=opts)
127 130
128 def _kwweb_changeset(web, req, tmpl): 131 def _kwweb_changeset(web, req, tmpl):
129 '''Wraps webcommands.changeset turning off keyword expansion.''' 132 '''Wraps webcommands.changeset turning off keyword expansion.'''
130 _kwtemplater.matcher = util.never 133 kwtools['templater'].matcher = util.never
131 return web.changeset(tmpl, web.changectx(req)) 134 return web.changeset(tmpl, web.changectx(req))
132 135
133 def _kwweb_filediff(web, req, tmpl): 136 def _kwweb_filediff(web, req, tmpl):
134 '''Wraps webcommands.filediff turning off keyword expansion.''' 137 '''Wraps webcommands.filediff turning off keyword expansion.'''
135 _kwtemplater.matcher = util.never 138 kwtools['templater'].matcher = util.never
136 return web.filediff(tmpl, web.filectx(req)) 139 return web.filediff(tmpl, web.filectx(req))
137 140
138 def _kwdispatch_parse(ui, args): 141 def _kwdispatch_parse(ui, args):
139 '''Monkeypatch dispatch._parse to obtain running hg command.''' 142 '''Monkeypatch dispatch._parse to obtain running hg command.'''
140 global _cmd 143 cmd, func, args, options, cmdoptions = _dispatch_parse(ui, args)
141 _cmd, func, args, options, cmdoptions = _dispatch_parse(ui, args) 144 kwtools['hgcmd'] = cmd
142 return _cmd, func, args, options, cmdoptions 145 return cmd, func, args, options, cmdoptions
143 146
144 # dispatch._parse is run before reposetup, so wrap it here 147 # dispatch._parse is run before reposetup, so wrap it here
145 dispatch._parse = _kwdispatch_parse 148 dispatch._parse = _kwdispatch_parse
146 149
147 150
162 165
163 def __init__(self, ui, repo, inc, exc): 166 def __init__(self, ui, repo, inc, exc):
164 self.ui = ui 167 self.ui = ui
165 self.repo = repo 168 self.repo = repo
166 self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1] 169 self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1]
167 self.restrict = _cmd in restricted.split() 170 self.restrict = kwtools['hgcmd'] in restricted.split()
168 171
169 kwmaps = self.ui.configitems('keywordmaps') 172 kwmaps = self.ui.configitems('keywordmaps')
170 if kwmaps: # override default templates 173 if kwmaps: # override default templates
171 kwmaps = [(k, templater.parsestring(v, quoted=False)) 174 kwmaps = [(k, templater.parsestring(v, quoted=False))
172 for (k, v) in kwmaps] 175 for (k, v) in kwmaps]
268 Subclass of filelog to hook into its read, add, cmp methods. 271 Subclass of filelog to hook into its read, add, cmp methods.
269 Keywords are "stored" unexpanded, and processed on reading. 272 Keywords are "stored" unexpanded, and processed on reading.
270 ''' 273 '''
271 def __init__(self, opener, path): 274 def __init__(self, opener, path):
272 super(kwfilelog, self).__init__(opener, path) 275 super(kwfilelog, self).__init__(opener, path)
276 self.kwt = kwtools['templater']
273 self.path = path 277 self.path = path
274 278
275 def read(self, node): 279 def read(self, node):
276 '''Expands keywords when reading filelog.''' 280 '''Expands keywords when reading filelog.'''
277 data = super(kwfilelog, self).read(node) 281 data = super(kwfilelog, self).read(node)
278 return _kwtemplater.expand(self.path, node, data) 282 return self.kwt.expand(self.path, node, data)
279 283
280 def add(self, text, meta, tr, link, p1=None, p2=None): 284 def add(self, text, meta, tr, link, p1=None, p2=None):
281 '''Removes keyword substitutions when adding to filelog.''' 285 '''Removes keyword substitutions when adding to filelog.'''
282 text = _kwtemplater.shrink(self.path, text) 286 text = self.kwt.shrink(self.path, text)
283 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) 287 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
284 288
285 def cmp(self, node, text): 289 def cmp(self, node, text):
286 '''Removes keyword substitutions for comparison.''' 290 '''Removes keyword substitutions for comparison.'''
287 text = _kwtemplater.shrink(self.path, text) 291 text = self.kwt.shrink(self.path, text)
288 if self.renamed(node): 292 if self.renamed(node):
289 t2 = super(kwfilelog, self).read(node) 293 t2 = super(kwfilelog, self).read(node)
290 return t2 != text 294 return t2 != text
291 return revlog.revlog.cmp(self, node, text) 295 return revlog.revlog.cmp(self, node, text)
292 296
293 def _status(ui, repo, *pats, **opts): 297 def _status(ui, repo, kwt, *pats, **opts):
294 '''Bails out if [keyword] configuration is not active. 298 '''Bails out if [keyword] configuration is not active.
295 Returns status of working directory.''' 299 Returns status of working directory.'''
296 if _kwtemplater: 300 if kwt:
297 files, match, anypats = cmdutil.matchpats(repo, pats, opts) 301 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
298 return repo.status(files=files, match=match, list_clean=True) 302 return repo.status(files=files, match=match, list_clean=True)
299 if ui.configitems('keyword'): 303 if ui.configitems('keyword'):
300 raise util.Abort(_('[keyword] patterns cannot match')) 304 raise util.Abort(_('[keyword] patterns cannot match'))
301 raise util.Abort(_('no [keyword] patterns configured')) 305 raise util.Abort(_('no [keyword] patterns configured'))
302 306
303 def _kwfwrite(ui, repo, expand, *pats, **opts): 307 def _kwfwrite(ui, repo, expand, *pats, **opts):
304 '''Selects files and passes them to kwtemplater.overwrite.''' 308 '''Selects files and passes them to kwtemplater.overwrite.'''
305 status = _status(ui, repo, *pats, **opts) 309 kwt = kwtools['templater']
310 status = _status(ui, repo, kwt, *pats, **opts)
306 modified, added, removed, deleted, unknown, ignored, clean = status 311 modified, added, removed, deleted, unknown, ignored, clean = status
307 if modified or added or removed or deleted: 312 if modified or added or removed or deleted:
308 raise util.Abort(_('outstanding uncommitted changes in given files')) 313 raise util.Abort(_('outstanding uncommitted changes in given files'))
309 wlock = lock = None 314 wlock = lock = None
310 try: 315 try:
311 wlock = repo.wlock() 316 wlock = repo.wlock()
312 lock = repo.lock() 317 lock = repo.lock()
313 _kwtemplater.overwrite(expand=expand, files=clean) 318 kwt.overwrite(expand=expand, files=clean)
314 finally: 319 finally:
315 del wlock, lock 320 del wlock, lock
316 321
317 322
318 def demo(ui, repo, *args, **opts): 323 def demo(ui, repo, *args, **opts):
410 415
411 Crosscheck which files in working directory are potential targets for 416 Crosscheck which files in working directory are potential targets for
412 keyword expansion. 417 keyword expansion.
413 That is, files matched by [keyword] config patterns but not symlinks. 418 That is, files matched by [keyword] config patterns but not symlinks.
414 ''' 419 '''
415 status = _status(ui, repo, *pats, **opts) 420 kwt = kwtools['templater']
421 status = _status(ui, repo, kwt, *pats, **opts)
416 modified, added, removed, deleted, unknown, ignored, clean = status 422 modified, added, removed, deleted, unknown, ignored, clean = status
417 files = modified + added + clean 423 files = modified + added + clean
418 if opts.get('untracked'): 424 if opts.get('untracked'):
419 files += unknown 425 files += unknown
420 files.sort() 426 files.sort()
421 wctx = repo.workingctx() 427 wctx = repo.workingctx()
422 islink = lambda p: 'l' in wctx.fileflags(p) 428 islink = lambda p: 'l' in wctx.fileflags(p)
423 kwfiles = [f for f in files if _kwtemplater.iskwfile(f, islink)] 429 kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
424 cwd = pats and repo.getcwd() or '' 430 cwd = pats and repo.getcwd() or ''
425 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or () 431 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
426 if opts.get('all') or opts.get('ignore'): 432 if opts.get('all') or opts.get('ignore'):
427 kwfstats += (('I', [f for f in files if f not in kwfiles]),) 433 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
428 for char, filenames in kwfstats: 434 for char, filenames in kwfstats:
449 Wraps commit to overwrite configured files with updated 455 Wraps commit to overwrite configured files with updated
450 keyword substitutions. 456 keyword substitutions.
451 This is done for local repos only, and only if there are 457 This is done for local repos only, and only if there are
452 files configured at all for keyword substitution.''' 458 files configured at all for keyword substitution.'''
453 459
454 global _kwtemplater
455
456 try: 460 try:
457 if (not repo.local() or _cmd in nokwcommands.split() 461 if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split()
458 or '.hg' in util.splitpath(repo.root) 462 or '.hg' in util.splitpath(repo.root)
459 or repo._url.startswith('bundle:')): 463 or repo._url.startswith('bundle:')):
460 return 464 return
461 except AttributeError: 465 except AttributeError:
462 pass 466 pass
468 else: 472 else:
469 exc.append(pat) 473 exc.append(pat)
470 if not inc: 474 if not inc:
471 return 475 return
472 476
473 _kwtemplater = kwtemplater(ui, repo, inc, exc) 477 kwtools['templater'] = kwt = kwtemplater(ui, repo, inc, exc)
474 478
475 class kwrepo(repo.__class__): 479 class kwrepo(repo.__class__):
476 def file(self, f): 480 def file(self, f):
477 if f[0] == '/': 481 if f[0] == '/':
478 f = f[1:] 482 f = f[1:]
479 return kwfilelog(self.sopener, f) 483 return kwfilelog(self.sopener, f)
480 484
481 def wread(self, filename): 485 def wread(self, filename):
482 data = super(kwrepo, self).wread(filename) 486 data = super(kwrepo, self).wread(filename)
483 return _kwtemplater.wread(filename, data) 487 return kwt.wread(filename, data)
484 488
485 def commit(self, files=None, text='', user=None, date=None, 489 def commit(self, files=None, text='', user=None, date=None,
486 match=util.always, force=False, force_editor=False, 490 match=util.always, force=False, force_editor=False,
487 p1=None, p2=None, extra={}, empty_ok=False): 491 p1=None, p2=None, extra={}, empty_ok=False):
488 wlock = lock = None 492 wlock = lock = None
517 521
518 # restore commit hooks 522 # restore commit hooks
519 for name, cmd in commithooks.iteritems(): 523 for name, cmd in commithooks.iteritems():
520 ui.setconfig('hooks', name, cmd) 524 ui.setconfig('hooks', name, cmd)
521 if node is not None: 525 if node is not None:
522 _kwtemplater.overwrite(node=node) 526 kwt.overwrite(node=node)
523 repo.hook('commit', node=node, parent1=_p1, parent2=_p2) 527 repo.hook('commit', node=node, parent1=_p1, parent2=_p2)
524 return node 528 return node
525 finally: 529 finally:
526 del wlock, lock 530 del wlock, lock
527 531