Summary: A more composable Git interface can be built with a facade that implements standard Clojure interfaces.
In a previous post, I used clj-jgit to interact with a local Git repository. The functions, and general workflow, matched what I would have performed at the command line (git-add followed by git-commit). The workflow made it very easy to get started, and is preferable to using jgit directly, but, to me, it didn’t feel very Clojure-like. It felt like Bash.
While I was using git-add and git-commit, I wanted conj. A Git branch can almost be imagined as a very-persistent strongly-ordered hashmap. Every commit is addressable by a “key”, and has a sequence of commits behind it. I should be able to get commits, map and reduce over them, and conj on a new ones using the built-in clojure functions.
Associative, IFn, IObj, Oh My!
Dig into the Clojure (JVM) internals and you will quickly find a list of common interfaces for everything from metadata to data structure access. I only wanted the behavior of a hashmap, so my implementation list, after much exploration, shrunk to Associative, IFn, IObj, and Object. Associative accounted for most of the functionality, but did not provide map-as-function, metadata handling, or toString.
Proof of Concept
Full Source: https://gist.github.com/Jared314/6418917
Usage
Some points of interest:
- The idea of a staging area disappears because you can now construct a commit, as a hashmap, independently of Git.
- The metadata, and commit information, is queried lazily, and not cached, because the repository could be changed from outside the application.
- I have reused the TreeIterator from a previous post to allow commits without writing to the file system.
There is missing functionality (commit totals, changing branches, and a clean commit data format to name a few), but most of it was outside of my original goal of using conj to add a new commit. There are no technical hurdles to prevent those features in the future, but my application did not call for them. This might make for a nice feature to submit to the clj-jgit project, if I ever fill in the missing features.