Merge Bubbles. No. Bad.
Hey you.. listen up!
I know, I know, I also felt the pain. Git: “Huuhh?? I have to commit and push? Uh.. And what’s the difference between git fetch and git pull?”
I know. But here’s something that will help: SVN and CVS are CVCS’s: Centralized Version Control Systems. Git is much closer to a file system. And please, don’t use Git like you used SVN or CVS.
The picture on the left is from a interesting talk on language developments from the creator of Clojure, Rick Hickey. You can find the talk here.
But it also (coincidentally) has a decent representation of how the Git ‘file’ system works. We can look at the two “trees” in this picture as branches in a Git repository.
However, what I really want you to take away from the minutes spent reading this is that you need to stop using Git as if it was SVN or CVS. You need to stop using merge unless you wanted to shout at everyone who looks at that merge commit later that “YES, I MERGED STUFF HERE!”.
This is the leap that Git has achieved: Git has made commits into a form of documentation. Commits with Git are so transparent and easy to manage and follow that everyone sees the commits — and wants to look at them.
However, most people use merge because they run into the following:
My teammate/colleague just committed something and I can no longer push to the repository!
So what do we do? What we do not do, is merge. Okay?
The first thing we do do is copy the name of the commit we’ve made. Now that the changes have been committed, that commit now exists on the “Git filesystem”, regardless of whether or not it’s part of a branch.
$ git commit -m"Added tests for new Fromungulator logic" [master 0ca7df5] Added tests for new Fromungulator logic 4 files changed, 3 insertions(+), 1 deletions(-)
0ca7df5 is of course your commit hash — or, if we’re thinking in terms of the Git file system, it’s the reference to the “git file” that contains your commit. Of course, if your colleague went ahead and pushed a commit to the repository before you could, you’ll see this:
$ git push origin master To email@example.com:mrietveld/frunubucation.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to 'firstname.lastname@example.org:mrietveld/frunubucation.git' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes (e.g. 'git pull') before pushing again. See the 'Note about fast-forwards' section of 'git push --help' for details.
They lie.. Do not, I repeat, do not then go do this:
git pull origin master
(unless you’ve setup your branch with auto-rebasing. There’s a funny post about that here.)
There are lots of ways to avoid merge bubbles, but the fastest is probably to do the following:
git fetch origin git rebase origin/master
Rebase! Man, I love git! Rebase is the equivalent to doing this:
git reset --hard HEAD^1 git merge --ff origin/master git cherry-pick 0ca7df5
And once you’re done, you’ll want to push that commit to the origin repository:
git push origin master
Why do we nog cause merge bubbles? Because:
- Merge bubbles make it hard to determine which commit caused which change in the code
- Merge bubbles make it hard to retroactively fork off a branch from a different commit.
The first reason, determining which commit caused what, is really enough, though. That’s one of the main reasons you’re using a versioning system!