Monday, 13 June 2011

svn switch --relocate for a git-svn repo

Today, to my great annoyance, I discovered that if you're using git locally hooked up to a remote subversion server and you find yourself in a position where, if using svn only, you'd want to do svn switch --relocate ( for example, if the server has its address changed), git-svn presents you with a world of pain. Since the svn url gets baked into all the commit logs, and these are used to keep your git repo synced with the svn server, you can't just edit the .git/config file and change the url as you'd do for a normal git remote.

I'm not going to claim any credit for figuring out what the hell was going on; after a brief period of free-form jazz swearing at the machine with twenty-odd suddenly disconnected git repositories, I found a handy post explaining how to resolve this bloody annoying state of affairs.

Since I had a lot of repos to switch, I thought it best to write a little bash script to sort it all out for me. Here it is so you, too, can ease the pain of the unexpected subversion repository renaming with less hassle. Or, alternatively, utterly hose your repo. Seriously, this rewrites your commit logs and could do all kinds of the nasty. At least back up your local repository first before using it. And even then, you use it at your own risk, etc, etc:

# git-svn-switch
# by Justen Hyde based on this blog post:
# Use at your own risk. For the love of cthulhu, back
# your repo up before letting this loose on it.

if [ $# -ne 1 ]
  echo "Usage: `basename $0` {new subversion url}"
  exit -1

if [[ $1 = "--help" || $1 = "-h" ]]
  echo "Usage: `basename $0` {new subversion url}"
  echo " Changes the url of the subversion repository a git-svn repo is connected to." 
  echo " Analogous to svn switch. Potentially a weapon of mass destruction. Use with care."
  echo " Run this from within your git repo. You only need one argument: the new url of the svn repo."
  echo " git-svn-switch will attempt to verify that the url is at least a svn repo before starting the switch"
  echo " but don't depend on it to stop you from doing summat daft."
  exit 1

# get the current subversion url
SRC=`git svn info --url`
if [ -n "$SRC" ]
  FROM=`echo $SRC | sed "s|/trunk||"`
  REPO=`svn info $1`
  echo "Checking $REPO is actually a subversion repository..."
  if [ -n "$REPO" ]
    echo "The new URL looks valid."
    echo "Rewriting the git history with the new url..."
    SED_FILTER="sed 's;git-svn-id: "$FROM";git-svn-id: "$1";g'"
    git gc
    git filter-branch --msg-filter "$SED_FILTER" $(cat .git/packed-refs | awk '// {print $2}' | grep -v 'pack-refs')
#Couple of pointless checkouts - on some repos the log changes seem to need flushing by an operation like this
    git checkout trunk
    git checkout master
    echo "Rebuild git-svn internals and updating the repo"
    rm -rf .git/svn 
    sed -i~ 's|'$FROM'|'$1'|g' .git/config
    git svn rebase
    echo "Error: $1 Does not appear to be a subversion repository."
  echo "Error: This doesn't appear to be a git working directory, or it's a git repo that hasn't been created using a git-svn bridge"
EDIT: I've added a couple of checkouts to the script which don't serve any obvious purpose, just before rebuilding the internals. This is because I've found on some repos that for some reason git doesn't seem to notice the logs have been rewritten (git log shows you logs with the old address, but attempt to re-run the rewrite and git will behave as if the logs have been rewritten). Subversion seems to get the old version of the logs in this case, and so any operations trying to use the subversion address in the logs fail. Doing these checkouts seems to flush the logs properly and allow git-svn to behave as expected. Weird. Unfortunately, this does mean you have to have a reasonably standard repository structure (i.e., one that actually has a trunk that can be checked out) or the script will fail.

1 comment:

Thomas Ferris Nicolaisen said...

Thanks for this! I've made some adaptions to make it work with git-svn clones where there are tags and branches (--stdlayout), as these have a higher-up svn url configured: