I review projects’ commit histories pretty frequently. What can I say? I’m an overzealous older brother always looking to improve myself and the teammates around me. I watch projects progress, looking back to see how features and implementations have evolved. As a team, we also use Github’s compare feature in incremental release notes to our clients to keep ourselves accountable, ease integrations, and educate our clients.
So, let’s compare commit histories in chruby and rails…
Looking at the commit history of chruby (on the left), it’s pretty clear what’s happening from commit to commit. Without too much digging, you can get a quick understanding of the changes that went into the first revision of v0.3. Rails commit history (on the right), on the other hand, is a little more challenging.
The difference is hidden in those pesky Merge pull request #17881 from awesome-contributor/cool-new-feature commits created by Github’s big green Merge Pull Request button. Others have voiced their hearty opinions about the Big Green History Crusher™ and though I tend to agree, I’d rather share an alternative for merging pull requests that leaves behind a helpful commit history for collaboration, code review, and diagnosis.
In almost every case, we’re collaborating by pushing and pulling against a central Github repository—the Hub and Spoke model. To get square, we would
git pull --rebase while in
master. Some prefer to use git-up for this, but I have an alias that’s less magical. The
--rebase assures that any commits I alone have are reapplied atop those coming from
origin. In most cases, nothing like this happens, but I’m always relieved when it does.
master is up to speed, we rebase our PR branch with
git rebase master. We rebase our PR branch for the same reason we used
--rebase—we want to ensure that our changes are reapplied on top of any changes already integrated into
origin/master (the Hub of our collaboration).
Git Fast Forward
This is the key to beautiful history. Put simply, we likely want
master to look like the branch underlying this pull request. If only we could just point
master at that last commit in
fancy-feature-branch. Well, we can:
$> git merge --ff-only fancy-feature-branch Updating 020fbbd..0d154e9 Fast-forward README.md | 2 ++ 1 file changed, 2 insertions(+)
Since we were good contributors and rebased the feature branch, we didn’t really need the
--ff-only option. By including this flag, though, we’re telling git we want this to fail if it’s not a fast forward. Coincidentally, this is the complete opposite of what the Big Green History Crusher™ invokes. That will give us a merge commit every time—even if it wasn’t necessary.
At this point, we’ve merged in the pull request locally, rerun our tests because we’ve been embarrassed too many times, and we’re ready to close things out with a
git push. And that’s all you really have to do. Except then we would have stale branches locally and on origin. Wouldn’t it be nice if we could push our merged pull request and clean it up in one go?
Surprise! We can:
$> git push && git push origin :fancy-feature-branch && git branch -d fancy-feature-branch Total 0 (delta 0), reused 0 (delta 0) To email@example.com:cromwellryan/dotfiles 020fbbd..0d154e9 master -> master To firstname.lastname@example.org:cromwellryan/dotfiles - [deleted] fancy-feature-branch Deleted branch fancy-feature-branch (was 0d154e9).
The odd-looking command in the middle,
git push origin :, will delete a remote branch. That’s a tall order for a simple colon, but it’s incredibly convenient. Heck, if you follow our Git Commit Style Guide, you can even close issues automatically!
That’s an awful lot of typing compared to clicking a Big Green Button™. Luckily, we have robots to do boring things for us. That’s why I’ve created Git-Poooosh.
Git-Poooosh is a plugin for oh-my-zsh that will push the current branch and delete the remote and local feature branches, with branch autocomplete. Simply drop the
git-poooosh directory into
~/.oh-my-zsh/custom/plugins, add it to your .zshrc plugins, and restart your shell. Now that monstrous last step looks like this:
$> gpp fancy-feature-branch Total 0 (delta 0), reused 0 (delta 0) To email@example.com:cromwellryan/dotfiles 020fbbd..0d154e9 master -> master To firstname.lastname@example.org:cromwellryan/dotfiles - [deleted] fancy-feature-branch Deleted branch fancy-feature-branch (was 0d154e9).
It can be very easy to take source control and commit history for granted, writing it off as a necessary evil. From more effective pull requests to locating changes in the past, a few subtle changes in your git workflow can help you take back control of your commit history from the Big Green History Crusher™.