Skip to content

Instantly share code, notes, and snippets.

@jbenet
Last active April 9, 2024 03:31
Show Gist options
  • Save jbenet/ee6c9ac48068889b0912 to your computer and use it in GitHub Desktop.
Save jbenet/ee6c9ac48068889b0912 to your computer and use it in GitHub Desktop.
a simple git branching model

a simple git branching model (written in 2013)

This is a very simple git workflow. It (and variants) is in use by many people. I settled on it after using it very effectively at Athena. GitHub does something similar; Zach Holman mentioned it in this talk.

Update: Woah, thanks for all the attention. Didn't expect this simple rant to get popular.

Special Thanks to friends for feedback:

The gist

  1. master must always be deployable.
  2. all changes made through feature branches (pull-request + merge)
  3. rebase to avoid/resolve conflicts; merge in to master

Or, as Zach Holman succinctly put it:

flow

The workflow

# everything is happy and up-to-date in master
git checkout master
git pull origin master

# let's branch to make changes
git checkout -b my-new-feature

# go ahead, make changes now.
$EDITOR file

# commit your (incremental, atomic) changes
git add -p
git commit -m "my changes"

# keep abreast of other changes, to your feature branch or master.
# rebasing keeps our code working, merging easy, and history clean.
git fetch origin
git rebase origin/my-new-feature
git rebase origin/master

# optional: push your branch for discussion (pull-request)
#           you might do this many times as you develop.
git push origin my-new-feature

# optional: feel free to rebase within your feature branch at will.
#           ok to rebase after pushing if your team can handle it!
git rebase -i origin/master

# merge when done developing.
# --no-ff preserves feature history and easy full-feature reverts
# merge commits should not include changes; rebasing reconciles issues
# github takes care of this in a Pull-Request merge
git checkout master
git pull origin master
git merge --no-ff my-new-feature


# optional: tag important things, such as releases
git tag 1.0.0-RC1

useful config

# autosetup rebase so that pulls rebase by default
git config --global branch.autosetuprebase always

# if you already have branches (made before `autosetuprebase always`)
git config branch.<branchname>.rebase true

DOs and DON'Ts

No DO or DON'T is sacred. You'll obviously run into exceptions, and develop your own way of doing things. However, these are guidelines I've found useful.

DOs

  • DO keep master in working order.
  • DO rebase your feature branches.
    • DO pull in (rebase on top of) changes
  • DO tag releases
  • DO push feature branches for discussion
  • DO learn to rebase

DON'Ts

  • DON'T merge in broken code.
  • DON'T commit onto master directly.
    • DON'T hotfix onto master! use a feature branch.
  • DON'T rebase master.
  • DON'T merge with conflicts. handle conflicts upon rebasing.

Links

FAQ

Won't git merge --no-ff generate merge bubbles?

Yes. Merge bubbles aren't inherently bad. They allow you to revert entire features at a time. They get confusing and annoying to deal with if they cross (commits interleave), so don't do that.

merge bubbles

What do you mean by "incremental, atomic" changes?

http://en.wikipedia.org/wiki/Atomic_commit#Atomic_Commit_Convention

Thanks wikipedia, I couldn't have put it better myself.

Why not gitflow or another complex workflow?

Be my guest. I've used gitflow and other similar models. After working in various teams, this is just what I've come to use. But next time you have to ask someone whether it is okay to push or pull from this or that branch, remember my face.

But, is it web-scale?

Friends claim more complex models are necessary for scaling large teams, maintaining old releases, controlling information flow, etc. It very well may be that using multiple mainlines (e.g. develop, stable, release, v2, tested, etc) is exactly what fits your organization's constraints. That's for you to decide, not me (unless we work together -- oh hi there!).

But you always have to wonder, "shouldn't I use tags for that"? For example, tracking releases on a branch is a bit silly. A release commit can be tagged. You can checkout a tag, just like any branch, or any commit, and do whatever it is you need to do.

My guess is this relationship holds:

headless

So, perhaps taking five minutes to teach your team how to use checkout and tag might save you more than 15% on car insurance.

GitHub notes

Don't fork. Push feature branches to main repo.

Sometimes I see people forking repositories in order to issue pull-requests. Yes, you may have to do this when contributing to open-source projects you don't regularly contribute to. But, if you are a contributor, or working in the same org, get push rights on the repo and push all your feature branches to it. Issue pull requests from one branch to another within the same repo.

Should I merge Pull Requests on the site or commandline?

Up to you. Github does git merge --no-ff so that the commit message indicates the pull request number. This is useful information to have, don't just throw away history for the sake of it. You never know what will be useful to look at in the future.

@fibo
Copy link

fibo commented Sep 28, 2013

I prefer keep master branch clean with current release, think about users going to github repo and viewing a README that is not of the current release. Instead I use a develop branch, a part that I use the same workflow.

@morhekil
Copy link

@amccloud gitolite allows you to mark any branch as "protected", so that only people with the appropriate permissions can push to it. This allows anyone to play with the feature branches (and maybe deploy them into staging sandbox), but then go through the review process with a more senior person to get the code sanity checked and merged. Gitlab (based on gitolite) puts a nice Github-like web interface on top of that - allowing people to create merge requests from their feature branches, and stopping them from merging their changes into master if they don't have write access to it.

@jbenet
Copy link
Author

jbenet commented Sep 28, 2013

@fibo cool! One point to note is that master should be release-ready at any time. One should be able to take master and expect it to work. For example, when using continuous-deployment, you don't have voluntary releases. As soon as the codebase passes the test suites, it is automatically deployed. Many webapp dev teams operate this way. So if the last release is many commits ago -- and the readme is now radically different -- perhaps try releasing more often :).

@JochenFromm
Copy link

@aaronjensen
Copy link

In my experience, this flow without the rebasing works well. The problem with rebasing is that you're rewriting history. If you have a merge conflict between what master has become and your branch that conflict resolution is lost into one (or worse, many) commits in your rebased branch. This means that if you did this merge wrong, well, you're going to have to hunt it down. A regular merge keeps the conflict resolution in the merge commit itself. It's easy to see if something went wrong there.

Rebasing also makes conflict resolution more cumbersome. You must resolve conflicts every commit instead of at the end. This means that if you may have to resolve conflicts in the same file multiple times without the context of what you did in your branch later on in the history. It makes those more error prone and more tedious. This obviously exacerbates the issue I mentioned in my previous point.

I've also had absolutely no problem bisecting with gnarly "branch bubbles". Yes, it's a little hard to read, but I don't often need to read it, and when I do, I can figure it out.

In short, it's less work and less dangerous to not rebase before merging, and the only benefit I've actually seen from rebasing before merging is a nicer looking history.

That said, I do rebase often, but typically it's -i and on my previous base simply to clean up commits before pushing.

@adymitruk
Copy link

No back merges. See my model on Dymitruk.com. You need more to make release candidate and see how things fit along the way with an integration branch.

@simonc
Copy link

simonc commented Oct 2, 2013

Small question though, if you use Early Pull Requests to discuss subjects, how would you easily rebase a branch since it has already been pushed to Github ?

@StefanWallin
Copy link

We're not using github, but a bare git workflow and trying to implement this, but we have an issue with wanting bug fixes from master in our feature branches and then after rebase and merge not have it appear in the history of the feature branch or generate any nasty merge conflicts. Could this be done?

If my question was unclear, maybe this is more clear? http://superuser.com/questions/653812/pulling-changes-into-remote-tracking-feature-branch-while-keeping-simple-branchi

@pawanpoudel
Copy link

@zimbatm I have been using Git's autosquash feature for automatically annotating commits that need to be squashed into the original commit. Here is a really good blogpost on it: http://chrismar035.com/2013/04/04/better-pull-requests-with-autosquash/. The author of that blogpost has also published a demo video: http://asciinema.org/a/3467.

@Elyahou
Copy link

Elyahou commented Nov 4, 2013

--no-ff preserves feature history and easy full-feature reverts

Can you please explain how to revert a feature branch merge, fix issues on it and remerge on master ?

Thank you

@shamrin
Copy link

shamrin commented Nov 21, 2013

The images are gone. The first image (flow) was taken from Zach Holman talk. I don't remember what are the remaining images (headless, merge bubbles). Does anyone know where to find them and restore this post?

flow:

flow

@eboto
Copy link

eboto commented Dec 2, 2013

Found this on one on pandawhale.
Merge bubbles good vs bad

@shvechikov
Copy link

@shamrin, @eboto, Thanks!

I've found other images in google cache and fixed the article in my fork — https://gist.github.com/shvechikov/de39c99574488a3de12a/

@jbenet
Copy link
Author

jbenet commented Dec 14, 2013

Ah, sorry guys. My server got taken down. Thanks for fixing @shamrin, @eboto, @shvechikov. I merged your changes. :)

In the future, feel free to email me, as gist comments do not notify me.

@Elyahou
Copy link

Elyahou commented Dec 25, 2013

# optional: push your branch for discussion (pull-request)
#           you might do this many times as you develop.
git push origin my-new-feature

Maybe you have to note that after first push, and after rebase, the command would be:

git push -f origin my-new-feature

@marcandre
Copy link

@Elyahou: indeed, without the --force / -f option, it won't work (assuming there was any development in master)

@leesmith
Copy link

Don't forget to cleanup your repo after you merge your feature branch into master:

git branch -d feature-branch-name
git push origin :feature-branch-name

@gsmendoza
Copy link

We recently released a gem that automates this workflow. Please check out http://tech.lovewithfood.com/blog/2014/01/19/git-pretty-accept-accept-pull-requests-the-pretty-way. We'd love to hear your feedback :)

@SurajGupta
Copy link

Here's Powershell that automates this workflow: https://github.com/NaosFramework/Naos.Powershell/blob/master/GIT/Naos-GitBranchingTools.ps1

And this will install it (download to disk and adds reference in your PS profile):
Invoke-Expression ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/NaosFramework/Naos.Powershell/master/GIT/Install-GitBranchModel.ps1'))

But you also need Posh Git, install that first:
https://chocolatey.org/packages/poshgit

Use functions BranchUp and GitUp

  • BranchUp updates your branch with the latest from the same branch on origin and then from origin/master. Then, you can optionally push your branch to origin
  • Use GitUp when you are ready to merge into master and push to origin. GitUp will run BranchUp before merging into master.
  • So a typical workflow is to branch, do work and call BranchUp iteratively, then finally GitUp when you are ready to merge into main

This is a must have so you don't have to keep re-typing your credentials:
https://chocolatey.org/packages/git-credential-winstore

@dhbradshaw
Copy link

Why secret? Now it can't be found through a search.

@bartlettroscoe
Copy link

Lots of rebasing on top of origin/master. Where is the mention of git rerere? Without that, you will be fighting the same merge conflicts over and over again every time you type git rebase origin/master. Without that people with long-lived feature branches and lots of refactoring being pushed to 'master' are going to hate you.

@alper
Copy link

alper commented Oct 15, 2015

Yeah, it would be great if this wasn't secret.

Also what I'm missing is that if you rebase your feature branch that you should push --force it to origin because otherwise things go fairly wrong for me.

@DonaldTsang
Copy link

@jbenet please update this repo with https://gist.github.com/chalasr/fd195d83a0a01e4291a8 if possible
Also if possible we should cross-reference https://www.endoflineblog.com/oneflow-a-git-branching-model-and-workflow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment