15

1995 年から 1997 年と 1999 年から 2013 年の 2 つの製品開発期間からのコミットを含む2 つの git リポジトリR1とがあります。R2(既存の RCS および CVS リポジトリを Git に変換して作成しました。)

R1:
A---B---C---D

R2:
K---L---M---N

プロジェクトの線形履歴の正確なビューを含む単一のリポジトリに 2 つのリポジトリを結合するにはどうすればよいですか?

A---B---C---D---K---L---M---N

R1と の間のR2ファイルが追加、削除、および名前変更されていることに注意してください。

空のリポジトリを作成して、その内容をマージしてみました。

git remote add R1 /vol/R1.git
git fetch R1

git remote add R2 /vol/R2.git
git fetch R2

git merge --strategy=recursive --strategy-option=theirs R1
git merge --strategy=recursive --strategy-option=theirs R2

ただし、これにより、リビジョンDではなくリビジョンにあった最後のファイルが残りKます。合成コミットを作成して、マージ間の余分なファイルを削除することもできますが、これは私にはエレガントではないようです。さらに、このアプローチにより、最終結果には実際には発生しなかったマージが含まれます。

4

4 に答える 4

15

git filter-branch の使用

git-filter-branch man ページから直接トリックを使用します。

最初に、以前と同じように、元の 2 つのリポジトリをリモートとして新しいリポジトリを作成します。どちらもブランチ名「master」を使用していると想定しています。

git init repo
cd repo
git remote add R1 /vol/R1.git
git fetch R1
git remote add R2 /vol/R2.git
git fetch R2

次に、「マスター」(現在のブランチ) を R2 の「マスター」の先端にポイントします。

git reset --hard R2/master

これで、R1 の「マスター」の歴史を最初に移植することができます。

git filter-branch --parent-filter 'sed "s_^\$_-p R1/master_"' HEAD

Dつまり、 と の間に偽の親コミットを挿入しているKため、新しい履歴は次のようになります。

A---B---C---D---K---L---M---N

Kthroughへの唯一の変更Nは、そのK親ポインターが変更されることです。したがって、すべての SHA-1 識別子が変更されます。コミット メッセージ、作成者、タイムスタンプなどは変わりません。

filter-branch を使用して 3 つ以上のリポジトリをマージする

R1 (最も古いもの) から R5 (最も新しいもの) まで、実行するリポジトリが 2 つ以上ある場合は、git resetgit filter-branchコマンドを時系列で繰り返します。

PARENT_REPO=R1
for CHILD_REPO in R2 R3 R4 R5; do
    git reset --hard $CHILD_REPO/master
    git filter-branch --parent-filter 'sed "s_^\$_-p '$PARENT_REPO/master'"' HEAD
    PARENT_REPO=$CHILD_REPO
done

移植片の使用

への--parent-filterオプションをfilter-branch使用する代わりに、グラフトメカニズムを使用することもできます。

R2/master(つまり、より新しい) の子として追加する元の状況を考えてみましょうR1/master。前と同様に、現在のブランチ ( master) を の先端に向けることから始めR2/masterます。

git reset --hard R2/master

ここで、コマンドを実行する代わりに、 ( ) の「ルート」(最も古い) コミットを( ) の先端(最新) コミットにリンクするfilter-branch「移植」(偽の親) を作成します。( のルートが複数ある場合、以下はそのうちの 1 つだけをリンクします。).git/info/graftsR2/masterKR1/masterDR2/master

ROOT_OF_R2=$(git rev-list R2/master | tail -n 1)
TIP_OF_R1=$(git rev-parse R1/master)
echo $ROOT_OF_R2 $TIP_OF_R1 >> .git/info/grafts

この時点で、履歴を (たとえば、 を通じてgitk) 見て、それが正しいかどうかを確認できます。その場合は、次の方法で変更を永続的にすることができます。

git filter-branch

最後に、移植ファイルを削除することですべてをクリーンアップできます。

rm .git/info/grafts

グラフトを使用することは、 を使用するよりも手間がかかる可能性が--parent-filterありますが、1 つの で 3 つ以上の履歴をグラフトできるという利点がありfilter-branchます。( で同じことを行うこともできます--parent-filterが、スクリプトは非常に速く非常に醜くなります。) また、変更が永続的になる前に変更を確認できるという利点もあります。見栄えが悪い場合は、移植ファイルを削除して中止してください。

2 つ以上のリポジトリを接ぎ木でマージする

R1 (最も古い) から R5 (最も新しい) までのグラフト メソッドを使用するには、グラフト ファイルに複数の行を追加するだけです。(コマンドを実行する順序echoは重要ではありません。)

git reset --hard R5/master

PARENT_REPO=R1
for CHILD_REPO in R2 R3 R4 R5; do
    ROOT_OF_CHILD=$(git rev-list $CHILD_REPO/master | tail -n 1)
    TIP_OF_PARENT=$(git rev-parse $PARENT_REPO/master)
    echo "$ROOT_OF_CHILD" "$TIP_OF_PARENT" >> .git/info/grafts
    PARENT_REPO=$CHILD_REPO
done

git rebase はどうですか?

他のいくつかは、上記のコマンドのgit rebase R1/master代わりに使用することを提案しています。git filter-branchこれは、空のコミット間の差分を取得し、Kそれを に適用しようとしますD。結果は次のようになります。

A---B---C---D---K'---L'---M'---N'

K'これにより、マージの競合が発生する可能性が高く、ファイルが との間で削除された場合、で偽のファイルが作成される可能性さえありDますK。これが機能する唯一のケースは、 と のツリーDK同一である場合です。

(別のわずかな違いは、 throughgit rebaseのコミッター情報を変更するのに対し、変更しないことです。)K'N'git filter-branch

于 2013-04-04T04:06:06.163 に答える
2

元のポスターには次のように記載されています。

R1:
A---B---C---D

R2:
K---L---M---N

プロジェクトの線形履歴の正確なビューを含む単一のリポジトリに 2 つのリポジトリを結合するにはどうすればよいですか?

プロジェクトの線形履歴の正確なビューを含む単一のリポジトリに 2 つのリポジトリを結合するにはどうすればよいですか?

A---B---C---D---K---L---M---N

R1R2の間で、ファイルが追加、削除、および名前変更されていることに注意してください。

したがって、新しいレポ の最初のコミット がK古いレポ の最後のコミットと同一またはわずかに変更されている場合、の履歴を にDフェッチしてから、 のコミット グラフをグラフにリベースできることは確かです。から:R1R2R2R1

# From R2
git fetch R1
git checkout master
git rebase --onto R1/master --root

非線形履歴 (マージコミットがある場合)

R2それは、グラフが線形であると仮定しています。マージ コミットがある場合は、マージ コミットを保持することを指定して、同じことを試みることができます。

git rebase --preserve-merges --onto R1/master --root

ただし、リベースしているマージのいずれかで競合を解決する必要があった場合は、おそらくそれらを再度解決する必要があり、おそらく面倒です。

根本的に異なる 2 つの歴史を組み合わせる?

元のポスターは次のように述べています。

R1R2の間で、ファイルが追加、削除、および名前変更されていることに注意してください。

上で指摘したように、新しいリポジトリの最初のコミットがK古いリポジトリの最後のコミットと同じか、わずかに異なる場合、単純なリベースが機能するはずDです。Kが実際に と大幅に異なる場合、同じリベースが正常に機能するかどうかはわかりませんDK最悪の場合、リベースの最初の適用時に多くの競合を解決する必要があると思います。

ドキュメンテーション

于 2014-07-22T00:09:07.097 に答える
1

これは私がやったことです:

git init
git remote add R1 /vol/R1.git
git fetch R1
git remote add R2 /vol/R2.git
git fetch R2
git co -B master R2/master
git rebase R1/master
git push -f
于 2013-06-07T01:09:18.073 に答える
0

必要なのは次のとおりです。 git rebaseその後に、リベースするブランチを続けます。

簡単に言えば、リベースはブランチのすべてのコミットを巻き戻し、リベースしているブランチのコミットとマージします。

2 つのブランチ間の違いによっては、競合が発生する可能性があります。しかし、他の方法を使用しても同じ競合を回避することはできません。

幸運を!

于 2013-04-04T04:12:51.607 に答える