Recently I have decided to make the switch to using
git-subtree for managing dependencies of my Go projects.
For a while now I have been searching for a good way to manage dependencies for my Go projects. I think I have finally found a work flow that I really like that uses git-subtree.
When I began investigating different ways to manage dependencies I had a few small goals or concepts I wanted to follow.
Keep it simple
I have always been drawn to the simplicity of Go and the tools that surround it. I didn’t want to add a lot of overhead or complexity into my work flow when programming in Go.
I decided right away that I wanted to vendor my dependencies, that is, where all of my dependencies
live under a top level
vendor/ directory in each repository.
This also means that I wanted to use the
Maintain the full source code of each dependency in each repository
The idea here is that each project will maintain the source code for each of its dependencies
instead of having a dependency manifest file, like
Godeps.json, to manage the dependencies.
This was more of an acceptance than a decision. It wasn’t a hard requirement that each repository maintains the full source code for each of its dependencies, but I was willing to accept that as a by product of a good work flow.
In come git-subtree
When researching methods of managing dependencies with
git, I came across a great article
from Atlassian, The power of Git subtree.
Which outlined how to use
git-subtree for managing repository dependencies… exactly what I was looking for!
The main idea with
git-subtree is that it is able to fetch a full repository and place
it inside of your repository. However, it differs from
git-submodule because it does not
create a link/reference to a remote repository, instead it will fetch all the files from that
remote repository and place them under a directory in your repository and then treats them as
though they are part of your repository (there is no additional
If you pair
git-subtree with its
--squash option, it will squash the remote repository
down to a single commit before pulling it into your repository.
git-subtree has ability to issue a
pull to update a child repository.
Lets just take a look at how using
git-subtree would work.
Adding a new dependency
We want to add a new dependency, github.com/miekg/dns to our project.
git subtree add --prefix vendor/github.com/miekg/dns https://github.com/miekg/dns.git master --squash
This command will pull in the full repository for
And that is it,
git-subtree will have created two commits for you, one for the squash of
and another for adding it as a child repository.
Updating an existing dependency
If you want to then update
github.com/miekg/dns you can just run the following:
git subtree pull --prefix vendor/github.com/miekg/dns https://github.com/miekg/dns.git master --squash
This command will again pull down the latest version of
github.com/miekg/dns (assuming it has changed)
and create two commits for you.
git-subtree also works with tags, branches, or commit hashes.
Say we want to pull in a specific version of
github.com/brettlangdon/forge which uses tags to manage versions.
git subtree add --prefix vendor/github.com/brettlangdon/forge https://github.com/brettlangdon/forge.git v0.1.5 --squash
And then, if we want to update to a later version,
v0.1.7, we can just run the following:
git subtree pull --prefix vendor/github.com/brettlangdon/forge https://github.com/brettlangdon/forge.git v0.1.7 --squash
Making it all easier
I really like using
git-subtree, a lot, but the syntax is a little cumbersome.
The previous article I mentioned from Atlassian (here)
suggests adding in
git aliases to make using
I decided to take this one step further and write a
git command, git-vendor
to help manage subtree dependencies.
I won’t go into much details here since it is outlined in the repository as well as at https://brettlangdon.github.io/git-vendor/,
but the project’s goal was to make working with
git-subtree easier for managing Go dependencies.
Mainly, to be able to add subtrees and give them a name, to be able to list all current subtrees,
and to be able to update a subtree by name rather than repo + prefix path.
Here is a quick preview:
$ git vendor add forge https://github.com/brettlangdon/forge v0.1.5 $ git vendor list email@example.com: name: forge dir: vendor/github.com/brettlangdon/forge repo: https://github.com/brettlangdon/forge ref: v0.1.5 commit: 4c620b835a2617f3af91474875fc7dc84a7ea820 $ git vendor update forge v0.1.7 $ git vendor list firstname.lastname@example.org: name: forge dir: vendor/github.com/brettlangdon/forge repo: https://github.com/brettlangdon/forge ref: v0.1.7 commit: 0b2bf8e484ce01c15b87bbb170b0a18f25b446d9
Godep/<package manager here>
I decided early on that I did not want to “deal” with a package manager unless I had to. This is not to say that there is anything wrong with godep or any of the other currently available package managers out there, I just wanted to keep the work flow simple and as close to what Go supports with respect to vendored dependencies as possible.
I have been asked why not
git-submodule, and I think anyone that has had to work
git-submodule will agree that it isn’t really the best option out there.
It isn’t as though it cannot get the job done, but the extra work flow needed
when working with them is a bit of a pain. Mostly when working on a project with
multiple contributors, or with contributors who are either not aware that the project
is using submodules or who has never worked with them before.
This isn’t the end of my search, I will always be keeping a look out for new and different ways to manage my dependencies. However, this is by far my favorite as of yet. If anyone has any suggestions, please feel free to leave a comment.