Thursday, February 24, 2011

Using git to keep your patches small and focused...


On my current project at work we have a process requirement to not send out patches that have a lot of changes in them. Instead such patches need to be broken up into smaller, more focused patches so that they're easier to review by peers.

As a mid-level user of git (I've been using it for nearly three years but in very rudimentary ways) my way of achieving this goal was to have a separate local branch for each part of an overall task. So, for example, I would have branches like:

TaskName-1-FirstPartOfTask
TaskName-2-SecondPartOfTask
TaskName-3-ThirdPartOfTask

and so on. And as I would work, if I needed to change something in the first part of the task, I would checkout that local branch and do my edits. Then I would go to each branch after it and rebase them. Most times it was fine. But every so often there would be a need for me to merge changes. And if that happened then it would be repeated for every branch afterward in the chain.

A pain in the ass, right? Yep!

But, thanks to a coworker, I have a better work flow for doing this. And that's done using git rebase -i and editing patches in place. And with this newer workflow it lets me work in a single branch, editing historical patches in that branch and makes life overall easier.

So, now, instead of having a separate branch for each part of the task, I can just work on them in a logical order. When I finish each part of the task I can simply use git commit -a to commit those changes.

If, after multiple such changes are committed, I realize I need to change something in a previous commit, I can use git rebase -i HEAD~n to bring up all changes including the one I want to fix.

So, for example, if I want to fix a commit that's three back I can use git rebase -i HEAD~3 to bring up the list, which looks like this:

pick 7376174 Creates the new POJO named Farkle.
pick 610412e Annotates the fields in Farkle.
pick 8d622f5 Creates the FarkleDAO Hibernate implementation.

# Rebase 72c5b6c..8d622f5 onto 72c5b6c
#
# 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
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

The patches are listed from oldest to newest.

Git lets your edit in place, so in this case I would modify the entry for that first entry and change "pick" to "edit":

edit 7376174 Creates the new POJO named Farkle.


which rolls back temporarily the commits after this entry and drops me back at the command line.

Now I can modify things on this commit, doing whatever changes I want. Once I finish I just do git commit -a --amend to save those changes, and then git rebase --continue to put all of the other changes back on top of it.

This makes life a lot easier. Sure there may be cases where you'll still have to do some merges, but they will be fewer and farther in between that in you used my previous work flow.

No comments:

Post a Comment