git svn での switch のやりかた

最初は /trunk だった svn リポジトリが、プロジェクトが進んでいくと、「やっぱりモジュール分割した方がいいよね」ってことで //trunk みたいにリポジトリの階層構造が変わることってありますよね。

そんなときに、普通に svn でやっていた場合は

> svn switch 旧パス 新パス

で手元のワーキングコピーを追従できるわけですが、git svn で追いかけている時はどうするか?

> git svn switch

は現時点では「switch なんぞ無い」とエラーになります。

ではどうするか、ということですが、

  1. .git/config の中の URL を新しい URL に書き換える
  2. git svn fetch
  3. 1.で書き換えたURLを一旦元に戻す(svn/hoge/proj -> svn/hoge)
  4. git svn rebase -l
  5. 再び 1. をする。書き換える (svn/hoge -> svn/hoge/proj)
  6. git svn rebase がちゃんと動くよ!

という手順でswitch相当の操作になります。
git-svn - switch to a different a svn url - theAdmin.org - Redmine Development by the Redmine Guy, Eric Davis

条件:svn リポジトリの構造変化 commit (move の commit) の後にファイルの内容変更など、
別の commit がされていること。(上の例だと svn/hoge/proj/a に変更commitが入ってる、など)
構造変化 commit の直後だと git svn が変化検出できないっぽいです。


実際にはこんな感じになります。

(/hoge/trunk → /hoge/proj/trunk とリポジトリの構造が変更されたところから)
リポジトリ構造が変わったので、svn update はエラーになりますが、switch で追尾できます。

C:\a\svn2>svn update
svn: Target path '/trunk' does not exist

C:\a\svn2>svn switch http://repository.example.com/svn/hoge/proj/trunk .
リビジョン 4 です。

C:\a\svn2>svn info
パス: .
URL: http://repository.example.com/svn/hoge/proj/trunk
リポジトリのルート: http://repository.example.com/svn/hoge
リポジトリ UUID: b656e3cd-a05a-4bf8-8265-f732596ee69d
リビジョン: 4
ノード種別: ディレクトリ
準備中の処理: 特になし
最終変更リビジョン: 4
最終変更日時: 2011-02-03 10:58:14 +0900 (木, 03 2 2011)

git ではどうなるかというと、こんな感じで rebase がエラーになります。

C:\a\git>git svn rebase
W: Ignoring error from SVN, path probably does not exist: (160013): Filesystem h
as no item: REPORT request failed on '/svn/hoge/!svn/bc/4': File not found: revi
sion 4, path '/trunk'
W: Do not be alarmed at the above message git-svn is just searching aggressively
for old history.
This may take a while on large repositories
Current branch master is up to date.

ここで .git/config の svn-remote "svn" セクションを書き換えてやります。
/hoge を /hoge/proj にします。

[svn-remote "svn"]
	url = repository.example.com/svn/hoge
	fetch = trunk:refs/remotes/trunk
	branches = branches/*:refs/remotes/*
	tags = tags/*:refs/remotes/tags/*

[svn-remote "svn"]
	url = repository.example.com/svn/hoge/proj
	fetch = trunk:refs/remotes/trunk
	branches = branches/*:refs/remotes/*
	tags = tags/*:refs/remotes/tags/*

のようにします。


書き換えたら git svn fetch。

C:\a\git>git svn fetch
        D       a
        D       b
        A       a
        A       b
        A       c
r5 = 639d40c95099eee55b083f1522b668752ba35e4f (refs/remotes/trunk)

ここで、何も出力されない場合は svn リポジトリ側が構造変化だけで止まってるので、ファイルに変更をして svn 側で commit するか、他の誰かがファイルを弄ってくれるまで待ちます。


fetch できたら一旦 .git/config を元の状態に戻します。
/hoge/proj → /hoge に戻します。


それから git svn rebase -l。 -l を付けるがポイントで、先の fetch で引っ張ってきた分を使って rebase します。

C:\a\git>git svn rebase -l
First, rewinding head to replay your work on top of it...
Fast-forwarded master to refs/remotes/trunk.


うまく rebase できたところで、再び .git/config を書き換えます。
/hoge → /hoge/proj に再変更。

そして git svn rebase

C:\a\git>git svn rebase
Current branch master is up to date.

これで git svn switch 相当の構造追従ができます。