Skip to content

Instantly share code, notes, and snippets.



Last active Dec 22, 2015
What would you like to do?
Improving the Clojure-Git Interface with a Nice Facade
(ns gitter.core
(:require [clj-jgit.porcelain :as c]
[clj-jgit.querying :as q]
[clj-jgit.internal :as i]
[clojure.string :as string]
[gitter.clj-jgit.porcelain :as helpers])
(:import [clojure.lang ISeq Associative IFn MapEntry IObj]
[org.eclipse.jgit.api Git]
[org.eclipse.jgit.revwalk RevWalk]))
(defn gen-metadata [^Git repo]
(let [path (-> repo .getRepository .getDirectory .getParent)
branch (c/git-branch-current repo)
branch-names (map #(string/replace (.getName %) #"^refs/heads/" "") (c/git-branch-list repo))
attached (c/git-branch-attached? repo)]
{::path path
::branch branch
::branches branch-names
::attached-head attached}))
(deftype Repository [^Git repo ^RevWalk walker metadata]
(count [this] -1) ;; TODO: Not yet
(seq [this] (seq walker))
(cons [this o]
;; TODO: if walker first is not the current HEAD, create branch from current first
(helpers/git-add-commit repo o)
(Repository. repo (i/new-rev-walk repo) metadata))
(empty [_] nil) ;; TODO: Not applicable or branch?
(equiv [_ o]
(and (instance? Repository o)
(= repo (.repo o))))
(containsKey [_ key] (not (nil? (i/resolve-object key repo))))
(entryAt [this k] (MapEntry. k (.valAt this k)))
(assoc [this _ _] this) ;; Not applicable
(valAt [this key] (.valAt this key nil))
(valAt [this key notFound] (if (.containsKey this key)
(q/find-rev-commit repo walker key)
(invoke [this arg1] (.valAt this arg1 nil))
(invoke [this arg1 arg2] (.valAt this arg1 arg2))
(withMeta [_ meta] (Repository. repo walker (merge metadata meta)))
(meta [_] (merge (gen-metadata repo) metadata))
(toString [this] (let [m (.meta this)] (str "Path: " (::path m) " Branch: " (::branch m)))))
(defn get-repo [path]
(c/with-repo path (Repository. repo rev-walk nil)))
(ns gitter.core-test
(:require [clojure.test :refer :all]
[gitter.core :refer :all]
[gitter.clj-jgit.porcelain :refer :all])
(:import [java.util Date]))
(def test-repo-path "/Users/user1/Desktop/tester1")
(deftest read-repo-test
(let [x (get-repo test-repo-path)]
(testing "Reading from the repository"
;; Checking for a commit by id
(is (contains? x "44cf78fa233d4ef44314099762ab77543471720a"))
(is (contains? x "HEAD"))
;; Fetching a commit
(is (= (get x "44cf78fa233d4ef443")
(x "44cf78fa233d4ef443")))
(is (= (get x "HEAD")
(x "HEAD")))
;; Fetching a commit with a notFound parameter
(is (= :fail
(get x "" :fail)
(x "" :fail)))
;; The repository has the following default metadata
(is (= {:gitter.core/path test-repo-path, :gitter.core/branches '("master"), :gitter.core/branch "master" :gitter.core/attached-head true}
(meta x)))
;; Assoc does nothing, and returns the repository
(is (= x
(assoc x "" 1)))
;;(seq? x) ;; TODO:
;;(first x) ;; TODO:
(deftest write-repo-test
(testing "Writing to the repository"
(let [x (get-repo test-repo-path)
c {:message "test commit"
:author {:name "guy 1234" :email ""}
:changes {"stuff.txt" (str "stuff contents " (Date.))
"stuff4.txt" (str "stuff4 contents " (Date.))}}]
(is (= x
(conj x c))))))
(ns gitter.clj-jgit.porcelain
(:require [clj-jgit.util :as util]
[clj-jgit.internal :refer :all]
[clj-jgit.porcelain :as c])
(:import [org.eclipse.jgit.api Git StatusCommand Status StashCreateCommand StashApplyCommand]
[org.eclipse.jgit.lib ObjectId]
[gitter TreeIterator]))
(defn git-create-stash [^Git repo]
(-> repo
(defn git-apply-stash [^Git repo ^String key]
(-> repo
(.setStashRef key)
(defn git-list-stash [^Git repo]
(-> repo
(defn git-drop-stash [^Git repo ^String key]
(let [stashes (git-list-stash repo)
target (first (filter #(= key (second %))
(map-indexed #(vector %1 (.getName %2)) stashes)))] ;; TODO: cleanup
(when-not (nil? target)
(-> repo
(.setStashRef (first target))
(defn git-pop-stash [^Git repo ^String key]
(git-apply-stash repo key)
(git-drop-stash repo key))
(defn head? [^Git repo k]
(let [r (.getRepository repo)
head (.getRef r "HEAD")]
(and (not (nil? head))
(not (nil? (.getObjectId head)))
(= head
(.getRef r k)))))
;; TODO: There has to be a better way!
(defn with-preserved-staging [repo f]
(let [has-staged-changes (not-every? empty? (vals (c/git-status repo :added :changed :removed)))
stashid (if has-staged-changes (ObjectId/toString (.getId (git-create-stash repo))))
result (f)]
(when has-staged-changes (git-pop-stash repo stashid))
(defn git-add-commit [repo {:keys [message author committer changes]
:or {message "" author {:name "" :email ""} committer nil changes {}}}]
(let [prefixed-author {:author-name (:name author) :author-email (:email author)}
c (merge author committer)
prefixed-committer {:committer-name (:name c) :committer-email (:email c)}]
(with-preserved-staging repo
#(doto repo
(c/git-add "." false (TreeIterator. (.getRepository repo) changes))
(c/git-commit message prefixed-author prefixed-committer)))))
(defproject gitter "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url ""
:license {:name "Eclipse Public License"
:url ""}
:dependencies [[org.clojure/clojure "1.5.1"]
[clj-jgit "0.3.9"]]
:aot [gitter.TreeIterator])
(ns gitter.TreeIterator
(:import [org.eclipse.jgit.lib Repository FileMode]
[org.eclipse.jgit.treewalk WorkingTreeIterator WorkingTreeIterator$Entry WorkingTreeOptions]
[ ByteArrayInputStream])
(:gen-class :extends org.eclipse.jgit.treewalk.WorkingTreeIterator
:init init2
:post-init postinit
:state state
:constructors {[org.eclipse.jgit.lib.Repository Object] [org.eclipse.jgit.treewalk.WorkingTreeOptions]
[org.eclipse.jgit.lib.Repository Object org.eclipse.jgit.treewalk.WorkingTreeIterator] [org.eclipse.jgit.treewalk.WorkingTreeIterator]}))
(gen-interface :name gittree.IHasValue
:methods [[getValue [] Object]])
(defn toEntry
([[k v]] (toEntry k v))
([k v]
(let [node-name (if (keyword? k) (name k) (str k))
node-mode (if (coll? v) FileMode/TREE FileMode/REGULAR_FILE)
node-modified 0
node-value (if (string? v) (.getBytes v) (byte-array 0))
node-length (count node-value)]
(proxy [WorkingTreeIterator$Entry gittree.IHasValue] []
(getMode [] node-mode)
(getName [] node-name)
(getLength [] node-length)
(getLastModified [] node-modified)
(openInputStream [] (ByteArrayInputStream. node-value))
(getValue [] v)))))
(defn -init2
([^Repository repo data]
(let [options (-> repo (.getConfig) (.get WorkingTreeOptions/KEY))]
[[options] {:repo repo}]))
([^Repository repo data ^WorkingTreeIterator itr]
[[itr] {:repo repo}]))
(defn -postinit
([this repo data] (-postinit this repo data nil))
([this repo data _]
(let [entries (if (map? data) (map toEntry data) (map-indexed toEntry data))]
(.initRootIterator this repo)
(.init this (into-array WorkingTreeIterator$Entry entries)))))
(defn -createSubtreeIterator [this reader idBuffer]
(let [repo (:repo (.state this))]
(gitter.TreeIterator. repo (.getValue (.current this)) this)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.