libgit2 is, in its own words “a portable, pure C implementation of the Git core methods provided as a re-entrant linkable library with a solid API” and git2go is a set of Go bindings that allow us to use libgit2 to interact with git repositories directly from Go programs.

Getting git2go working isn’t massively complicated, but if using it is your fist experience of using a Go library that links against an underlying C library, there may be a few places you could trip up. This post is my attempt to explain how to painlessly get started using git2go on OS X.

Everything that follows has been tested on a MacBook Air running OS X 10.10.5, but it should all work on any OS X machine that has homebrew and the Go toolchain available.

Dynamic linking

We’ll use the program shown below as a quick example of how to get up and running with git2go. Assuming you replace /path/to/a/git/repo, with (you guessed it) the path to a git repository, it will check if the repository is ‘bare’ or not. Not a great deal of use, but it’ll do to confirm things are working as expected.

package main

import (
    "fmt"
    "github.com/libgit2/git2go"
    "os"
)

func main() {
    repo, err := git.OpenRepository("/path/to/a/git/repo")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    if repo.IsBare() {
        fmt.Println("Yep, it's bare.")
    } else {
        fmt.Println("Nope. Not a bare repo.")
    }
}

If you try and go get the dependencies for the example program, you’ll probably get an error saying something like this.

# pkg-config --cflags libgit2
Package libgit2 was not found in the pkg-config search path.
Perhaps you should add the directory containing 'libgit2.pc' to the PKG_CONFIG_PATH environment variable
No package 'libgit2' found
pkg-config: exit status 1

This is just telling us that we don’t have libgit2 available. The easiest way to solve this on OS X is to install libgit2 using homebrew. Simply run brew install libgit2.

You can now run go get and go install from our example program’s directory and you’ll find a working binary that can tell you if a git repository is bare or not, as long as you have libgit2 installed.

If you uninstall libgit2 or run this binary on a Mac that doesn’t have it installed, you’ll see an error like this.

dyld: Library not loaded: /usr/local/lib/libgit2.23.dylib
  Referenced from: /Users/Peter/code/go/bin/git-example
  Reason: image not found
Trace/BPT trap: 5

To get around this and allow compiling a binary that will run on a Mac that doesn’t have libgit2 installed, we need to statically link libgit2.

Static linking

Up until now we’ve used the master branch version of git2go, this tracks the latest version of libgit2 (currently v0.23). In order to statically link against libgit2, we have to use the next branch version of git2go, this tracks libgit2’s master branch and not a released version.

We’re going to need cmake installed so that git2go can build its vendored version of libgit2 to link against. If you don’t already have cmake installed, it’s available through homebrew with a simple: brew install cmake.

Assuming you’ve been following along this far, you’ll have the git2go source code installed in $GOPATH/src/github.com/libgit2/git2go (if not, run go get -d github.com/libgit2/git2go now). Change into this directory and run these commands.

$ git checkout next
$ git submodule update --init
$ make install

This will trigger a lot of output as libgit2 and git2go are built. Once the build is finished, we have a version of git2go available that no longer requires the machine that will be running our binary to have libgit2 installed.

To test this out, you can uninstall libgit2 by running brew uninstall libgit2 and then try to build and run our example program again. All being well, it works just like before, but now we don’t need our users to install anything except our program’s binary.