15

2 つの Subversion リポジトリがあり、それぞれに 1 つのプロジェクトがあります。そう:

svn://server/svn/project_a
svn://server/svn/project_b

それらは別々のプロジェクトであり、完全に別々のコミット履歴を持つ別々のリポジトリにあります。プロジェクト A がr1, r2, ... r100あり、プロジェクト B があるr1, r2, ... r400

最終的には、これら 2 つの SVN リポジトリを 1 つの Git リポジトリにマージしたいと考えています。マージを Git で実行できるか、最初に 3 番目の一時的な SVN リポジトリで実行する必要があるかにかかわらず、最終的には次のことを確認したいと考えています。

git://server/svn/projects/

これは、プロジェクト A とプロジェクト B の両方を含むリポジトリです。これらは、次のように別々のフォルダーに保存されます。

git://server/svn/projects/project_a
git://server/svn/projects/project_b

そのため、2 つを「マージ」する競合は発生しません。この回答を完璧に使用して、単一の SVN プロジェクトを単一の Git プロジェクトに転送し、コミット履歴を含めることができました。

2 つの SVN プロジェクト A と B を 1 つの Git リポジトリにマージしたいのですが、コミットは日付ごとにマージしたいと考えていますすなわち:

8b8dad: Project A, r1 (first commit in Git)
dbdffe: Project B, r1 (child of previous)
0ae7f7: Project B, r2 ...
615b51: Project A, r2 ...
916e59: Project A, r3 ...
85f241: Project B, r3 ...

これは可能ですか?2 つの SVN リポジトリを 1 つにマージしてから、Git にインポートする必要がありますか? それとも、それらを別々にして、Git インポート中にマージを実行する方が簡単ですか?

4

3 に答える 3

4

最終的に行ったことは次のとおりです。

ステップ 1: SVN リポジトリを一時的な SVN リポジトリにマージする

これには、SVN リポジトリ (作業コピーではない) へのアクセスが必要です。

まず、マージする各リポジトリのダンプ ファイルを作成します。

svnadmin dump project_a > dumps/a.dmp
svnadmin dump project_b > dumps/b.dmp
svnadmin dump project_c > dumps/c.dmp

次に、マージされたリポジトリを格納する新しいリポジトリを作成します。

svnadmin create svn-temp-project

このリポジトリを作業コピーにチェックアウトし、プロジェクトのサブディレクトリを作成する必要があることに注意してください。そうしないと、ダンプのロードが機能しません。

svn co file:///var/svn/svn-temp-project svn-temp-project-wc
cd svn-temp-project-wc
mkdir project_a
mkdir project_b
mkdir project_c
svn add . --force
svn ci -m "Added initial project directories."

次に、個々のダンプ ファイルをそれぞれ固有の (!!) プロジェクト ディレクトリにロードできます。

svnadmin load svn-temp-project --parent-dir project_a < dumps/a.dmp
svnadmin load svn-temp-project --parent-dir project_b < dumps/b.dmp
svnadmin load svn-temp-project --parent-dir project_c < dumps/c.dmp

これで 3 つのマージされた SVN リポジトリができました。

ステップ 2: 3 マージされた SVN リポジトリを Git リポジトリに移行する

次の手順は、ローカル マシンで実行できます。サーバーで実行する必要はありません。

まず、git-svn が各コミットの作成者を特定するために使用できる authors.txt ファイルを作成します。私が使用した:

someguy = Some Guy <someguy@yourcompany.com>
...
(no author) = no_author <no_author@no_author>

この作成者ファイルを配置すると、次のことができます。

cd projects/
mkdir my-git-repository
cd my-git-repository
git svn init https://svn.mycompany.com/svn/svn-temp-project --no-metadata
git config svn.authorsfile ../authors.txt
git svn fetch

ステップ 3: クリーンアップ

この方法はコミット履歴のマージにはうまく機能します、SVN のようなディレクトリになってしまいます。

repo/project_a/trunk
repo/project_a/branches
repo/project_a/tags
repo/project_b/trunk
repo/project_b/branches
repo/project_b/tags
...

したがって、プッシュする前に、タグ/ブランチを Git に移行する必要があります。私たちはこれをしませんでした。タグを取得する他のソースがあり、これらのプロジェクトのブランチがなかったため、タグを保持する必要はありませんでした。

branchesディレクトリとディレクトリを削除した後tags、内容をtrunk/1 つ下のレベルに落としたので、すべてがプロジェクト固有の「ルート」レベルになりました。

于 2013-05-06T21:43:42.443 に答える
3

Linuxシェルで行うことは次のとおりです(テストされていません):

  1. それぞれを独自のgitリポジトリに変換します
  2. 空の最初のコミットで 3 番目の git リポジトリを作成する

    git ci --allow-empty -m'Add empty, initial commit'

  3. 空のレポに、各レポをリモートとして追加します

    git remote add repoA 'path/to/git/repoA'
    git remote add repoB 'path/to/git/repoB'

  4. リポジトリを空のリポジトリにフェッチします (これにより、すべてのオブジェクトが 1 つのリポジトリに取得されます)

    git fetch repoA
    git fetch repoB

  5. Unix タイムスタンプ (1970 年 1 月 1 日からの秒数) で始まる各リポジトリのコミットのリストを取得する

    git --no-pager log --format='%at %H' master >repoACommits
    git --no-pager log --format='%at %H' master >repoBCommits

  6. それらの両方を 1 つの (タイムスタンプで) 並べ替えられたリストに分類し、タイムスタンプを選別します。

    cat repoACommits repoBCommits | sort | cut -d' ' -f2 >orderedCommits

  7. 新しいレポで、リストを実行し、それぞれをチェリーピッキングします(おそらくマスターするため)

    git co master
    cat orderedCommits | while read commit; do git cherry-pick $commit; done

これはすべて理論上の話ですが、うまくいくと思います。2 つの間にマージの競合がある場合はどうなるかわかりません。whileが停止するのか、継続しようとして失敗するのかはわかりません。

レポの作業のそれぞれを最終フォルダーの個別のフォルダーに保持したいとおっしゃっていたことに気づきました。git filter-branch最初に各レポを個別に実行し、コミットごとに追加されたものをフォルダーに移動する作業を行うには、神秘的で強力な機能が必要です。SOでまだ回答されていない場合、おそらく新しい質問の価値があります。

于 2013-05-04T04:22:49.113 に答える