Jeff Kreeftmeijer

Git your act together

2010-08-23

Last week I released an article on git-flow, a tool that helps you keep your Git repositories organized. Today I’d like to explain some things that tend to annoy me while working with other people and using Git.

This article possibly won’t tell you anything you didn’t know already, but I just wanted to get this out there and tell you some things that I think would make working with Git a bit more enjoyable.

Write good commit messages

Commit messages are important. They document the project’s progress and they’re a great way to see what has been done in a commit without having to read the code. Also, commit messages make it easy to dive into git log and find that commit you want to review or revert.

Obviously, you should think about your commit message before typing git commit -m ', slamming your hand on your keyboard, adding the last ' and pressing return. Yes, people actually do that.

Another thing is trying to be funny in their messages, blaming other people for writing bad code or writing stuff like “I don’t know what the hell I was thinking.”. Newsflash: You’re not funny, you’re being an idiot.

Check out whatthecommit.com for more bad examples.

I don’t want to know how you feel, what you ate, what time it is or who fucked up in the first place. Please tell me what you did and how you did it. Don’t try to be clever, witty or funny. Document your change as good as you can and nothing else.

A note about Git commit messages” by @tpope might be a good read if you think you need some guidelines.

Commit all the fucking time

You’re at work and your client sends you an email saying he wants a couple of small bugs fixed and the header to be blue instead of green. Other than the rest of your clients, he’s right: these are really small bugs and changing the header’s color is just a one-line change.

After getting yourself some coffee, you fix the bugs and change the header in about five minutes. You commit everything:

commit 32faa25585e162f82206b8ce791ec098d6e34677
Author: Jeff Kreeftmeijer
Date:   Mon Aug 23 11:27:10 2010 +0200

  Fix some bugs and make the header blue.

After some time and some commits, the client calls you and asks you to undo the header’s background color back to green. What do you do now?

You can’t git revert 32faa25585e162f82206b8ce791ec098d6e34677, because that will bring back the bugs you fixed in that commit too. You’ll have to manually check what the original hue of green was and change it back.

Seriously. Just commit one thing at a time. Don’t wait until you go home, don’t fix three really simple bugs before committing, commit every single change separately.

Rebase & —amend to get rid of “oops”-commits

Let’s say you wrote this:

def started
  update_attributes({
    :started_at =>  Time.now
  }
end

And you committed it:

commit a5d8e964f6ac22809ffcd7d38ec503e36a27e00b
Author: Jeff Kreeftmeijer
Date:   Mon Aug 23 15:11:10 2010 +0200

  Add Task#start to set the started_at attribute to the current time.

After you committed, you find out that you missed the closing parenthesis on line four. You quickly fix it and commit the new version:

commit b3ff4620c2accd674427322cd1c10d634ab63d3a
Author: Jeff Kreeftmeijer
Date:   Mon Aug 23 15:14:48 2010 +0200

  Oops! Forgot the closing parenthesis in Task#start.

It might happen that the second commit didn’t fix everything either and you have to commit a fix again. Maybe once more, because it’s been a long day. You’d end up with a bunch of commits fixing problems caused by previous commits.

Well, now it works. After a week you don’t like this method anymore and want to remove it, which should be easy because you committed small chunks, right? No. You can’t just git revert a5d8e964f6ac22809ffcd7d38ec503e36a27e00b, you’ll have to revert both separately.

Whenever you find yourself writing “oops” because you’re fixing a mistake you made in the last commit, please rebase them into one. Don’t worry, it sounds harder than it is.

Let’s stick with the example we used above. We want to squash the last two commits into one:

git rebase -i HEAD~2

A text editor opens and shows you something like this:

pick a5d8e96 Add Task#start to set the started_at attribute to the current time.
pick b3ff462 Oops! Forgot the closing parenthesis in Task#start.

# Rebase a5d8e96..b3ff462 onto a5d8e96
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

If you replace the second occurrence of “pick” with “squash” and save, another text editor pops up asking you to write a new commit message. After you saved that one, your commits will be merged into one, making your repository cleaner and allowing you to git revert the whole thing at once.

If you want more information about rebasing, be sure to read “Squashing commits with Rebase” on Git Ready.

If the commit you want to squash into is the last commit, you can even directly add new changes to it by using the --amend flag when committing:

  git commit --amend

I’m not perfect either

Before you start digging through my repositories: I’ve learned a lot in the last couple of months and I’m improving, but I’m not perfect either. I’d love to learn what you do to keep your Git repositories maintainable, so be sure to put your best tip in the comments.