author | Alberto Bertogli
<albertito@blitiri.com.ar> 2008-11-30 21:57:06 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2008-11-30 21:59:49 UTC |
parent | 7b470430a9bf4c365e247c6744c094d1a7e89dbd |
config.py.sample | +1 | -0 |
wikiri.cgi | +162 | -1 |
diff --git a/config.py.sample b/config.py.sample index 6c5eb71..5758cc7 100644 --- a/config.py.sample +++ b/config.py.sample @@ -26,6 +26,7 @@ title = "I like wikis" # History backend. Can be one of: # - "none" for no history # - "git" to use git (needs git and the data path to be a repository) +# - "darcs" to use darcs (needs darcs and the data path to be a repository) # - "auto" to select automatically (looks if the data path is a repository) history = "auto" diff --git a/wikiri.cgi b/wikiri.cgi index dab565d..52f5c87 100755 --- a/wikiri.cgi +++ b/wikiri.cgi @@ -31,6 +31,7 @@ title = "I like wikis" # History backend. Can be one of: # - "none" for no history # - "git" to use git (needs git and the data path to be a repository) +# - "darcs" to use darcs (needs darcs and the data path to be a repository) # - "auto" to select automatically (looks if the data path is a repository) history = "auto" @@ -644,7 +645,7 @@ class Article (object): # # History backends # -# At the moment we only support git (it's optional, though) +# At the moment we support none, git and darcs class HistoryError (Exception): pass @@ -654,10 +655,14 @@ class History: if history == 'auto': if os.path.isdir(data_path + '.git'): self.be = GitBackend(data_path) + elif os.path.isdir(data_path + '_darcs'): + self.be = DarcsBackend(data_path) else: self.be = NoneBackend(data_path) elif history == 'git': self.be = GitBackend(data_path) + elif history == 'darcs': + self.be = DarcsBackend(data_path) else: self.be = NoneBackend(data_path) @@ -665,6 +670,15 @@ class History: self.be.commit(msg, author = author) def log(self, fname): + # log() yields commits of the form: + # { + # 'commit': commit id + # 'author': author of the commit + # 'committer': committer + # 'atime': time of the change itself + # 'ctime': time of the commit + # 'msg': commit msg (one line) + # } return self.be.log(file = fname) def add(self, *files): @@ -834,6 +848,153 @@ def _add_times(commit): commit['committer'] = committer commit['ctime'] = datetime.datetime.fromtimestamp(epoch) +class DarcsBackend: + def __init__(self, repopath): + self.repo = repopath + self.prevdir = None + + def cdrepo(self): + self.prevdir = os.getcwd() + os.chdir(self.repo) + + def cdback(self): + os.chdir(self.prevdir) + + def darcs(self, *args): + # delay the import to avoid the hit on a regular page view + import subprocess + self.cdrepo() + cmd = subprocess.Popen( ['darcs'] + list(args), + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = sys.stderr ) + self.cdback() + return cmd + + def darcsq(self, *args): + cmd = self.darcs(*args) + stdout, stderr = cmd.communicate() + return cmd.returncode + + def commit(self, msg, author = None): + if not author: + author = "Unknown <unknown@example.com>" + + # see if we have something to commit; if not, just return + if self.darcsq('whatsnew') != 0: + return + + r = self.darcsq('record', + '-a', + '-m', msg, + '-A', author) + if r != 0: + raise HistoryError, r + + def log(self, file = None, files = None): + import xml.dom.minidom as minidom + + if not files: + files = [] + if file: + files.append(file) + + cmd = self.darcs("changes", + "--xml-output", + "--", *files) + cmd.stdin.close() + + xml = minidom.parse(cmd.stdout) + + cmd.wait() + if cmd.returncode != 0: + raise HistoryError, cmd.returncode + + for p in xml.getElementsByTagName('patch'): + # ignore patches not directly inside <changelog> (they + # can be, for instance, inside <created_as>, which we + # want to ignore) + if p.parentNode.nodeName != 'changelog': + continue + + cid = p.getAttribute("hash") + author = p.getAttribute('author') + atime = p.getAttribute('date') + atime = datetime.datetime.strptime(atime, + "%Y%m%d%H%M%S") + msg = p.getElementsByTagName('name')[0] + msg = msg.childNodes[0].data + msg = msg.split('\n')[0].strip() + + commit = { + 'commit': cid.encode('utf8'), + 'author': author.encode('utf8'), + 'committer': author.encode('utf8'), + 'atime': atime, + 'ctime': atime, + 'msg': msg.encode('utf8'), + } + yield commit + + def add(self, *files): + r = self.darcsq('add', "--", *files) + # 0 means success, 2 means the file was already there (which + # is ok because we always add the files) + if r != 0 and r != 2: + raise HistoryError, r + + def remove(self, *files): + r = self.darcsq('remove', '--', *files) + if r != 0: + raise HistoryError, r + + def get_content(self, fname, commitid): + cmd = self.darcs("show", "contents", + "--match", "hash %s" % commitid, + "--", fname) + content = cmd.stdout.read() + cmd.wait() + return content + + def get_commit(self, cid): + import xml.dom.minidom as minidom + + cmd = self.darcs("changes", + "--xml-output", + "--match", "hash %s" % cid) + cmd.stdin.close() + + xml = minidom.parse(cmd.stdout) + + cmd.wait() + if cmd.returncode != 0: + raise HistoryError, cmd.returncode + + try: + p = xml.getElementsByTagName('patch')[0] + except IndexError: + raise HistoryError, "not such patch" + + cid = p.getAttribute("hash") + author = p.getAttribute('author') + atime = p.getAttribute('date') + atime = datetime.datetime.strptime(atime, + "%Y%m%d%H%M%S") + msg = p.getElementsByTagName('name')[0] + msg = msg.childNodes[0].data + msg = msg.split('\n')[0].strip() + + commit = { + 'commit': cid.encode('utf8'), + 'author': author.encode('utf8'), + 'committer': author.encode('utf8'), + 'atime': atime, + 'ctime': atime, + 'msg': msg.encode('utf8'), + } + return commit + + # # Main