Fri 13 Jan 2012
FRIDAY THE 13TH – GIT HORROR STORIES
It’s Friday the 13th!
Git’s an amazing tool, but it has some sharp edges like any powerful tool. Just like the killer from a horror movie, those sharp edges can cause some problems over and over again.
Rally to the rescue!
If there’s a mistake to be made with git, we’ve made it. We’ve been the curious and terrified idiots who stray from the group, only to be singled out and eliminated by the masked killer. Luckily, we’ve also been the conquering hero who discovers the killer’s weakness and uses it to live on to the next movie.
Killer #1 => Commit to Wrong Branch
So you heard about a production bug and you fixed. The problem is, you accidentally fixed it on a development branch that has work that’s not ready for prime-time yet. There are other commits you don’t want, so you can’t just merge your branch into the release branch.
Weakness: Cherry Pick
What you need to do is cherry pick your commit to the release branch:
git log # copy the SHA of the commit you want
git checkout release_branch
git cherry pick <SHA_from_log>
The key here is that you need to revert the commit on your development branch to avoid a merge conflict when you merge to the release branch.
Killer #2 = > Delete the Wrong Branch
You typed this:
git branch
And it showed you about 30 branches. You got pissed that you hadn’t cleaned branches in a while and then typed this:
git branch -D seemingly_innocuous_branch
HOLY SHIT I JUST DELETED 10 DAYS OF WORK!
Weakness: Reflog
The silver bullet is:
git reflog
From there, find the reflog entry that contains the changes you want. Grab the SHA from it, then do this:
git checkout -b new_innocuous_branch_name <SHA_from_reflog>
Killer #3 = > Smittied the Repo
This is a bad one. You’re working on a feature branch. You’re regularly merging your master branch into it to avoid merge conflicts down the road. You did this:
git checkout feature_branch
make a commit to feature_branch
git merge origin/master
Then you mean to do this:
git push origin HEAD:feature_branch
But you did this instead:
git push origin HEAD:master
What just happened is that you pushed a bunch of commits from your feature branch into master instead of the opposite. The weird merge direction will screw up your git timeline.
Weakness: Rebuild the branch
Your local feature branch is still what you want to push to your remote feature branch, so your first step is to do what you originally intended:
git push origin HEAD:feature_branch
Next, you need to un-hork your remote master branch. To do this, you’ll actually delete and recreate your remote master branch:
git fetch
git log origin/master # copy the last good SHA from before your merge
git branch -D master # delete your local master branch
git checkout -b master <SHA_from_master_log>
git push origin :master # delete the remote master branch
git push origin HEAD:master # create a new master branch from HEAD
One important note on that last solution is that anyone who rebased against or pulled from master after the merge commit was pushed will need to delete and recreate their local master branch like so:
git fetch
git branch -D master
git checkout -b master origin/master
Git’s an awesome tool, but using it can be a lot like using a chainsaw: there are ways you can hack off your own foot if you’re not careful. Hopefully this post will help you recover from some of the common and seemingly destructive mistakes.

Pushing HEAD just seems like a terrible idea. If you always used your branch name, this couldn’t be an issue.
git push origin master. Master is getting pushed, period.
git push origin feature. Feature is getting pushed, period.
Names don’t match up? I question your motives, but you can still push. git push origin feature:remote-name.
HEAD floats around willy nilly, and branches don’t. By treating branches as their own things with names matching and tracking remotes, the following two operations are safe:
git push #push everything
git pull #pull everything.
If you had commits on master, they had better have been on their way into the remote, and if they weren’t, you weren’t branching enough.
tldr: Branch more, never push HEAD.
@Stefan: Good point – that’s part of why it’s a horror story. Never pushing HEAD is a great idea if you always use tracking branches. Git’s local branches are awesome. Pushing HEAD can be valuable if you’re looking to create a new remote branch from your working set, which is how I believe we’ve screwed this up in the past. We should probably reevaluate how we use it otherwise.
I’ll argue vehemently against ever using “git push” without a qualifier, at least the way we work. I’ll happily provide more detail if needed, but the short version is that we work a lot on pairing stations. If you picked up on a pairing station with a stray commit on branch XYZ that happened to have a clean-seeming branch ABC, and you made a change to ABC, you could easily do damage to XYZ by just doing a push. In a shared machine environment, NEVER doing an unqualified push is a good idea.
Also, I’ll argue pretty strongly against remote feature branches.