Go has many good things: simple language, good efficiency for scalability, concurency primitives, garbage collection, etc. However, one thing that I find is awkward, to say the least, is forking repositories and contributing to them.

Pre Go 1.5, the way to handle dependency management was to use third party tools like godep. That can be a whole topic by itself. But since Go 1.6, go comes with its own dependency management via vendoring. It works like magic, magic is bad.

Forking and contributing

Suppose we see a nice library out there and we want to install it.

> go install github.com/them/a
> a -h

Easy. Now, suppose we want to fork this repository to github.com/you/a and you want to modify it. Two things will happen:

  1. the forked repo will appear in $GOPATH/src/github.com/you/a and
  2. The import lines inside the package you cloned will still have reference to the original package. i.e. github.com/them/a

This is problematic for local development. Modifications to a subpackage github.com/you/a/b will not get picked up by the files in github/you/a. Yes, you can do a search and replace to modifying all import occurences of github.com/them/a to github.com/you/a. But this means the substitution needs to be reverted before sending a PR.

One solution

There is a less awkward way of contributing. Instead of using the tool go get or go install, manually clone it into the correct place. If the repo is github/them/a, manually create a folder $GOPATH/src/github.com/them and git clone it there.

git clone github.com/you/a $GOPATH/src/github.com/them/a

The same outcome that be achieved by doing a go get and

git add remote fork <your fork.git>

Subtree

If you deal with third party libraries as much as us, some sane way of managing dependencies can save you a lot of hair pulling. Git subtree is one of them. But that’s for another time.

Vendoring

Remember I said something about vendoring being magical? That is the next source of pain. It is nice to vendor dependencies so that you can ensure your software still works when the dependency breaks compatibility. However, it is not very open to modifications or reuse.

Suppose you have a structure like this

.
└── github.com
    ├── them
    │   ├── a
    │   |   ├── a.go
    │   |   └── vendor
    │   |       └── github.com
    │   |           └── them
    │   |               └── b
    │   |                    └── b.go
    │   └── b
    │       └── b.go
    └── you
        └── c
            └── c.go

Suppose github.com/them/a is an awesome program, and it uses github.com/them/b as a dependency. At the same time, there are exported functions in github.com/them/a that you want to use your awesome application github.com/you/c

So perhaps c.go looks something like

package main

import (
    "github.com/them/a"

    // Which of the following should be used?
    // Choice 1: 
    // "github.com/them/b" // this won't work
    // Choice 2: 
    // "github.com/them/a/vendor/github.com/them/b" // this won't work either
)

func main() {
    a.Add(b.Int(1), b.Int(2))
}

Where a.go looks something like

package a

import (
    "github.com/them/b"
)

func Add(x, y b.Int) {
    ...
}

Now, if you were to do go install github.com/them/a that would work fine because go is magical enough to know to look into the vendor directory to resolve dependencies. It tries the vendored subdirectory before looking in $GOPATH.

However, if you try and build github.com/you/c, neither of the import lines would work.

The first line would not work even after doing a go get because it will complain a type mismatch. It will resolve the type of b.Int in a.go to be of type github.com/them/a/vendor/github.com/them/b.Int instead of github.com/them/b.Int which is what the import says.

The second line won’t work because, just like the internal package, vendored packages cannot be used outside of the package.

Solution?

I do not know of a nice way to solve this vendoring problem. One thing is for sure: for modification of the package, all vendored packages needs to be unvendored. There is really nothing wrong deleting the folder vendor except if you are using git subtrees to maintain state of third party libraries.

└── github.com
    ├── them
    │   ├── a
    │   │   └── a.go
    │   └── b
    │       └── b.go
    └── you
        └── c
            └── c.go

Conclusion

These are some pain points when pulling in third party libraries and trying to modify them. There are steps around it, as demonstrated above. But they are by no means obvious and it can be mysterious to beginners.