git svn provides a way to check out Subversion repositories and interact with them using the git interface. It is useful to avoid the mental leap of working with another revision control system tool occassionally when, eg, dealing with RANCID repositories of switch and router configuration (historically RANCID only supported CVS, and then more recently CVS and Subversion; recent versions do support git directly, but not all my clients are using recent enough versions to have direct git support).

Unfortunately the git svn command interaction is still fairly different from "native git" repository interaction, which causes some confusion. But fortunately with a few kludges you can hide the worst of this from day-to-day interation.

Starting at the beginning, you "clone" the repository with something like:

git svn clone svn+ssh://svn.example.com/var/lib/rancid/SVN/switches

(using a specific path within the Subversion repository as suggested by Janos Gyerik, rather than fighting with the git svn/Subversion branch impedance mismatch).

After that you're supposed to use:

git svn rebase

to update the repository pulling in changes made "upstream"; "git pull" simply will not work.

However my fingers, trained by years of git usage, really want to type "git pull" -- if I have to type something else to update, then I might as well just run svn directly. So I went looking for a solution to make "git pull" work on git svn clone (which I never changed locally).

An obvious answer would be to define a git alias (see Git Aliases), but sadly it is not possible to define a git alias that shadows an internal command, and it appears this is considered a feature. I could call the alias something else, but then I am back at "have to type something different, so I might as well just run svn" :-(

A comment on the same Stack Overflow thread suggests the best answer is to define a bash "shell function" that intercepts calls to git and redirects commands as appropriate. In my case I want "git pull" to run "git svn rebase" if (and only if) I am in a git svn repository. Inspecting those repositories showed that one unique feature they have is that there is a .git/svn directory -- so that gave me a way to tell which repositories to run this command on. Some more searching turned up git rev-parse --show-toplevel as the way to find the .git directory, so my work around could work no matter how deep I am in the git svn repository.

Putting these bits together I came up with this shell function that intercepts "git pull", checks for a git svn repository, and if we are running "git pull" in a git svn repository runs "git svn rebase" instead -- which does a fetch and update, just like "git pull" would do on a native repository:

function git {
    local _GIT

    if test "$1" = "pull"; then
        _GIT=$(command git rev-parse --show-toplevel)
        if test -n "${_GIT}" -a -e "${_GIT}/.git/svn"; then
            command git svn rebase
        else
            command git "$@"
        fi
    else
        command git "$@"
    fi
}

(The "command git" bit forces bash to ignore the alias, and run the git in the PATH instead, preventing infinite recursion -- without having to hard code the path of the git binary.)

Now "git pull" functions like normal in git repositories, and magically does the right thing on git svn repositories; and all other git commands run as normal.

It is definitely a kludge, but avoiding a daily "whoops, that is the repository that is special" confusion is well worth it. (git log and git diff seem to "just work" in git svn repositories -- which are the main two other commands I end up using on RANCID repositories.)