Git and code review

I recently discovered git, a version control system. After learning and trying it out for a few weeks, I am impressed by its efficiency, flexibility, and capability.

For a project I am working on at VMware, it uses subversion as a centralized code repository for multiple developers. Git has good support of subversion through the git-svn command. However, git does not have a built-in support for doing side-by-side code reviews. This document describes my code development process with git and my attempt to customize git to support side-by-side code reviews.

Prepare the repository

First, I use git-svn to clone a copy of the source code from the subversion server.

git svn clone svn+ssh://server.name/svn/valgrind-vassert

Once this is done, a local git repository of the valgrind-vassert project is cloned from the subversion server. This is also my working directory for modifying and testing the code. If I need more than one working directories, I can just make more local clones using git (not git svn).

git clone valgrind-vassert work_dir_2

Note that only the first local directory (cloned by git svn) can talk to the remote subversion server. This is what I want. If I have multiple changes I am working on, I can easily "git push" my changes from the local clones to the svn clone and then commit it to the subversion server. I may have to use branches. But it is very easy to use branches with git. (Check out this book! [pdf])

View the commit history

The local repository has full history of the project. I can get the commit history from either git log or git svn log command, even when I am offline.

$ git log -1
commit 5c9382278d8517a158d0507ede9c7b5d23cb10ac
Author: mqiu 
Date:   Tue Aug 19 12:56:06 2008 +0000

    ...
    
    git-svn-id: svn+ssh://localhost/svn/valgrind-vassert@22 aec7b058-a04d-4682-b3b4-202a7a29d950

$ git svn log -1
------------------------------------------------------------------------
r22 | mqiu | 2008-08-19 05:56:06 -0700 (Tue, 19 Aug 2008) | 3 lines

...

------------------------------------------------------------------------

To get a condensed view of the history, I put the following alias in my .bashrc file.

alias gl="git log --pretty=oneline"

Make code changes and commit locally

Now, I can edit the source code and start testing. After a while, I may have forgotten what file I have changed. I ask git with git diff command. git diff shows detailed changes by default. I put the following alias in my .gitconfig and .bashrc to change the output to show a list of files that I have modified. (I also specified my name and email in .gitconfig.)

# in .gitconfig
[alias]
       changes=diff --name-status -r
[user]
       name = Your Name
       email = email@address

# in .bashrc
function gd {
   # describe a change
   if [ -z "$1" ]; then
      git changes
   elif [ "$1" = "--cached" ]; then
      git changes --cached
   else
      git changes "$1^" "$1"
   fi
}

Now, I simply type gd and it shows a list of files I have changed.

$ gd
M       bashrc
M       vimrc

Git allows me to put some changes in a staging area, called the index. By using "git add .", it puts all changes to the staging area from my modified copy. If I want to select only part of the changes (includes partial changes from a single file!), I can use git add -i command to invoke the interactive mode.

Once I am happy with the code changes I have in the staging area, I can commit the change locally into git using git commit. Don't worry, this does not publish the change to the subversion yet. This local commit allows me to write a decent commit message to describe what my change is about in detail. I use a commit message template file, which reminds to put title, details, testing and reviewer information. This commit message can be changed later, after my change is reviewed by other developers.

# in .gitconfig
[commit]
       template = /path/to/.git.commit.template
# in .git.commit.template
put your commit message title

put your commit message details

testing done:
reviewers:

Send out a code review

To support side by side code review, I wrote a couple of scripts for generating and viewing side by side diff files using vimdiff. The first script is git_diff_to_review.py. This script generates a directory in /tmp from the information obtained from "git diff". To use it, put this "make review" function (gr) in my .bashrc file. Then, invoke gr with the commit ID to make a review tarball.
# in .bashrc
function gr {
   if [ -z "$1" ]; then
      reviewDir=`GIT_EXTERNAL_DIFF=git_diff_to_review.py git diff`
   elif [ "$1" = "--cached" ]; then
      reviewDir=`GIT_EXTERNAL_DIFF=git_diff_to_review.py git diff --cached`
   else
      reviewDir=`GIT_EXTERNAL_DIFF=git_diff_to_review.py git diff "$1^" "$1"`
   fi
   if [ -d "$reviewDir" ]; then
      theDir=`dirname $reviewDir`
      theBase=`basename $reviewDir`
      pushd "$theDir" >/dev/null
      tar zcf "$theBase.tgz" "$theBase"
      popd >/dev/null
      echo "$reviewDir.tgz"
      rm -rf $reviewDir
   else
      echo "something wrong with $reviewDir"
   fi
}

$ gr 92a72512369ec7398af0e9437172ec31a9562879
/tmp/20080820_08339.tgz

The second script is diff_view.py, which is then used to view the generated review tarball using vimdiff. One can use the number in [] to view specific file in the changeset.

$ diff_view.py /tmp/20080820_08339.tgz
bashrc [0]
vimrc [1]
Next/Previous/exit(N/p/x/)?

As a shorthand, I use the following bash function to view both committed and uncommitted changes in development. This enables me to quickly review my own changes within vimdiff. If I pass an extra "-l" option to diff_view.py it will find the local copy of the modified file and enable me to change the local copy in vimdiff as-I-go.

function gdd {
   if [ -z "$1" ]; then
      reviewDir=`GIT_EXTERNAL_DIFF=git_diff_to_review.py git diff`
   elif [ "$1" = "--cached" ]; then
      reviewDir=`GIT_EXTERNAL_DIFF=git_diff_to_review.py git diff --cached`
   else
      reviewDir=`GIT_EXTERNAL_DIFF=git_diff_to_review.py git diff "$1^" "$1"`
   fi
   diff_view.py $reviewDir
   rm -rf $reviewDir
}

With this two scripts, I can generate a code review directory and make a tarball and send it, along with my local commit message, to the other developers working on the same project. Usually, if my peer developers have shared file space with me, I can just send them the path to the review tarball.

After the code is reviewed

After the I get my feedback on the code review. I can change the code to address the concerns and suggestions made by other developers. But wait, didn't I already commit my old change locally? No problem! Git allow me to change my last commit since I have not published the commit to the subversion server yet. The secret command is "git commit --amend". I can use the opportunity to update my commit message too.

Commit to and update from SVN

Once my code is reviewed, I can "push" the local commit to the subversion server by using git svn dcommit. To get updates from the subversion server, I use git svn rebase.

alias p5s="git svn dcommit"
alias psr="git svn rebase"

Comments:

Have comments? Email them to me. I will post it here.
On Wed, Jun 17, 2009 at 6:58 PM Pablo From Argentina wrote:
thought about the possibility of using git patches sent by mail to be analyzed by reviewing the code? did you know some open software tool for code reviewing ? Thanks

On Wed, Jun 17, 2009 at 7:10 PM Min wrote:
What I do is to send the resulting *.tgz file generated from "gr" to friends for reviewing. It is not like "git am" command, which works on mailboxes directly, I think.