私が読んだことから、どちらも線形の歴史を得るのに役立ちます。
私が実験したことから、リベースは常に機能します。ただし、マージ--ff-onlyは、早送りできるシナリオでのみ機能します。
また、git mergeはマージコミットを作成しますが、-ff-onlyを使用すると、本質的にgit rebasingと同等の線形履歴が得られます。 --ff-onlyはgit mergeの目的を殺しますよね?
それでは、実際の違いは何ですか?
_git rebase
_には_git merge
_(_--ff-only
_の有無にかかわらず)とは異なるjobがあることに注意してください。 rebase
が行うことは、既存のコミットを取得してcopyすることです。たとえば、_branch1
_を使用しており、A
とB
の2つのコミットを行ったとします。
_...-o--o--A--B <-- HEAD=branch1
\
o--C <-- branch2
_
代わりに、これらの2つのコミットを_branch2
_で実行することを決定します。あなたはできる:
A
で行った変更のリストを取得します(その親に対してA
を差分します)B
で行った変更のリストを取得します(B
との差分A
)branch2
_に切り替えますA
で行ったのと同じ変更を加えてコミットし、A
からコミットメッセージをコピーします。このコミットを_A'
_と呼びましょうB
で行ったのと同じ変更を加えてコミットし、B
からコミットメッセージをコピーします。これを_B'
_と呼びましょう。このdiff-and-then-copy-and-commitを実行するgitコマンドがあります:_git cherry-pick
_。そう:
_git checkout branch2 # switch HEAD to branch2 (commit C)
git cherry-pick branch1^ # this copies A to A'
git cherry-pick branch1 # and this copies B
_
今、あなたはこれを持っています:
_...-o--o--A--B <-- branch1
\
o--C--A'-B' <-- HEAD=branch2
_
これで、_branch1
_に切り替えて、元のA
とB
を削除できます。_git reset
_を使用します(ここでは_--hard
_を使用します。ワークツリーをクリーンアップするので便利です)も)):
_git checkout branch1
git reset --hard HEAD~2
_
これにより、元のA
およびB
が削除されます。1 だから今あなたは持っています:
_...-o--o <-- HEAD=branch1
\
o--C--A'-B' <-- branch2
_
ここで作業を続けるには、_branch2
_を再チェックアウトするだけです。
これは_git rebase
_が行うことです:コミットを「移動」します(ただし、実際に移動することはできません。なぜなら、gitではコミットは変更できないため、親IDを変更するだけでもコピーが必要です新しいわずかに異なるコミット)。
つまり、_git cherry-pick
_はoneコミットの自動化された差分とやり直しですが、_git rebase
_は自動化されたやり直しプロセスmultipleコミット、さらに、最後に、ラベルを移動して「忘れる」か、オリジナルを隠します。
上記は、1つのローカルブランチ_branch1
_から別のローカルブランチ_branch2
_へのコミットの移動を示していますが、gitはexact same processを使用して、 _git fetch
_(_git pull
_の最初のステップであるfetch
を含む)を実行したときにいくつかの新しいコミットを取得するリモート追跡ブランチ。 _Origin/feature
_のアップストリームを持つブランチfeature
で作業を開始し、独自のコミットをいくつか行うことができます。
_...-o <-- Origin/feature
\
A--B <-- HEAD=feature
_
しかし、その後、上流で何が起こったのかを見る必要があると判断したので、_git fetch
_を実行します。2 そして、ああ、上流の誰かがコミットC
を書いた:
_...-o--C <-- Origin/feature
\
A--B <-- HEAD=feature
_
この時点で、feature
のA
とB
をC
にリベースするだけで、次のようになります:
_...-o--C <-- Origin/feature
\
A'-B' <-- HEAD=feature
_
これらは、元のA
およびB
のコピーであり、コピーが完了すると元のファイルは破棄されます(ただし脚注1を参照)。
場合によっては、リベースするものがない、つまり、あなた自身が行った作業がないことがあります。つまり、fetch
の前のグラフは次のようになります。
_...-o <-- Origin/feature
`-- HEAD=feature
_
_git fetch
_をコミットしてC
がコミットされた場合、yourfeature
ブランチは古いコミットを指し、_Origin/feature
_は移動しますフォワード:
_...-o--C <-- Origin/feature
`---- <-- HEAD=feature
_
これが_git merge --ff-only
_の出番です:現在のブランチfeature
を_Origin/feature
_とマージするように要求すると、gitは矢印をそのまま前にスライドすることが可能であることを確認し、feature
が直接コミットを指すようにしますC
。実際のマージは必要ありません。
ただし、独自のコミットA
およびB
があり、それらをC
とマージするように要求した場合、gitは実際のマージを行い、新しいマージコミットM
を作成します。
_...-o--C <-- Origin/feature
\ `-_
A--B--M <-- feature
_
ここでは、_--ff-only
_が停止し、エラーが発生します。一方、リベースでは、A
およびB
を_A'
_および_B'
_にコピーし、元のA
およびB
を非表示にすることができます。
つまり、要するに(大丈夫、遅すぎる:-))、彼らは単に異なることをします。結果が同じ場合もあれば、そうでない場合もあります。 A
およびB
をコピーしてもよい場合は、_git rebase
_を使用できます。しかし、何らかの理由notをコピーする場合は、_git merge
_を使用して、おそらく_--ff-only
_を使用して、必要に応じてマージまたは失敗できます。
1Gitは、実際にはしばらく(通常はこの場合は1か月間)オリジナルを保持しますが、非表示にします。それらを見つける最も簡単な方法は、各変更がブランチやHEAD
を更新する前に、各ブランチがポイントした場所とHEAD
がポイントした場所の履歴を保持するgitの「reflogs」を使用することです。
最終的に、reflog履歴エントリは期限切れになり、その時点でこれらのコミットは garbage collection の対象となります。
2または、再び_git pull
_を使用できます。これは、_git fetch
_を実行することで開始される便利なスクリプトです。フェッチが完了すると、コンビニエンススクリプトは、構成および実行方法に応じて、_git merge
_または_git rebase
_を実行します。
はい、違いがあります。 git merge --ff-only
は、早送りできない場合は中止し、マージ(通常はブランチ)してマージします。早送りできない場合にのみマージコミットを作成します(つまり、--ff-only
)。
git rebase
は、現在のブランチの履歴を書き換えます。または、既存のブランチを既存のブランチにリベースするために使用できます。その場合、マージではなくリベースであるため、マージコミットは作成されません。
はい、 --ff-only
は、常にgit merge
は失敗し、プレーンgit merge
は成功します。それがポイントです-線形の履歴を保持しようとしていて、マージがそのようにできない場合、want失敗します。
失敗のケースをコマンドに追加するオプションは無駄ではありません。これは前提条件を検証する方法であるため、システムの現在の状態が予期したものでない場合、問題を悪化させることはありません。