Forking Golang repositories on GitHub and managing the import path

Problem: there’s an awesome Golang project on GitHub which you want to fork. You want to develop & collaborate on that fork, but the golang import path, in your source code, still references the original path, breaking everything.

A couple solutions offered below. First, though, let’s get some names.

A sample case, the problem at hand

There’s an awesome tool on http://github.com/awsome-org/tool. You successfully fork it onto http://github.com/awesome-you/tool.

You want to collaborate on http://github.com/awesome-you/tool; you wish to pull, commit & push. Maybe you want to send pull requests to the origin.

The following is commonly found throughout .go files in the repository:

import (
    "github.com/awesome-org/tool/config"
    "github.com/awesome-org/tool/driver"
    "github.com/awesome-org/tool/net"
    "github.com/awesome-org/tool/util"
)

If you:

go get http://github.com/awesome-you/tool

golang creates your $GOPATH/src/github.com/awesome-you/tool/, which is awesome. However, as you resolve dependencies via

cd $GOPATH/src/github.com/awesome-you/tool/ ; go get ./...

golang digs into the source code, finds references to github.com/awesome-org/tool/configgithub.com/awesome-org/tool/driver etc, and fetches those from http://github.com/awsome-org/tool and onto $GOPATH/src/github.com/awesome-org/tool/, which is not awesome. You actually have two copies of the code, one from your fork, one from the origin, and your own fork will be largely ignored as it mostly points back to the origin.

A bad solution

The dirty, bad solution would be for you to go over the source code and replace “github.com/awesome-org/tool” entries with “github.com/awesome-you/tool”. It is bad for two reasons:

  • You will not be able to further pull changes from upstream
  • You will not be able to pull-request and push your own changes upstream

When I say “You will not be able” I mean “in a reasonable, developer-friendly manner”. The code will be incompatible with upstream and you have effectively detached your code. You will need to keep editing and re-editing those entries anytime you wish to pull/push upstream.

Solution #1: add remote

Described in GitHub and Go: forking, pull requests, and go-getting, follow these procedures:

go get http://github.com/awesome-org/tool
git remote add awesome-you-fork http://github.com/awesome-you/tool

You’re adding your repository as remote. You will from now on need to explicitly:

git pull --rebase awesome-you-fork
git push awesome-you-fork

If you forget to add the “awesome-you-fork” argument, you are pulling and pushing from upstream.

Solution #2: cheat “go get”, DIY

The problem began with the go get command, which copied the URI path onto $GOPATH/src. However go get implicitly issues a git clone, and we can do the same ourselves. We will dirty our hands just once, and then benefit from an ambiguous-less environment.

We will now create our git repository in the name of awesome-org but with the contents of awesome-you:

cd $GOPATH
mkdir -p {src,bin,pkg}
mkdir -p src/github.com/awesome-org/
cd src/github.com/awesome-org/
git clone git@github.com:awesome-you/tool.git # OR: git clone https://github.com/awesome-you/tool.git
cd tool/
go get ./...

The mkdir -p {src,bin,pkg} is there just in case you do not have anything setup in your $GOPATH. We then create the repository path under the name of awesome-org, but once inside clone from awesome-you.

The source code’s import path fits your directory layout now, but as you push/pull you are only speaking to your own awesome-you repository.

4 thoughts on “Forking Golang repositories on GitHub and managing the import path

  1. Thanks for the useful blog entry. I am learning Go and found an improvement in a depedency, when I ran into this exact issue.

    I had initially thought the fact that you can simply do an import on a github project like this is a really cool feature in golang…

    However, after running into this issue, and then realising there is no “official” or “good” solution for this, I kind of think it’s a bad design decision. My 1 line “fix” I wanted to test now becomes a lot bigger hassle. Especially when I am building and running my go-app within a docker. Simply going in and changing the git clone and similar solutions will be very “unclean” and cumbersome.

  2. Any reason you didn’t submit this as an Issue to the main go repository? I would go so far as to say this is bug with security and social implications. The additional friction this adds to the fork/PR/merge workflow means that the amount of collaboration from open source and other contributors is greatly limited for Go relative to just about every other language.

  3. You say “The source code’s import path fits your directory layout now, but as you push/pull you are only speaking to your own awesome-you repository.”

    But what about when you “go get ./…”? Or when the compiler tries to resolve dependencies? Are you then speaking to the original repository? Seems like then it might pull from the original when that is not what you want.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.