From 151f8ec9c9cb3a68fa9221bf798590a9b25db91c Mon Sep 17 00:00:00 2001 From: brettlangdon Date: Wed, 3 Feb 2016 19:11:07 -0500 Subject: [PATCH] Add article, Managing Go dependencies with git-subtree Resolves #3 --- .../index.md | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 contents/writing/about/managing-go-dependencies-with-git-subtree/index.md diff --git a/contents/writing/about/managing-go-dependencies-with-git-subtree/index.md b/contents/writing/about/managing-go-dependencies-with-git-subtree/index.md new file mode 100644 index 0000000..89a7328 --- /dev/null +++ b/contents/writing/about/managing-go-dependencies-with-git-subtree/index.md @@ -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.