あるブランチから別のブランチの履歴にいくつかのコミットを挿入したいので、次のようにします。
A - B - F - G - H - I - J (branch working)
\
C - D - E (old branch)
になる...
A - B - C - D - E - F - G - H - I - J (continue with branch working)
これまでに試したことは、複数の競合が発生して続行できなくなりましたが、後続のコミットの状態が同じである限り、それらについては気にしません。コミットFがコミットEの子になる方法を「知らない」という問題はありますか?
あなたは文字通り行うことができないことをそれはいくつかのコミット既存変える伴うだろうので、。この場合、既存のコミットF
はB
のハッシュIDを親として保存します。既存のコミットを変更することはできません。コミットは完全に読み取り専用です(実際、Gitの内部オブジェクトはすべて読み取り専用です)。
既存のコミットに関連付けられているスナップショットを変更せずに、代わりにのハッシュIDを親としてF
格納するふりをしたいとしますE
。あなたはこれを行うことができます、そしてあなたは多くの同様のことをすることができます。ただし、それらを見る前に、を見てみましょう。git rebase
使用に問題git rebase
ここでは、することによって、そのリベース機能でコピーするかのように行われた各コピーで、(今のところすべてが順調と良いです)コミットを1使って、git cherry-pick
(物事がうまく行くことを始めるところです)。1つのコミットを選択するために、Gitは次のことを行います。
コミットのスナップショットとコミットの親のスナップショットを比較します(例:J
vs I
、またはF
vs。)B
。これは、を実行git diff <hash1> <hash2>
するか、より単純にを実行することで実行できますgit show <hash2>
。
ここに示されている差分を、親(<hash1>
上記)から現在またはHEAD
コミットまでの差分とマージし、マージ結果を使用して新しいコミットを作成します。
元のコミットのメッセージを新しいコミットにコピーします。
新しいコミットは、いつものように、現在のブランチに移動し、ブランチを1つのコミットより長くし、HEAD
コミットを変更して新しいコミットに名前を付けます。
マージステップに加えて、Gitがスナップショット(コミット)をチェンジセット(差分)に変換しているという事実は、最終的なチェリーピックの結果が元のスナップショットとは大きく異なる可能性があることを意味します。これは、すべてのどのように異なるに依存し<hash1>
ているものは何でもからであるHEAD
あなたは、プロセスを起動したとき。
このgit rebase
コマンドは基本的に、一連のコミット全体を一度にチェリーピッキングするプロセスを自動化します。すべてのコピーの最後にgit rebase
、ブランチラベルが最後にコピーされたコミットを指すように強制します。これは、あなたがコピーできるようになるF
新にF'
その親されE
、そのコピーG
の新しいにG'
その親であるF'
、というように:
A--B--F--G--H--I--J <-- (original)
\
C--D--E <-- (target of rebase)
\
F'-G'-H'-I'-J' <-- branch
1git rebase
文字通り実行されることもgit cherry-pick
あれば、通常は同じ結果を生成するはずのメソッドを使用することもありますが、奇妙な場合にはそうではありません。
ここで、これらすべての代わりに、Gitが使用しようとしているときはいつでもGitが「脇を見る」ことができるGitオブジェクトを作成するとしますF
。この置換F
は、次のようにグラフに入ります。
A--B--F--G--H--I--J <-- branch
\
C--D--E <-- some_name
\
Frepl <-- refs/replace/<hash>
私たちは、のためにコミットメッセージをコピーするF
ためのものにFrepl
し、コミットのツリーオブジェクトの内部GitのハッシュIDを含め、同様に他の分野のほとんどをコピーします。しかし、その代わりにコミットするポインティングB
、コミットFrepl
コミットするポイントをE
。
なんらかの理由でcommitを使用F
するFrepl
たびに、Gitが「目を向ける」必要がありますF
。そして、それを行う方法があります。Frepl
特別な名前を付けます。ここで、はcommitの実際のハッシュIDです。refs/replace/big-ugly-hash-id
big-ugly-hash-id
F
置換を行うGitコマンドはgit replace
です。ルックアサイドは自動的に行われgit --no-replace-objects
ます。実行しない限り、Gitは常にそれを実行します。これはすべて、既存のGitオブジェクトを変更せずに実行されるため、リポジトリに追加するだけです。
置換オブジェクトの最大の欠点は、git clone
デフォルトでは置換オブジェクトをコピーしないことです(オブジェクトも名前もフェッチしません)。これは、クローンに置換が表示されておらず、それを無視することがないことを意味します。フェッチrefspecに置換を明示的に追加して取得することはできますが、少し面倒です。git push
操作は、いずれかのデフォルトでは、それらを転送しません。
大きな利点は、新しく改善されたコミットを優先して、元のコミットの使用を停止する必要がないことです。ただし、全員に切り替えてもらうことができれば、git rebase
多くのコミットをコピーしてブランチラベルを移動するために使用できます。または、git replace
最初にを使用して置換オブジェクトを作成し、次にgit filter-branch
フィルターなしで実行して、置換が発生するブランチをフィルター処理するように指示することもできます。
どのようなgit filter-branch
行いは、コピーすることで、すべてが(それはフィルターに言われていますのブランチに)コミット。それ—少なくとも論理的には; 多くの最適化があります。各コミットを抽出し、最も古いコミットから最新のコミット、一時ディレクトリに移動し、すべてのフィルターをある順序で適用してから、フィルターが行ったことを使用して新しいコミットを作成します。新しいコミットが元のコミットとビットごとに同一である場合、2つのコミットは実際には1つのコミットにすぎません。それ以外の場合、コピーは新しく異なるコミットです。新しい各コピーのデフォルトの親は、親の以前のコピーで行われたコミットです(ただし、--parent-filter
変更できるようにするためのがあります!)。これはすべて、交換用のルックアサイドギミックが発生することで実行されます2。したがって、Gitがコピーを実行するとF
、実際にコピーされます。Frepl
代わりに、にFrepl
戻りE
ます。私たちは、このフィルタに名前を持っている場合branch
、結果はそのGitのコピーA
、そしてB
、そしてC
、そしてD
、そしてE
、そしてFrepl
、そしてG
、そしてH
、そしてI
、そしてJ
、与えます:
A--B--F--G--H--I--J <-- refs/original/refs/heads/branch
\
C--D--E <-- some_name
\
Frepl <-- refs/replace/<hash>
\
G'-H'-I'-J' <-- branch
親がであるがツリー(スナップショット)が。のツリー(スナップショット)と同じであるbranch
commitを指すことに注意してください。ポイントをにコミットし、ポイントをに戻し、ポイントをに戻します(実行時に作成したコピー:フィルタリング中は変更されませんでした)。この点に戻り、バックにポイントする、(自身の変わらないコピーである)の背中にそう、と。J'
I'
J
I'
H'
G'
Frepl
git replace
E
D
A
最終的な効果は、のrefs/original/
ようなすべての名前を捨てた場合refs/original/refs/heads/branch
、置換コミットを「所定の位置に固定」することです。その後、のrefs/replace/
名前を削除Frepl
するA-B-C-D-E-Frepl-G'-H'-I'-J'
と、リポジトリにコミットしかなかったかのように見えます。多くのコミットのハッシュIDが変更されたため、このリポジトリは元のリポジトリと互換性がなくなりました。しかし、名前から始めるとbranch
、すべての適切な場所に、光沢のある新しくコピーされたコミットのみが表示されます。
2もちろん、を実行git --no-replace-objects filter-branch
すると、置換ルックアサイドが無効になります。これを行う理由はおそらくありません。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加