Carlos Reyes
January 05, 2026 • 4 min read

Doing a git helper with Clojure

So I’m learning Clojure, and one thing I noticed I need was a git helper, whenever I want to push some code quick, I spend some time writing the same git add, git commit, git push, so what better than writing a simple tool to make that process faster, and even better, with Clojure.

Initialize project

So for this, I’m using Leiningen, which is a great Clojure tool to handle your projects and do things like initialize projects, add dependencies, compile your code, and more.

So we will start with the following command, to initialize a Clojure project.

lein new app git-initializer 

Which will give us a structure like the following:

.
├── LICENSE
├── project.clj
├── README.md
├── src
├── target
└── test

For this structure, we have project.clj which is where the dependencies and main information about the project are, target is where our compiled code go, and src is where our coding files are, so inside src we have our file src, which we’ll use for our project.

Okay, now, we will start by defining our namespace, and importing a java library, yes, a java library, because as clojure compiles to java bytecode, clojure allow us to use java libraries.

(ns git-initializer.old-version
  (:gen-class)
  (:require [clojure.java.shell :as sh]))

For this git tool, I will need to interact with the system to use git commands, so I’m importing the java.shell library, and using :as sh to instead of having to write the full name, just write sh

Now, for this tool I just need to execute 3 basic commands, add, commit and add. this may not be the best practice, but for this example, I’m just defining 3 variables containing the git command to use as a string.

(def add ["add" "."])
(defn commit [commit-msg] ["commit" "-m" commit-msg])
(def push ["push"])

Our first function, add just holds a vector with our separate commands, push does the same, but commit is a bit different.

As you can see, insead of using def, we’re using defn, which is the keyword to create a function, so in this function we’re saying that commit, will have one parameter, commit-msg, and then it will return the git commands, plus the commit msg, so it would be like git commit -m 'commit msg'

Next is the main function to process the commands, we start by defining the function, and saying that it will have one parameter.

(defn execute-process [commit-msg]

And this function is going to start by defining a vector called commands, which will hold our 3 git commands, and as you can see, when calling the commit function, we put it inside parenthesis because is a function call.

(let [commands [add (commit commit-msg) push]]

Now, we tell clojure ‘You have a vector of commands, so process it, and do x thing for each value inside the vector’.

(doseq [args commands]

So this ‘x thing’ we do, is create a vector called result, which will hold the value of the execution of apply sh/sh "git" args, here we’re using the java.shell library we defined before, and we are calling the git commands.

(let [result (apply sh/sh "git" args)]

Finally, each time we call apply, it saves the returned value into result, and so with that value, we use it to print to the terminal the values we got after executing each command.

(println (:out result))
(println (:err result))))))

Now we just have to call the function, so we create a private function called main, which will take user input, and do an if, to check is the user arguments are empty or not, if they are, then we just get print to specift a commy msg, but if args contain a value, we execute our function with the args as parameter.

(defn -main
  [& args]
  (if (empty? args)
    (println "Error, you didn't specify a commit msg")
    (execute-process (first args))))

Finally, the complete code looks like this.

(ns git-initializer.core
  (:gen-class)
  (:require [clojure.java.shell :as sh]))

(def add ["add" "."])
(defn commit [commit-msg] ["commit" "-m" commit-msg])
(def push ["push"])

(defn execute-process [commit-msg]
  (let [commands [add (commit commit-msg) push]]
    (doseq [args commands]
      (let [result (apply sh/sh "git" args)]
        (println (:out result))
        (println (:err result))))))

(defn -main
  [& args]
  (if (empty? args)
    (println "Error, you didn't specify a commit msg")
    (execute-process (first args))))