changeset 117:b07cdd68de70

Added partial Git support: * New Git source control file, with only support for querying state. * Auto-detect repo type during initialization.
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 19 Nov 2013 13:14:05 -0800
parents b7950fa699f7
children 7c8543878b47
files wikked/resources/defaults.cfg wikked/scm.py wikked/wiki.py
diffstat 3 files changed, 95 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/wikked/resources/defaults.cfg	Mon Nov 18 17:16:24 2013 -0800
+++ b/wikked/resources/defaults.cfg	Tue Nov 19 13:14:05 2013 -0800
@@ -1,4 +1,3 @@
 [wiki]
-scm=hg
 auto_update=False
 default_extension=md
--- a/wikked/scm.py	Mon Nov 18 17:16:24 2013 -0800
+++ b/wikked/scm.py	Tue Nov 19 13:14:05 2013 -0800
@@ -6,6 +6,12 @@
 import tempfile
 import subprocess
 
+try:
+    import pygit2
+    SUPPORTS_GIT = True
+except ImportError:
+    SUPPORTS_GIT = False
+
 
 STATE_COMMITTED = 0
 STATE_MODIFIED = 1
@@ -88,7 +94,7 @@
         if not os.path.isfile(ignore_path):
             self.logger.info("Creating `.hgignore` file.")
             with open(ignore_path, 'w') as f:
-                f.write('.cache')
+                f.write('.wiki')
             self._run('add', ignore_path)
             self._run('commit', ignore_path, '-m', 'Created .hgignore.')
 
@@ -307,3 +313,75 @@
         else:
             self.client.revert(all=True, clean=True)
 
+
+class GitBaseSourceControl(SourceControl):
+    def __init__(self, root, logger=None):
+        SourceControl.__init__(self, logger)
+        self.root = root
+
+    def initRepo(self):
+        # Make a Git repo if there's none.
+        if not os.path.isdir(os.path.join(self.root, '.git')):
+            self.logger.info("Creating Git repository at: " + self.root)
+            self._initRepo(self.root)
+
+        # Create a `.gitignore` file there's none.
+        ignore_path = os.path.join(self.root, '.gitignore')
+        if not os.path.isfile(ignore_path):
+            self.logger.info("Creating `.gitignore` file.")
+            with open(ignore_path, 'w') as f:
+                f.write('.wiki')
+            self._add(ignore_path)
+            self._commit('Created .gitignore.', [ignore_path])
+
+    def getSpecialFilenames(self):
+        specials = ['.git', '.gitignore']
+        return [os.path.join(self.root, d) for d in specials]
+
+    def getState(self, path):
+        return self._status(path)
+
+    def _run(self, cmd, *args, **kwargs):
+        exe = [self.git]
+        if 'norepo' not in kwargs or not kwargs['norepo']:
+            exe.append('--git-dir="%s"' % self.root)
+        exe.append(cmd)
+        exe += args
+        self.logger.debug("Running Git: " + str(exe))
+        return subprocess.check_output(exe)
+
+
+class GitLibSourceControl(GitBaseSourceControl):
+    def __init__(self, root, logger=None):
+        if not SUPPORTS_GIT:
+            raise Exception("Can't support Git because pygit2 is not available.")
+        GitBaseSourceControl.__init__(self, root, logger)
+
+    def initRepo(self):
+        GitBaseSourceControl.initRepo(self)
+        self.repo = pygit2.Repository(self.root)
+
+    def _initRepo(self, path):
+        pygit2.init_repository(path, False)
+
+    def _add(self, paths):
+        pass
+
+    def _commit(self, message, paths):
+        pass
+
+    def _status(self, path):
+        flags = self.repo.status_file(self._getRepoPath(path))
+        if flags == pygit2.GIT_STATUS_CURRENT:
+            return STATE_COMMITTED
+        if (flags & pygit2.GIT_STATUS_WT_MODIFIED or
+                flags & pygit2.GIT_STATUS_INDEX_MODIFIED):
+            return STATE_MODIFIED
+        if (flags & pygit2.GIT_STATUS_WT_NEW or
+                flags & pygit2.GIT_STATUS_INDEX_NEW):
+            return STATE_NEW
+        raise Exception("Unsupported status flag combination: %s" % flags)
+
+    def _getRepoPath(self, path):
+        return os.path.relpath(path, self.root).replace('\\', '/')
+
--- a/wikked/wiki.py	Mon Nov 18 17:16:24 2013 -0800
+++ b/wikked/wiki.py	Tue Nov 19 13:14:05 2013 -0800
@@ -4,11 +4,11 @@
 import logging
 import itertools
 import importlib
-from ConfigParser import SafeConfigParser
+from ConfigParser import SafeConfigParser, NoOptionError
 from page import DatabasePage, FileSystemPage
 from fs import FileSystem
 from db import SQLDatabase
-from scm import MercurialCommandServerSourceControl
+from scm import MercurialCommandServerSourceControl, GitLibSourceControl
 from indexer import WhooshWikiIndex
 from auth import UserManager
 
@@ -54,9 +54,22 @@
         return SQLDatabase(self.db_path, logger=self.logger_factory())
 
     def scm_factory(self, config):
-        scm_type = config.get('wiki', 'scm')
+        try:
+            scm_type = config.get('wiki', 'scm')
+        except NoOptionError:
+            # Auto-detect
+            if os.path.isdir(os.path.join(self.root, '.hg')):
+                scm_type = 'hg'
+            elif os.path.isdir(os.path.join(self.root, '.git')):
+                scm_type = 'git'
+            else:
+                # Default to Mercurial. Yes. I just decided that myself.
+                scm_type = 'hg'
+
         if scm_type == 'hg':
             return MercurialCommandServerSourceControl(self.root, logger=self.logger_factory())
+        elif scm_type == 'git':
+            return GitLibSourceControl(self.root, logger=self.logger_factory())
         else:
             raise InitializationError("No such source control: " + scm_type)