Git: Evolution of a topic branch and how to review it

The key to reviewing topic branches in Git, i.e. story-related branches in case of Scrum, is Git’s rev-list command.

Let us suppose we’ve got the following commit/branch history:

   B---C-----D---E--
  /        \        \
 A---F---G--+----H---+--I

Here, the series of commits B-C-D-E happened on a topic branch that was created when commit A was the tip (or HEAD, in git-speak) of the master branch. The master branch continued with commits F, G – when a remerge had to occur. After another commit H on top the master branch we got the final merge with the topic branch and another commit I.

In this scenario, reviewing the series of commits B-C-D-E plus the merge would be a piece of cake but what happens if F-G-H-I was forming the topic branch with master getting merged in from time to time? We’d have to tell apart commits, i.e. know not to review F-G-H as well. It might seem trivial here but figure F-G-H was really hundreds of commits with even more merges in between.

In this case, rev-list offers the inconspicuous (or even misleadingly
named) option --first-parent:

Follow only the first parent commit upon seeing a merge commit. This
option can give a better overview when viewing the evolution of a
particular topic branch, because merges into a topic branch tend to be
only about adjusting to updated upstream from time to time, and this
option allows you to ignore the individual commits brought in to your
history by such a merge.

Creating the review patch

To cut a long story short,

git rev-list --first-parent --reverse HASH-OF-A...HASH-OF-I

does just what you need and prints the series of hashes for F-G-+-H-+-I. Similarly,

git rev-list --first-parent --reverse HASH-OF-A...HASH-OF-E

gives you B-C-D-E so it even works for a simple use case without in-between merges. Adding --no-merges will additionally skip the merge commits (+).

Make this an alias in your ~/.gitconfig by adding this:

[alias]
        review-branch = rev-list --reverse --first-parent

Now, in order to use this for a review, all that’s left to do is using such a list of hashes to retrieve their diffs:

git show $(git review-branch HASH-OF-A...HASH-OF-I) > review.patch

or, alternatively

git review-branch HASH-OF-A...HASH-OF-I \
  | xargs -n1 git show > review.patch

In order to just get all files involved throughout all commits, you could add some funny action, e.g.

git show $(git review-branch HASH-OF-A...HASH-OF-I) \
  | grep -E '^(--- a|\+\+\+ b)/' \
  | perl -ple '$_ =~ s/.......*)/$1/' \
  | sort | uniq > files

Reviewing the changes

In order to make best use out of the review patch files created above, I’ve been using Review Board, which is Free Software as well, of course.

Review Board diff viewer

Review Board uses patch files as its native format and even supports loading missing context code from the repository, if set up to do so.

Conclusion

Git kicks ass and I’ve got Balls of Steel™.