| @ -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. | |||