Git workflows and best practices
Preliminary remark: I use a bunch of shortcuts and abbreviations here for git commands which follow the official “git best practices” and you should configure your environment accordingly as well. See https://github.com/troessner/dot/blob/master/.gitconfig
- Create feature branch and switch to it
git co -b feature_branch
- Publish it on remote
git ps -u origin feature_branch
Hint: If you use a tool like:
- git wtf: http://git-wt-commit.rubyforge.org/
- git-remote-branch: https://github.com/webmat/git_remote_branch
you can get this done even quicker.
- Do your changes
- Stage them
git add -p
Yes, that’s a “-p” there. Please do never use
git add .
- When you think you’re done commit everything
git ci -m ‘Your meaningfull commit message in proper english.’
- Publish them
Now you can open up that pull request on github. Tip: Becomes even easier with the github gem.
This pull request should be reviewed and commented by one of your peers before anything else happens.
- Close the pull request remotely and delete the branch via github interface (there’s a button showing up after you merged / closed that pull request.)
- Delete the branch locally via
git b -D your_feature_branch
First of all, you need to understand why it is absolutely necessary that your whole PR is one, atomic pull request:
- git-log is one of the most important git tools and if you’re not familiar with it, you really should learn about it (more to come here soon). However, if one feature is split across several commits git-log is useless because I can’t tell what belongs where.
- If I realize a feature has gone wrong and I need to remove it from the mainline I can’t do that if that feature is spread across multiple commits which are already intertwined with master without doing potentially dangerous rebasing. This would also require that I do know exactly what commits belong to what feature which is next to impossible, even after a short time.
- If one feature is one commit I can easily play around with, e.g. deploy features in different combinations to staging servers.
So how do I do that?
Ok, so let’s say you have 3 commits in your feature branch that do not exist in master and your feature branch is named “fancy_feature”.
First of all we need to find out how many and what commits that are:
Basically this is just
git log fancy_feature –not master
However this gets tedious to repeat so you want to do something similar to what I did and add an alias to your bashrc (or whereever it fits you) like this:
alias sh_exclusive_commits=’git log $(git symbolic-ref -q HEAD) –not master –pretty=oneline –abbrev-commit –decorate’
So now, when I execute
I get (given that I am on the feature branch of course):
9c5ec4b (HEAD, fancy_feature) Finalize feature. 6b24ba0 Fix foobar. b3210dc Update foo to do bar.
Now let’s squash those commits into one:
Generally you would do it like that:
git rebase -i HEAD~3
which tells git to take the last 3 commits starting from HEAD (the commit on top of the branch you’re working on).
But since you don’t want to count commits manually you’d better extract that in a helpful alias as well:
alias squash_exclusive_commits=’git rb -i HEAD~$(
sh_exclusive_commits) | wc -l‘
Now, if you execute this, you’ll see something like this:
pick b3210dc Update foo to do bar.$ pick 6b24ba0 Fix foobar.$ pick 9c5ec4b Finalize feature.$ # Rebase 82ed078..9c5ec4b onto 82ed078
What you want to do now is:
- Squash those 3 commits into one
- Give that one commit a proper name
- Merge it back into master
To do that this is what the menu should look like:
r b3210dc Update foo to do bar.$ f 6b24ba0 Fix foobar.$ f 9c5ec4b Finalize feature.$
This tells git to “fix up” 9c5ec4b and 6b24ba0 into b3210dc and rename b3210dc in the same step. If you save that file now git will perform those operations and then let you rename b3210dc. Now to the last steps:
git co master
git cherry-pick your_final_commit