2013-09-24

git lesson of the day (courtesy of Brad)

git rebase, reflog, and force push - the nice way to deal with your own branches and keep a clean git history.

Warning: don't do this on branches that other people are working on - the force push does nasty things. In that case, it's better to coordinate, collect changes, make a new branch, whatever.

So you've been working on a branch (let's call it ben.foo) and you're ready to squash a bunch of commits and push it back up to master.
* bring your checkout up to date by getting the heads of all branches
git fetch
* set your upstream to master
git branch --set-upstream ben.foo origin/master
* rebase all your changes that are different from master
** this brings your branch up to date and puts all your branch's commits after the most recent commit to master
git rebase
* squash all your commits
** this automatically chooses only the commits that don't exist in master
git rebase -i
* push them up, forcing your branch to remove your previous more verbose commits
** the + sign before the branch name means force push only this branch - much safer than --force, which forces all branches
git push origin +ben.foo
Whee!! You're done!

Let's do this another way.

We don't really care about master for most of this process - we're really just interested in squashing our own commits, then we can merge or do a PR or whatever like normal.

* Look for the commit before the place we started our branch. I'll assume it's visually easy to find - often the last commit by someone else before a long string of my own commits. Note the commit ID of the not-my-branch commit:
git log
* Do an interactive git rebase from that commit on
git rebase -i abcd1234
* squash all but the first commit (or however you want to collapse your commit history)
2Gv}:s/^pick/squash/<ret>:wq
* push this up, using the same +branch force in the previous example, overwriting the remote repository's messy history
git push origin +ben.foo
Voila! Your commit history is now clean

If you screw up any of these steps...

git reflog to the rescue.
git reflog ben.foo
This is a complete history of the changes you've made to the branch. Choose the commit ID you want to backtrack to (eg abcd1234) and run
git reset --hard abcd1234
Further Reading:
* Two good posts about git reflog
* Many thanks to Brad Kittenbrink for going over this with me.