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