Zack Scholl

zack.scholl@gmail.com

Shallow `go get`

 / #golang #tutorial 

Note: this tutorial is deprecated. There now exists modules that replace the need for this hack.

This is a little hack to use shallow clones for new git checkouts with go get. Unfortunately for Gophers, this has been an open issue for three years counting without a workable solution aside from patching the go toolchain yourself. This solution utilizes a git wrapper that determines if a pull/clone is happening and then makes sure it is shallow.

The wrapper is only a few lines of code, here’s the main.go:

 1package main
 2
 3import (
 4    "os"
 5    "os/exec"
 6)
 7
 8func main() {
 9    args := os.Args
10    if len(args) > 1 {
11        if args[1] == "pull" {
12            args = append(args[:2], append([]string{"--depth=1"}, args[2:]...)...)
13        } else if args[1] == "clone" {
14            args = append(args[:2], append([]string{"--depth=1", "--shallow-submodules", "--single-branch"}, args[2:]...)...)
15        }
16    }
17    args[0] = "/usr/bin/git"
18    cmd := exec.Command(args[0])
19    if len(args) > 0 {
20        cmd = exec.Command(args[0], args[1:]...)
21    }
22    cmd.Stdout = os.Stdout
23    cmd.Stderr = os.Stderr
24    err := cmd.Run()
25    if err != nil {
26        os.Exit(1)
27    }
28}

To make it easy, I’ve saved this code as main.go in a repo named git (schollz/git). The repo is named “git” on purpose so that your GOPATH can be prepended to the PATH and then the wrapper can be substituted for the real git (/usr/bin/git). So then, to activate shallow cloning all you have to do is:

$ go get github.com/schollz/git
$ export PATH=$GOPATH/bin:$PATH

which you can add to your .bashrc files if you want it to be permanent. This way, the wrapper will aways be used and the wrapper will force cloning to be shallow.

Benchmarks

Here’s a benchmark showing a 50% reduction in disk usage and thus a 50% reduction in time taken for a go get. You’ll not get that much for smaller repositories, but its not bad.

normal go get

% docker run -it golang:1.10 /bin/bash
root@d9208178f1fa:/go# time go get github.com/juju/juju/...
real    7m35.631s
user    1m40.059s
sys     0m45.436s
root@d9208178f1fa:/go# du -sh .
1.1G

shallow go get

% docker run -it golang:1.10 /bin/bash
root@68135fb64a3e:/go# go get github.com/schollz/git
root@68135fb64a3e:/go# export PATH=$GOPATH/bin:$PATH
root@68135fb64a3e:/go# time go get github.com/juju/juju/...
real    3m0.335s
user    0m29.192s
sys     0m17.253s
root@d9208178f1fa:/go# du -sh .
499M    .

Thanks tscholl2 for the idea.