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
K
throughへの唯一の変更N
は、そのK
親ポインターが変更されることです。したがって、すべての SHA-1 識別子が変更されます。コミット メッセージ、作成者、タイムスタンプなどは変わりません。
filter-branch を使用して 3 つ以上のリポジトリをマージする
R1 (最も古いもの) から R5 (最も新しいもの) まで、実行するリポジトリが 2 つ以上ある場合は、git reset
とgit 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/grafts
R2/master
K
R1/master
D
R2/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
。これが機能する唯一のケースは、 と のツリーD
がK
同一である場合です。
(別のわずかな違いは、 throughgit rebase
のコミッター情報を変更するのに対し、変更しないことです。)K'
N'
git filter-branch