# redminehelper: Redmine helper extension for Mercurial # # Copyright 2010 Alessio Franceschelli (alefranz.net) # Copyright 2010-2011 Yuya Nishihara # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. """helper commands for Redmine to reduce the number of hg calls To test this extension, please try:: $ hg --config extensions.redminehelper=redminehelper.py rhsummary I/O encoding: :file path: urlencoded, raw string :tag name: utf-8 :branch name: utf-8 :node: 12-digits (short) hex string Output example of rhsummary:: ... Output example of rhmanifest:: ... ... """ import re, time, cgi, urllib from mercurial import cmdutil, commands, node, error _x = cgi.escape _u = lambda s: cgi.escape(urllib.quote(s)) def _tip(ui, repo): # see mercurial/commands.py:tip def tiprev(): try: return len(repo) - 1 except TypeError: # Mercurial < 1.1 return repo.changelog.count() - 1 tipctx = repo.changectx(tiprev()) ui.write('\n' % (tipctx.rev(), _x(node.short(tipctx.node())))) _SPECIAL_TAGS = ('tip',) def _tags(ui, repo): # see mercurial/commands.py:tags for t, n in reversed(repo.tagslist()): if t in _SPECIAL_TAGS: continue try: r = repo.changelog.rev(n) except error.LookupError: continue ui.write('\n' % (r, _x(node.short(n)), _x(t))) def _branches(ui, repo): # see mercurial/commands.py:branches def iterbranches(): for t, n in repo.branchtags().iteritems(): yield t, n, repo.changelog.rev(n) def branchheads(branch): try: return repo.branchheads(branch, closed=False) except TypeError: # Mercurial < 1.2 return repo.branchheads(branch) for t, n, r in sorted(iterbranches(), key=lambda e: e[2], reverse=True): if repo.lookup(r) in branchheads(t): ui.write('\n' % (r, _x(node.short(n)), _x(t))) def _manifest(ui, repo, path, rev): ctx = repo.changectx(rev) ui.write('\n' % (ctx.rev(), _u(path))) known = set() pathprefix = (path.rstrip('/') + '/').lstrip('/') for f, n in sorted(ctx.manifest().iteritems(), key=lambda e: e[0]): if not f.startswith(pathprefix): continue name = re.sub(r'/.*', '/', f[len(pathprefix):]) if name in known: continue known.add(name) if name.endswith('/'): ui.write('\n' % _x(urllib.quote(name[:-1]))) else: fctx = repo.filectx(f, fileid=n) tm, tzoffset = fctx.date() ui.write('\n' % (_u(name), fctx.rev(), _x(node.short(fctx.node())), tm, fctx.size(), )) ui.write('\n') def rhcat(ui, repo, file1, *pats, **opts): return commands.cat(ui, repo, urllib.unquote(file1), *map(urllib.unquote, pats), **opts) def rhdiff(ui, repo, *pats, **opts): """diff repository (or selected files)""" change = opts.pop('change', None) if change: # add -c option for Mercurial<1.1 base = repo.changectx(change).parents()[0].rev() opts['rev'] = [str(base), change] opts['nodates'] = True return commands.diff(ui, repo, *map(urllib.unquote, pats), **opts) def rhmanifest(ui, repo, path='', **opts): """output the sub-manifest of the specified directory""" ui.write('\n') ui.write('\n') ui.write('\n' % _u(repo.root)) try: _manifest(ui, repo, urllib.unquote(path), opts.get('rev')) finally: ui.write('\n') ui.write('\n') def rhsummary(ui, repo, **opts): """output the summary of the repository""" ui.write('\n') ui.write('\n') ui.write('\n' % _u(repo.root)) try: _tip(ui, repo) _tags(ui, repo) _branches(ui, repo) # TODO: bookmarks in core (Mercurial>=1.8) finally: ui.write('\n') ui.write('\n') # This extension should be compatible with Mercurial 0.9.5. # Note that Mercurial 0.9.5 doesn't have extensions.wrapfunction(). cmdtable = { 'rhcat': (rhcat, [('r', 'rev', '', 'revision')], 'hg rhcat ([-r REV] ...) FILE...'), 'rhdiff': (rhdiff, [('r', 'rev', [], 'revision'), ('c', 'change', '', 'change made by revision')], 'hg rhdiff ([-c REV] | [-r REV] ...) [FILE]...'), 'rhmanifest': (rhmanifest, [('r', 'rev', '', 'show the specified revision')], 'hg rhmanifest [-r REV] [PATH]'), 'rhsummary': (rhsummary, [], 'hg rhsummary'), }