ブランチとコラボレーションの観点からgitの優れた機能を使用できるように、svnからgitに永続的に移行したいと考えています。
現在のsvnリポジトリは次のようになっています
svnrepo/
frontend/
trunk
branches/
ng/
...
tags/
1.x
...
backend/
trunk
branches/
ng/
...
tags/
1.x
...
作業レイアウトは、フロントエンドプロジェクトをチェックアウトし、その中にバックエンドフォルダーを作成し、バックエンドプロジェクトをチェックアウトすることです。
ここで、gitに移行し、フロントエンドとバックエンドの分割を(別々のプロジェクトであるという観点から)あきらめたいと考えています。これは、利点よりも問題が多いためです。両方を単一のgitリポジトリに配置する必要があります。
svn2git を変換に使用したかった。残念ながら、最新の開発はすべてトランクではなくブランチで行われましたが、これはsvn2gitにとって問題ではないと思います。したがって、新しいgitリポジトリのレイアウトは次のようになります。
/ => svnrepo/frontend/branches/ng
/backend => svnrepo/backend/branches/ng
ここで、=>は「移行/変換元」を意味します。
変換のために、すべてのタグとブランチをsvnリポジトリからgitに変換する必要はありません。これは私たちにとって重要ではありません。ただし、重要なのは、branchs/ngディレクトリ内のすべてのファイルへのすべてのコミットの完全な履歴があり、トランクからの分岐と、それ以前にトランクで発生したすべてのコミットに戻ることです。そして、これらのすべてのコミットを、単一のgitリポジトリ内の前述のレイアウトに合わせます。これも可能ですか?そして、これをどのように実行しますか?
私はすでにグーグルとstackoverflowで検索しました 1 、 2 ですが、私たちの問題の正確な解決策を見つけることができませんでした。
1つの解決策は、svn2gitまたはgit svn
(すでにgitに組み込まれている素敵な小さなツールです)を使用して各リポジトリを個別に生成し、それらをgit filter-branch
と一緒にワイヤリングすることです。
git filter-branch
を実行し、それらの新しいサブディレクトリを生成します。master
(または必要なブランチ)にマージします。完全な履歴が保持されます。手順3のコマンドは次のようになります。
git filter-branch --index-filter '
git ls-files -s |
Perl -pe "s{\t\"?}{$&newsubdir/}" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD
魔法、そして私がこれをしなければならないときはいつも、魔法のように感じられますが、それはPerl
ステートメントです。 git filter-branch
はインデックスをフィルタリングしています各コミットで、すべてのblobパスの前に(つまり、作業ツリーのファイルパスを変更して) 'newsubdir'を付けています。パスを正確に取得するには、実験をする必要があるかもしれません。 このパスを前に歩いた人)から学んだいくつかのレッスン:
git filter-branch
は履歴を破壊します。一度変更すると、簡単に元に戻すことはできません。使用しているすべてのリポジトリコピーを必ずバックアップしてください。複雑な操作を終了して、パスで/
を逃したことを発見した場合よりも悪いことはありません。git filter-branch
はCPUに非常に負荷がかかります。深い履歴のインデックスフィルターをローカル環境で実行するには数時間かかる可能性がありますが、AWSでのその時間の一部 cluster compute instance。 確かに、コストは 1時間あたり2ドル しかし、必要なのは数時間だけです。苦痛を軽減し、操作を簡単にするハードウェアで作成したスクリプトを使用してください。素敵なランチの値段がかかります。解決策の1つは、両方のSVNプロジェクトリポジトリを2つのGitリポジトリに変換してから、1つのGitリポジトリを別のリポジトリの Gitサブモジュール として追加することです。
SVNリポジトリをGitリポジトリに変換するには、任意のgit-svnベースのスクリプトまたは SubGit を使用できます。最新のツールを使用して、単一のコマンドを実行します
$ subgit install path/to/svn/repository
変換されたgitリポジトリは、path/to/svn/repository/gitにあります。
次に、両方のGitリポジトリへのアクセスを設定し、一方をもう一方のサブモジュールとして追加します。
$ git clone <frontend_GitURL> frontend
$ git co
$ cd frontend
$ git submodule add -b ng <backend_GitURL> backend
svn2git
(私はではないエキスパートである)がネイティブでサポートされていない限り、これには極端なハッカーが必要になると思います。これはどういうわけか。
問題は、frontend
のコミットが、backend
のコミットから完全に独立していることです。どのコミットが単一のリポジトリ内のどのコミットにマップされるかを判断する実際の方法はありません。これにより、実際の選択肢は1つだけになります。履歴は、元のプロジェクトの履歴を表す2つのブランチがマージされて構成され、マージされると、新しいブランチが「より良いモデル」になります。
これからは、svn-frontend
ブランチにfrontend
をインポートし、svn-backend
ブランチにbackend
をインポートし、両方に独自の履歴が含まれていると仮定します。
最初の問題は、svn-backend
がbackend/
ディレクトリにあるように修正することです。
git checkout svn-backend
git filter-branch --index-filter '
git ls-files -s |
Perl -pe "s{\t\"?}{$&newsubdir/}" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
( このドキュメント 、および@Christopherによる回答を参照してください)
さて、これらにベースと同じコミットが何らかの形で含まれていない限り(svn2git
が事前定義されたベースコミットなどを作成しない限り)、作成する必要があります。どのブランチを開始するかは問題ではありません。
git symbolic-ref HEAD refs/heads/svn-base
rm .git/index
git clean -dxf
Gitは空のディレクトリを追跡できません。これがルートディレクトリに適用されるかどうかをテストしたことがありませんが、私の想定はそうではないので、空のgit ignoreファイルを作成してコミットします。
touch .gitignore
git add .gitignore
git commit -m "Base for SVN branches"
履歴を書き直してみましょう:
git rebase svn-base svn-frontend
git rebase svn-base svn-backend
あと少しで完了です。ここでマスターブランチを作成しましょう。すでに存在する場合:
git update-ref master "$head"
さもないと:
git branch master
それをチェックしよう:
git checkout master
最後に、マージ:
git merge svn-backend
古いブランチにタグを付けてから削除するのは良い考えです:
git checkout svn-frontend
git tag svn-frontend
git branch -d svn-frontend
git checkout svn-backend
git tag svn-backend
git branch -d svn-backend
git checkout master
git branch -d svn-base