A school friend, namely p4bl0, mentioned the idea of maintaining blog posts with git and a set of hooks which would produce the blog html from the contents of the repo. I loved the idea, but thought I could push it a little further : a blog engine which would use no other storage than git, with the post subject and contents being the commit message subject and contents. A post-commit or post-receive hook then produces the html. As simple as that !
You can find the source in BloGit git repo, and see an example at BloGit example. To use the source, you first have to pack it (using the pack script), which will merge the raw_post and raw_produce, producing a single post script (which I also included at the end of this post), which you can simply put in an empty directory and run it. It will unpack the other script (produce), initialize the git repo, and set the hooks. It’ll then prompt you for your post title and then open an editor for you to set your post contents. Save the file, and you’re done with your first post : check the index.html file which has been produced in the same directory. You can write your own stylesheet in the blogit-style.css file. Further posts can be done with the same post script.
Yet, the best way is probably just to use the usual git workflow. To initialize the repo and all, run post --unpack, and to post post --raw or git commit --allow-empty (when using git commit, leave a blank line between the subject line and the rest of the post). You can also amend existing commits (using git commit --amend), use the GIT_AUTHOR_* environment variables to change the author, and so on. Since merge commits are skipped by the html generator, it should work just great for multi author blogging !
PS : I know this is JUST a git log pretty printer, and that the whole thing is pretty much trivial. I also know that using versionned files to store the posts would allow a lot of extra bonuses (such as automatically adding “Updated on …” mentions based on the commit log of each single file). I just thought the idea was fun
There are probably a lot of things to improve, or a lot of smart git features to use there that I overlooked. Feel free to leave a line
#!/usr/bin/env python """ bloggit Author : Guillaume "iXce" Seguin Email : guillaume@segu.in (or guillaume.seguin@ens.fr) Copyright (C) 2010 Guillaume Seguin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """ import subprocess, os, sys import base64, bz2 CONTENTS_FILE = "POST_CONTENTS" repo = os.path.dirname (os.path.abspath (__file__)) os.chdir (repo) if not os.path.exists (os.path.join (repo, ".git")): subprocess.call (["git", "init"]) produce_path = os.path.join (repo, "produce") produce_script = bz2.decompress (base64.b64decode (""" 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 6MrbOTHwIixlYyW5xO7y/+LuSKcKEgD+5O2A== """.strip ().replace ("\n", ""))) if not os.path.exists (produce_path): open (produce_path, "w").write (produce_script) os.chmod (produce_path, 0755) for hook in ("post-receive", "post-commit"): hook_path = os.path.join (repo, ".git", "hooks", hook) if not os.path.exists (hook_path) \ or os.path.realpath (hook_path) != produce_path: if os.path.lexists (hook_path): os.remove (hook_path) os.symlink (produce_path, hook_path) if "--unpack" in sys.argv: print "Unpack only" raise SystemExit if "--raw" in sys.argv: p = subprocess.Popen (["git", "commit", "--allow-empty"]) p.communicate () else: title = raw_input ("Post title ? ") editor = "vi" if "VISUAL" in os.environ: editor = os.environ["VISUAL"] elif "EDITOR" in os.environ: editor = os.environ["EDITOR"] contents_path = os.path.join (repo, CONTENTS_FILE) if os.path.exists (contents_path): os.remove (contents_path) ret = subprocess.call ([editor, contents_path]) if ret == 0 and os.path.exists (contents_path): contents = open (contents_path).read () os.remove (contents_path) else: print "A problem occured while editing the contents file" raise SystemExit p = subprocess.Popen (["git", "commit", "--allow-empty", "-F", "-"], stdin = subprocess.PIPE) p.communicate ("%s\n\n%s" % (title, contents)) if p.returncode == 0: print "New post successfully added" else: print "Couldn't add new post" sys.exit (1)
December 14th, 2010 at 3:37 am