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.)