web-dev-qa-db-ja.com

複数のsvnリポジトリを単一のgitリポジトリに移行します

ブランチとコラボレーションの観点から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で検索しました 12 ですが、私たちの問題の正確な解決策を見つけることができませんでした。

5
Shyru

1つの解決策は、svn2gitまたはgit svn(すでにgitに組み込まれている素敵な小さなツールです)を使用して各リポジトリを個別に生成し、それらをgit filter-branchと一緒にワイヤリングすることです。

  1. 各svnリポジトリのクローンを個別に作成します。
  2. ルートにしたいリポジトリで、他のリポジトリをリモートとして追加し、マージするブランチをそのリポジトリにフェッチします(ブランチには共通の履歴がないため、警告が表示されますが、これは予想されます)。
  3. インデックスフィルターを使用して、これらの新しいブランチでgit filter-branchを実行し、それらの新しいサブディレクトリを生成します。
  4. フィルタリングされたブランチをルートリポジトリの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は履歴を破壊します。一度変更すると、簡単に元に戻すことはできません。使用しているすべてのリポジトリコピーを必ずバックアップしてください。複雑な操作を終了して、パスで/を逃したことを発見した場合よりも悪いことはありません。
  • すべてをスクリプトに記述します。本格的なスキルがない限り、初めてこれを正しく行うことはできません。個々のステップを完了するたびにスクリプトを作成して、ステップの再実行が簡単になるようにします。また、1週間後に旗を台無しにしたことに気付いた場合は、すぐに複製できます。
  • EC2のクラスターコンピューティングインスタンスに$ 20を費やします。git filter-branchはCPUに非常に負荷がかかります。深い履歴のインデックスフィルターをローカル環境で実行するには数時間かかる可能性がありますが、AWSでのその時間の一部 cluster compute instance。 確かに、コストは 1時間あたり2ドル しかし、必要なのは数時間だけです。苦痛を軽減し、操作を簡単にするハードウェアで作成したスクリプトを使用してください。素敵なランチの値段がかかります。
5
Christopher

解決策の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
0
Dmitry Pavlenko

svn2git(私はではないエキスパートである)がネイティブでサポートされていない限り、これには極端なハッカーが必要になると思います。これはどういうわけか。

問題は、frontendのコミットが、backendのコミットから完全に独立していることです。どのコミットが単一のリポジトリ内のどのコミットにマップされるかを判断する実際の方法はありません。これにより、実際の選択肢は1つだけになります。履歴は、元のプロジェクトの履歴を表す2つのブランチがマージされて構成され、マージされると、新しいブランチが「より良いモデル」になります。

これからは、svn-frontendブランチにfrontendをインポートし、svn-backendブランチにbackendをインポートし、両方に独自の履歴が含まれていると仮定します。

最初の問題は、svn-backendbackend/ディレクトリにあるように修正することです。

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
0
alternative