Mercurial > wikked
changeset 72:4d157afdb560
Added support for Mercurial command server as backend.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 27 Feb 2013 21:50:26 -0800 |
parents | 1d004c53f2b2 |
children | d0be251663b5 |
files | wikked/scm.py wikked/views.py wikked/wiki.py |
diffstat | 3 files changed, 129 insertions(+), 37 deletions(-) [+] |
line wrap: on
line diff
--- a/wikked/scm.py Wed Feb 27 07:06:35 2013 -0800 +++ b/wikked/scm.py Wed Feb 27 21:50:26 2013 -0800 @@ -1,6 +1,7 @@ import re import os import os.path +import time import logging import tempfile import subprocess @@ -51,6 +52,7 @@ class Revision(object): def __init__(self, rev_id=-1): self.rev_id = rev_id + self.rev_name = rev_id self.author = None self.timestamp = 0 self.description = None @@ -65,19 +67,11 @@ return self.rev_id != -1 -class MercurialSourceControl(SourceControl): +class MercurialBaseSourceControl(SourceControl): def __init__(self, root, logger=None): SourceControl.__init__(self, logger) self.root = root - self.hg = 'hg' - self.log_style = os.path.join(os.path.dirname(__file__), 'resources', 'hg_log.style') - self.actions = { - 'A': ACTION_ADD, - 'R': ACTION_DELETE, - 'M': ACTION_EDIT - } - def initRepo(self): # Make a Mercurial repo if there's none. if not os.path.isdir(os.path.join(self.root, '.hg')): @@ -97,11 +91,33 @@ specials = ['.hg', '.hgignore', '.hgtags'] return [os.path.join(self.root, d) for d in specials] + def _run(self, cmd, *args, **kwargs): + exe = [self.hg] + if 'norepo' not in kwargs or not kwargs['norepo']: + exe += ['-R', self.root] + exe.append(cmd) + exe += args + self.logger.debug("Running Mercurial: " + str(exe)) + return subprocess.check_output(exe) + + +class MercurialSourceControl(SourceControl): + def __init__(self, root, logger=None): + MercurialBaseSourceControl.__init__(self, root, logger) + + self.hg = 'hg' + self.log_style = os.path.join(os.path.dirname(__file__), 'resources', 'hg_log.style') + self.actions = { + 'A': ACTION_ADD, + 'R': ACTION_DELETE, + 'M': ACTION_EDIT + } + def getHistory(self, path=None): if path is not None: st_out = self._run('status', path) if len(st_out) > 0 and st_out[0] == '?': - return [Revision()] + return [] log_args = [] if path is not None: @@ -178,6 +194,7 @@ rev = Revision() rev.rev_id = int(m.group(1)) + rev.rev_name = rev.rev_id[:12] rev.rev_hash = m.group(2) rev.author = m.group(3) rev.timestamp = float(m.group(4)) @@ -198,11 +215,83 @@ return rev - def _run(self, cmd, *args, **kwargs): - exe = [self.hg] - if 'norepo' not in kwargs or not kwargs['norepo']: - exe += ['-R', self.root] - exe.append(cmd) - exe += args - self.logger.debug("Running Mercurial: " + str(exe)) - return subprocess.check_output(exe) + +class MercurialCommandServerSourceControl(MercurialBaseSourceControl): + def __init__(self, root, logger=None): + MercurialBaseSourceControl.__init__(self, root, logger) + + import hglib + self.client = hglib.open(self.root) + + def getHistory(self, path=None): + if path is not None: + rel_path = os.path.relpath(path, self.root) + status = self.client.status(include=rel_path) + if len(status) > 0 and status[0] == '?': + return [] + + if path is not None: + repo_revs = self.client.log(files=[path]) + else: + repo_revs = self.client.log() + revisions = [] + for rev in repo_revs: + r = Revision(rev.node) + r.rev_name = rev.node[:12] + r.author = rev.author + r.timestamp = time.mktime(rev.date.timetuple()) + r.description = rev.desc + revisions.append(r) + return revisions + + def getState(self, path): + rel_path = os.path.relpath(path, self.root) + statuses = self.client.status(include=rel_path) + if len(statuses) == 0: + return STATE_COMMITTED + status = statuses[0] + if status[0] == '?' or status[0] == 'A': + return STATE_NEW + if status[0] == 'M': + return STATE_MODIFIED + raise Exception("Unsupported status: %s" % status) + + def getRevision(self, path, rev): + rel_path = os.path.relpath(path, self.root) + return self.client.cat(rel_path, rev=rev) + + def diff(self, path, rev1, rev2): + rel_path = os.path.relpath(path, self.root) + if rev2 is None: + return self.client.diff(files=[rel_path], change=rev1, git=True) + return self.client.diff(files=[rel_path], revs=[rev1, rev2], git=True) + + def commit(self, paths, op_meta): + if 'message' not in op_meta or not op_meta['message']: + raise ValueError("No commit message specified.") + + # Get repo-relative paths. + rel_paths = [os.path.relpath(p, self.root) for p in paths] + + # Check if any of those paths needs to be added. + status = self.client.status(unknown=True) + add_paths = [] + for s in status: + if s[1] in rel_paths: + add_paths.append(s[1]) + if len(add_paths) > 0: + self.client.add(*add_paths) + + # Commit! + if 'author' in op_meta: + self.client.commit(*rel_paths, message=op_meta['message'], user=op_meta['author']) + else: + self.client.commit(*rel_paths, message=op_meta['message']) + + def revert(self, paths=None): + if paths is not None: + rel_paths = [os.path.relpath(p, self.root) for p in paths] + self.client.revert(*rel_paths, clean=True) + else: + self.client.revert(all=True, clean=True) +
--- a/wikked/views.py Wed Feb 27 07:06:35 2013 -0800 +++ b/wikked/views.py Wed Feb 27 21:50:26 2013 -0800 @@ -61,30 +61,33 @@ return meta -def get_history_data(history): +def get_history_data(history, needs_files=False): hist_data = [] for i, rev in enumerate(reversed(history)): rev_data = { 'index': i + 1, 'rev_id': rev.rev_id, - 'rev_hash': rev.rev_hash, + 'rev_name': rev.rev_name, 'author': rev.author, 'timestamp': rev.timestamp, - 'description': rev.description, - 'pages': [] + 'description': rev.description } - for f in rev.files: - f_info = g.wiki.fs.getPageInfo(f['path']) - if f_info is None: - continue - page = g.wiki.getPage(f_info['url']) - if not is_page_readable(page): - continue - rev_data['pages'].append({ - 'url': f_info['url'], - 'action': scm.ACTION_NAMES[f['action']] - }) - if len(rev_data['pages']) > 0: + if needs_files: + rev_data['pages'] = [] + for f in rev.files: + f_info = g.wiki.fs.getPageInfo(f['path']) + if f_info is None: + continue + page = g.wiki.getPage(f_info['url']) + if not is_page_readable(page): + continue + rev_data['pages'].append({ + 'url': f_info['url'], + 'action': scm.ACTION_NAMES[f['action']] + }) + if len(rev_data['pages']) > 0: + hist_data.append(rev_data) + else: hist_data.append(rev_data) return hist_data @@ -311,7 +314,7 @@ @app.route('/api/history') def api_site_history(): history = g.wiki.getHistory() - hist_data = get_history_data(history) + hist_data = get_history_data(history, needs_files=True) result = {'history': hist_data} return make_auth_response(result)
--- a/wikked/wiki.py Wed Feb 27 07:06:35 2013 -0800 +++ b/wikked/wiki.py Wed Feb 27 21:50:26 2013 -0800 @@ -8,7 +8,7 @@ from page import Page, DatabasePage from fs import FileSystem from db import SQLiteDatabase, conn_scope -from scm import MercurialSourceControl +from scm import MercurialCommandServerSourceControl from indexer import WhooshWikiIndex from auth import UserManager @@ -62,7 +62,7 @@ def scm_factory(self, config): scm_type = config.get('wiki', 'scm') if scm_type == 'hg': - return MercurialSourceControl(self.root, logger=self.logger_factory()) + return MercurialCommandServerSourceControl(self.root, logger=self.logger_factory()) else: raise InitializationError("No such source control: " + scm_type)