Skip to content
ENOMA DEBORAH edited this page Apr 1, 2015 · 3 revisions

Overview

  • These are some rough notes on a Git workflow that uses rebasing instead of merging for feature branches.
  • There is now a brief section at the end on how to handle production release branches (which, like master, are never rebased). Essentially, these can just be branches off of a stable master branch revision.
  • This is an incomplete work in progress It was originally copied from https://gist.github.com/17twenty/6733076 (the best writeup I could find), and then modified. Some things are still not completely accurate, and some I (Chad Woolley) don't fully agree with (e.g. using tags)
  • I (Chad Woolley) am working on a tool, 'gitrflow' to support this workflow, as well as some documentation on why rebasing is preferred for feature branches.
  • Finally, an alternative to feature branches (when they are used to hide incomplete features in production) is Feature Flags/Toggles.

Git Visualization

  • Also, check out Git D3 Visualization - it is a great interactive tool to visualize exactly what git commands are doing.

A Rebase-Based Git Branching Model

This is a very simple git workflow. It (and variants) is in use by many people. It has been used effectively on many teams. GitHub does something similar; Zach Holman mentioned it in this talk.

The gist

  1. master must always be deployable.
  2. all changes made through feature branches
  3. rebase continually (before beginning work on a feature branch, after pushing to a feature branch) to avoid/resolve conflicts;
  4. merge feature branches back in to master with --no-ff flag.

Or, as Zach Holman succinctly put it:

flow

The workflow

IMPORTANT PREREQUISITES:

  • With this workflow, only ONE PERSON OR PAIR should work on a feature branch at a time.
  • After a rebase onto a feature branch, you will be required to force push to that branch. This is OK because only one person at a time is working on it.
# everything is happy and up-to-date in master
git status # make sure your working directory is clean

# discarding all changes you don't want
# If you want to discard all changes in your working directory and start over:
git reset --hard
git clean -df

# checkout master branch
git checkout master
git pull origin master

Now you have the repo, make changes on master... Write tests and code, red-green-refactor. Once you have good changes, green tests, and haven't broken anything on master, you are ready to commit and push:

IMPORTANT NOTE: Note the git pull --rebase before you push. This is so you don't get merge conflicts. Remember you should never rebase master itself and force push it, like you would a feature branch, but that's not what you are doing here. Here, you are just ensuring that your local changes are rebased ONTO master BEFORE you push them, and avoiding a possible separate merge-conflict-resolving commit

# If your changes are safe (and you ran tests), commit on master:
git add -p
git commit -m "message"
# Ensure you are up to date with master
git pull --rebase # to get any changes that have happened on master since you started
git push [origin master]

Use this flow to get on a feature branch, or if you're already on one.

# let's branch to make changes (omit the -b if the branch already exists)
git checkout -b my-new-feature 

# make any more required changes...

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

# push your changes to the feature branch
git push origin my-new-feature

# keep abreast of other changes to master.
# rebasing keeps our code working, merging easy, and history clean.
# Do this often - at the beginning of every day, and after every
# push to the feature branch
git fetch origin
git rebase origin/master
# IMPORTANT NOTE: If you have conflicts, always use '--continue', never '--skip'.
#                 See "DOs" and "DON'Ts" below.  If you are confused, ask for help

# push your branch to github after every commit
git push --force origin my-new-feature

important note: Note the "force" above in the push. This is necessary after a rebase, because the SHAs of the commits change. However, if you are the ONLY person/pair working on the feature branch (and you should be!), then it is fine to force push, because nobody else should have changed it.

# optional: feel free to rebase within your feature branch at will.
#           ok to rebase after pushing if your team can handle it!
#           Google for more details on interactive rebase.
git rebase -i <SHA you are rebasing against, NOT necessarily 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

# now push the branch to master
git push origin master

# this MIGHT fail if somebody happened to push to master while you were merging.
# If so, you can do `git pull --rebase` to get their changes, then try pushing again
git pull --rebase && git push origin master

# after you are done merging your feature branch back to master and pushing, delete the local
# branch with the '-d' option (not forced, like -D).  if you have successfully merged
# all the feature branch's commits to master, this no-force delete command should complete
# with no warnings or errors, because git knows all the commits on the branch have 
# been successfully merged to master.
git branch -d my-new-feature

# Then, delete the remote branch off of github:
git push --delete origin my-new-feature

# then everyone else can do a fetch with --prune to clean up the branch
# off their local machines
git fetch --prune

# 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

Fixing a Commit Before You Push

  • Firstly, check for the status
  • git status
  • If there was a staged commit and changes was made to the directory, you can roll back the commit with
  • git reset HEAD^
  • Check to confirm all the changes you have made to the current project
  • git diff
  • Add the changes you intend to commit
  • git add -p
  • Do the commit and add your message
  • git commit -m "message"
  • Do the push
  • git push

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
    • When rebasing in changes and resolving conflicts, always use '--continue' - never skip conflicts

DON'Ts

  • DON'T EVER force push master! This includes push +master
  • DON'T rebase the remote master (Note that doing pull --rebase prior to a push doesn't count at as "remote").
  • DON'T merge in broken code.
  • DON'T merge with conflicts. handle conflicts upon rebasing.
    • When rebasing, you should NEVER commit a merge conflict! If you end up with a merge conflict, you did something wrong, and should abort and start over.

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.

Production Release Branches

Purpose of Prod Release Branches

Production release branches are intended to be a "stable" fork of the code. It allows you to:

  • Prepare code for a production release in isolation, while still being able to commit new code to master without the risk of breaking the production release.
  • Make final tweaks (e.g. cherry pick changes from master or other branches) to prepare for the production deploy.
  • After the release is in production, make subsequent hotfixes and further releases from the same branch, ensuring it is completely stable and unchanged other than the hotfix.

Rules for Prod Release Branches

  • They are NEVER rebased or force pushed. This would go against their goal of stability and repeatability. The only changes made to them are to make minor bug fixes or tweaks, ideally cherry picked from master or merged in from a hotfix branch (which can also be merged to master).
  • Naming Conventions: It's good to name them consistently, and use a date which is the target release date, or a branch date. E.g. prod-release-2015-01-15. You could also use Semantic Versioning, e.g. v1.0 (leave off patch version, that's included in a tag, see below)
  • Tags: It's a good idea to create a tag for the revision that actually gets deployed to staging and then production, with incrementing numbers. If you make multiple releases from the same prod release branch, you can increment the tag number. E.g. prod-release-2015-01-15-1, prod-release-2015-01-15-2. Or with Semantic Versioning, just tag the first release from the branch the same as the tag (1.0.0), then subsequent releases bump the patch version (e.g. 1.0.1, 1.0.2).
  • The general idea is that tags and branches should sort chronologically.

References for Prod Release Branches

The following pages have some info on prod release branches, search for 'prod' or 'release'. The important thing to remember when reading them is that we do not use a develop branch, we make branches directly off of master. Other than that, the information these pages have on release branches and hotfix branches is relevant: