| @ -0,0 +1,145 @@ | |||||
| --- | |||||
| title: Managing Go dependencies with git-subtree | |||||
| author: Brett Langdon | |||||
| date: 2016-02-03 | |||||
| template: article.jade | |||||
| --- | |||||
| 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](https://golang.org/) | |||||
| projects. I think I have finally found a work flow that I really like that uses | |||||
| [git-subtree](http://git.kernel.org/cgit/git/git.git/plain/contrib/subtree/git-subtree.txt). | |||||
| 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. | |||||
| ### Vendor dependencies | |||||
| 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 `GO15VENDOREXPERIMENT="1"` flag. | |||||
| ### 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 `package.json` or `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](https://developer.atlassian.com/blog/2015/05/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 `.git` directory). | |||||
| 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. | |||||
| As well, `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](https://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 `github.com/miekg/dns` at `master` to `vendor/github.com/miekg/dns`. | |||||
| And that is it, `git-subtree` will have created two commits for you, one for the squash of `github.com/miekg/dns` | |||||
| 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 `master` from `github.com/miekg/dns` (assuming it has changed) | |||||
| and create two commits for you. | |||||
| ### Using tags/branches/commits | |||||
| `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](ttps://developer.atlassian.com/blog/2015/05/the-power-of-git-subtree/)) | |||||
| suggests adding in `git` aliases to make using `git-subtree` easier. | |||||
| I decided to take this one step further and write a `git` command, [git-vendor](https://github.com/brettlangdon/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 | |||||
| forge@v0.1.5: | |||||
| 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 | |||||
| forge@v0.1.7: | |||||
| name: forge | |||||
| dir: vendor/github.com/brettlangdon/forge | |||||
| repo: https://github.com/brettlangdon/forge | |||||
| ref: v0.1.7 | |||||
| commit: 0b2bf8e484ce01c15b87bbb170b0a18f25b446d9 | |||||
| ``` | |||||
| ## Why not... | |||||
| ### 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](https://github.com/tools/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. | |||||
| ### git-submodule | |||||
| I have been asked why not `git-submodule`, and I think anyone that has had to work | |||||
| with `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. | |||||
| ### Something else? | |||||
| 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. | |||||