How to Clean $GOPATH

This is a short article with some useful tips I’ve gathered after quiet some time after I began to program in Go.

One of the core features of Go is the ease in which you can import libraries and use them right away in your programming. However, as a beginner, I’ve tried many libraries over the course of my first programs that were meant for the same purpose, ending up using only some of them.

This resulted in many go get commands which downloaded packages to my $GOPATH/src I don’t use and will never intend to use.

The question is, how can I know what directories are not imported in any of my projects? Luckily, when I created new Go projects, I'd first put them inside my $GOPATH and then I'd link their directory to ~/repos/ which is outside of my $GOPATH.

Besides that, when I came to realise I don’t manage my dependencies smartly at all, I began to take a look at Go modules. This has led me into pulling my Go projects outside of $GOPATH and use go.mod files in them which I initilized with:

go mod init github.com/doronbehar/myproject

Can I delete all of $GOPATH/src?

If you strictly use only Go modules and you never develop with vendor style dependencies, you can completely remove `$GOPATH/src` as all your packages will be used by default from `$GOPATH/pkg/mod`.

From here on, the rest of the cleanning process is pretty straight forward, here are the two commands I ran in order to clean both unused Go modules ($GOPATH/pkg/mod/) and Go packages in $GOPATH/src:

Gathering all used packages

find ~/repos -maxdepth 2 -name go.mod -execdir zsh -c 'go list -f "{{range \$dep := .Deps}}{{printf \"%s\n\" \$dep}}{{end}}" | xargs go list -f "{{if not .Standard}}{{.ImportPath}}{{end}}"' {} \; | sort -u | awk -F / -v OFS=/ '
{
    p = $0
    while(--NF > 1) {
        if ($0 in paths) next
    }
    print p
    paths[p]
}' | sort -u > ~/not-delete.txt

Explanation:

This command is based on what @pmcgrath examplified in his article and I shamelessly used and extended for my own cause. I used find so this command will run in every directory where go.mod is found (every project of mine) and I’ve had to use zsh to wrap @pmcgrath’s command which includes a shell pipe (|).

After I got all of the pacakges imported in all of my projects, I sorted these (sort -u) and filtered them with an awk script stolen from a StackExchange QA. It’s job is to filter directories already contained in previous directories so the list will be more concise.

https://unix.stackexchange.com/a/362578/135796

Finaly, everything is sorted and filtered with only unique values (sort -u) and piped to a file ~/not-delete.txt. This file will be used in the following commands that do 2 different things:

How to know what should be deleted?

As I said before, if you strictly use Go modules in all of your projects and you never use ./vendor style dependencies, you can remove all of your $GOPATH/src. All your imports will use $GOPATH/pkg/mod/ as the base path for the packages. Never the less, I’ll show how to delete all unused modules in GOPATH/pkg/mod:

As most of the dependencies come from GitHub, We can cd to $GOPATH/pkg/mod and run the following:

find github.com -mindepth 2 -maxdepth 2 -type d -not -exec grep -q {} ~/not-delete.txt \; -print -exec rm -r {} \;

For every package from github.com, we check if it matches something in our list of packages we don’t want to delete (~/not-delete.txt) and only if it doesn’t (mind the -not flag find gets!), we remove it with rm -r

I hope this may help for anyone who reads this. It has certainly helped me to note this for my self so I could rehearse this process when I’ll feel like it’s time to clean up unused imports in $GOPATH/pkg/mod.