I’m a huge fan of gitflow
. It’s made my life much easier. However, I’ve noticed recently that, as the codebase grows, features can become disorganized or unwieldy. gitflow-avh
solves this problem incredibly well by allowing features to be based off any branch.
Note
This is not a gitflow
overview. Check out the original branching model and the gitflow
repo for more information on those.
Code
You can view the code related to this post in its repo. To see how I generated everything, check out the scripts
directory.
Problem
After reading the intro, you might be thinking that my features are too large. On the contrary, I’m a pedant with a penchant for pigeonholing things. Let’s say you want to add feature foo
:
foo-dependency
must be built for foo
foo-dependency
must be testedfoo
must be builtfoo
must be tested
Assume foo-dependency
is only used by foo
.
Feature Per Item
Your flow could look like this:
$ git flow feature start add-foo-dependency $ touch src/foo-dependency.ext $ git add src/foo-dependency.ext $ git commit -m 'Create foo-dependency' $ git flow feature finish --no-ff add-foo-dependency $ git flow feature start test-foo-dependency $ touch tests/foo-dependency.ext $ git add tests/foo-dependency.ext $ git commit -m 'Test foo-dependency' $ git flow feature finish --no-ff test-foo-dependency $ git flow feature start add-foo $ touch src/foo.ext $ git add src/foo.ext $ git commit -m 'Create foo' $ git flow feature finish --no-ff add-foo $ git flow feature start test-foo $ touch tests/foo.ext $ git add tests/foo.ext $ git commit -m 'Test foo' $ git flow feature finish --no-ff test-foo $ git log --graph --all --topo-order --decorate --oneline --boundary --color=always
|
* 03d94f9 (HEAD -> dev) Merge branch 'feature/test-foo' into dev
|\
| * 842ac3a Test foo
|/
* 2a84d06 Merge branch 'feature/add-foo' into dev
|\
| * 052adc7 Create foo
|/
* a9b2d78 Merge branch 'feature/test-foo-dependency' into dev
|\
| * 7cdf67e Test foo-dependency
|/
* 14cb44b Merge branch 'feature/add-foo-dependency' into dev
|\
| * ba5c2c8 Create foo-dependency
|/
* a2a682a (master) Initial commit
|
I’m not a huge fan of this approach, because add-foo-dependency
and add-foo
both merge untested code directly into the dev
branch. While each feature does exactly one thing, the one thing sorta causes organizational problems. I don’t like untested code floating around in a main branch when I can avoid it.
Two Features
It could also look like this:
$ git flow feature start add-foo-dependency $ touch src/foo-dependency.ext $ git add src/foo-dependency.ext $ git commit -m 'Create foo-dependency' $ touch tests/foo-dependency.ext $ git add tests/foo-dependency.ext $ git commit -m 'Test foo-dependency' $ git flow feature finish --no-ff add-foo-dependency $ git flow feature start add-foo $ touch src/foo.ext $ git add src/foo.ext $ git commit -m 'Create foo' $ touch tests/foo.ext $ git add tests/foo.ext $ git commit -m 'Test foo' $ git flow feature finish --no-ff add-foo $ git log --graph --all --topo-order --decorate --oneline --boundary --color=always
|
* 76dada7 (HEAD -> dev) Merge branch 'feature/add-foo' into dev
|\
| * 54d82ae Test foo
| * 152b98c Create foo
|/
* 1d841a7 Merge branch 'feature/add-foo-dependency' into dev
|\
| * 0e062b5 Test foo-dependency
| * d730279 Create foo-dependency
|/
* ac908e3 (master) Initial commit
|
While this avoids merging untested code, it merges totally useless code with foo-dependency
. If there’s any appreciable time between add-foo-dependency
and add-foo
, someone else might create a feature that removes foo-dependency
by merit of its unnecessary inclusion.
One Feature
Or it could look like this:
$ git flow feature start add-foo $ touch src/foo-dependency.ext $ git add src/foo-dependency.ext $ git commit -m 'Create foo-dependency' $ touch tests/foo-dependency.ext $ git add tests/foo-dependency.ext $ git commit -m 'Test foo-dependency' $ touch src/foo.ext $ git add src/foo.ext $ git commit -m 'Create foo' $ touch tests/foo.ext $ git add tests/foo.ext $ git commit -m 'Test foo' $ git flow feature finish --no-ff add-foo $ git log --graph --all --topo-order --decorate --oneline --boundary --color=always
|
* 5f83929 (HEAD -> dev) Merge branch 'feature/add-foo' into dev
|\
| * dc4edaa Test foo
| * 20e3801 Create foo
| * aea02da Test foo-dependency
| * 113e27f Create foo-dependency
|/
* 8bff3c8 (master) Initial commit
|
If foo
is a bit more complicated than touch
ing a few files, this gets messy fast. foo-dependency
and foo
(as well as their associated tests) will probably have a ton of commits, which defeats the purpose of splitting out the branches.
Solution
The gitflow-avh
project attempts to update and extend gitflow
with some useful features. As far as I know, it’s a drop-in replacement for gitflow
, so you won’t notice a difference.
Installation
See the official docs for detailed instructions. Depending on your OS, you might be able to find it via a package manager as gitflow-avh
. If not, you can install manually without much trouble:
1
2
3
4
5
6
7
8
9
10
11
12
13 | #!/bin/bash # This is basically the wiki's installation script except more complicated
if which wget >/dev/null; then wget --no-check-certificate -q https://raw.githubusercontent.com/petervanderdoes/gitflow-avh/develop/contrib/gitflow-installer.sh elif which curl >/dev/null; then curl -fLO https://raw.githubusercontent.com/petervanderdoes/gitflow-avh/develop/contrib/gitflow-installer.sh else echo 'Unable to download installer' exit 1 fi sudo bash gitflow-installer.sh rm -f gitflow-installer.sh
|
Using Subfeatures
Vanilla gitflow
bases all features off the dev
branch.
$ git flow feature start do-something Switched to a new branch 'feature/do-something'
Summary of actions: - A new branch 'feature/do-something' was created, based on 'dev' - You are now on branch 'feature/do-something'
Now, start committing on your feature. When done, use:
git flow feature finish do-something $ git flow feature finish do-something Switched to branch 'dev' Already up to date. Deleted branch feature/do-something (was 6c3fe58).
Summary of actions: - The feature branch 'feature/do-something' was merged into 'dev' - Feature branch 'feature/do-something' has been locally deleted - You are now on branch 'dev'
|
With gitflow-avh
, you can base features off any branch, which means when you finish it, it’s merged back into the source branch.
$ git flow feature start do-something Switched to a new branch 'feature/do-something'
Summary of actions: - A new branch 'feature/do-something' was created, based on 'dev' - You are now on branch 'feature/do-something'
Now, start committing on your feature. When done, use:
git flow feature finish do-something $ git flow feature start subtask feature/do-something Switched to a new branch 'feature/subtask'
Summary of actions: - A new branch 'feature/subtask' was created, based on 'feature/do-something' - You are now on branch 'feature/subtask'
Now, start committing on your feature. When done, use:
git flow feature finish subtask $ git flow feature finish subtask Switched to branch 'feature/do-something' Already up to date. Deleted branch feature/subtask (was 6c3fe58).
Summary of actions: - The feature branch 'feature/subtask' was merged into 'feature/do-something' - Feature branch 'feature/subtask' has been locally deleted - You are now on branch 'feature/do-something' $ git flow feature finish do-something Switched to branch 'dev' Already up to date. Deleted branch feature/do-something (was 6c3fe58).
Summary of actions: - The feature branch 'feature/do-something' was merged into 'dev' - Feature branch 'feature/do-something' has been locally deleted - You are now on branch 'dev'
|
Problem Solved with Subfeatures
$ git flow feature start add-foo $ git flow feature start add-foo-dependency feature/add-foo $ git flow feature start create-foo-dependency feature/add-foo-dependency $ touch src/foo-dependency.ext $ git add src/foo-dependency.ext $ git commit -m 'Create foo-dependency' $ git flow feature finish --no-ff create-foo-dependency $ git flow feature start test-foo-dependency feature/add-foo-dependency $ touch test/foo-dependency.ext $ git add tests/foo-dependency.ext $ git commit -m 'Test foo-dependency' $ git flow feature finish --no-ff test-foo-dependency $ git flow feature finish --no-ff add-foo-dependency $ git flow feature start create-foo feature/add-foo $ touch src/foo.ext $ git add src/foo.ext $ git commit -m 'Create foo' $ git flow feature finish --no-ff create-foo $ git flow feature start test-foo feature/add-foo $ touch tests/foo.ext $ git add tests/foo.ext $ git commit -m 'Test foo' $ git flow feature finish --no-ff test-foo $ git flow feature finish --no-ff add-foo $ git log --graph --all --topo-order --decorate --oneline --boundary --color=always
|
* bd73c92 (HEAD -> dev) Merge branch 'feature/add-foo' into dev
|\
| * 4ee685f Merge branch 'feature/test-foo' into feature/add-foo
| |\
| | * 6ca71ec Test foo
| |/
| * 2472346 Merge branch 'feature/create-foo' into feature/add-foo
| |\
| | * c4f870a Create foo
| |/
| * e081189 Merge branch 'feature/add-foo-dependency' into feature/add-foo
| |\
|/ /
| * 7334e3e Merge branch 'feature/create-foo-dependency' into feature/add-foo-dependency
| |\
|/ /
| * bb4c45e Create foo-dependency
|/
* 26ddd97 (master) Initial commit
|