Write Your Own Git Subcommands

Steroidtocat by James Kang

Git aliases are nice, right? I mean, just have a quick look at the following two commands, and tell me which one would you prefer to remember:

$ git log --cherry-mark --left-right master...feature-branch
$ git lc master...feature-branch

Easy choice! Now, what if you want to pass any parameters? Git’s got your back:

# Put the following inside your .gitconfig
[alias]
lc = "!fn() { \
git log -\"$1\" --oneline \
--cherry-mark --left-right \
\"$2\";
}; fn"
# Now you can use it like this
$ git lc 10 master...feature-branch

How cool is that?

What if you want to run a more complex task? Let’s say you need to sync up your local branch with both origin/master and origin/best-of-all-features; you’d then probably do something like this:

# The name in parens indicate which branch you're currently in
(best-of-all-features) $ git stash -u; git pull
(best-of-all-features) $ git checkout master; git pull
(master) $ git checkout best-of-all-features
(best-of-all-features) $ git merge master --no-edit
(best-of-all-features) $ git stash pop

Add a couple of parameters and then you’ve messed your .gitconfig up… Big time! So, how to sort this out?

Extending Git

Well, Git allows you to define custom functions and run them as subcommands, pretty much the way aliases work. You can, however, take advantage of your whole shell environment in them, even including any script you’ve exposed in your $PATH. Have you ever heard of the git-flow extensions? They leverage this feature to provide Git with extended capabilities.

Hands-On: Your First Git Subcommand

So, let’s get cracking. First things first: create an executable file for your script and put it inside a new folder in your $HOME:

$ mkdir ~/.gitbin; touch ~/.gitbin/git-custom
$ chmod u+x ~/.gitbin/git-custom

Notice how the file name is prefixed with git-. It’s very important to always name your scripts like that; that’s how Git knows your file is supposed to be run as a subcommand.

Now, add your new folder to the shell’s $PATH:

$ export PATH="$HOME/.gitbin:$PATH"

You might want to add the previous line to your shell’s initialisation file (be it ~/.zshrc,~/.bashrc, or whatever CLI you’re running). Next, open ~/.gitbin/git-custom in your editor, and write the following:

#!/usr/bin/env ruby
message = ARGV[0]
puts message

Save the file, and go to the command line; you could now run git custom ‘Hello world’ and see Hello world in your terminal’s output. Amazing!

Also, have you noticed what language have I used to write the script? Yes, you’re right… Ruby! As long as you have an interpreter available, you can very well use the programming language of your choice, and let your imagination do the rest.

This is a trivial example, of course, as my aim is to teach you how to make the script available for Git to use. However, I’ll leave you a link to my dotfiles, where you can find a git-browse function. What does it do? Well, they say a picture’s worth a thousand words — in this case, a GIF:

Browse to your repository’s remote url using a custom git command

Conclusion

You’ve just seen how easily you can extend Git, and have it run pretty much any task your scripting language lets you write. From improving logging to syncing and rebasing a local branch with its remote peer and master, the options are vast.

I hope you’ve found this useful, and come up with some wicked ideas to take your Git to a whole other level. Have fun!