git rebase
のリスク について、Pro Gitで提示されているシナリオを理解しています。作者は基本的に重複したコミットを避ける方法を教えてくれます:
パブリックリポジトリにプッシュしたコミットをリベースしないでください。
Pro Gitシナリオに正確に適合せず、コミットが重複することになるため、特定の状況を説明します。
ローカルに対応する2つのリモートブランチがあるとします。
Origin/master Origin/dev
| |
master dev
4つのブランチすべてに同じコミットが含まれており、dev
で開発を開始します。
Origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
Origin/dev : C1 C2 C3 C4
dev : C1 C2 C3 C4
数回のコミットの後、変更をOrigin/dev
にプッシュします。
Origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
Origin/dev : C1 C2 C3 C4 C5 C6 # (2) git Push
dev : C1 C2 C3 C4 C5 C6 # (1) git checkout dev, git commit
master
に戻って簡単に修正する必要があります。
Origin/master : C1 C2 C3 C4 C7 # (2) git Push
master : C1 C2 C3 C4 C7 # (1) git checkout master, git commit
Origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C5 C6
dev
に戻り、変更をリベースして、実際の開発にクイックフィックスを含めます。
Origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
Origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C7 C5' C6' # git checkout dev, git rebase master
GitX/gitkでコミットの履歴を表示すると、Origin/dev
にGitとは異なる2つの同一のコミットC5'
とC6'
が含まれていることに気付きます。これで、変更をOrigin/dev
にプッシュすると、これが結果になります。
Origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
Origin/dev : C1 C2 C3 C4 C5 C6 C7 C5' C6' # git Push
dev : C1 C2 C3 C4 C7 C5' C6'
たぶん、Pro Gitの説明を完全に理解していないので、2つのことを知りたいと思います。
C5
の後にC6
とC7
を単に適用する代わりに、それを行う特別な理由はありますか?ここではリベースを使用しないでください。単純なマージで十分です。リンクしたPro Gitブックは、基本的にこの正確な状況を説明しています。内部の仕組みは少し異なるかもしれませんが、ここでそれを視覚化する方法を示します。
C5
およびC6
は一時的にdev
から引き出されますC7
はdev
に適用されますC5
とC6
はC7
の上で再生され、新しいdiffを作成するため、新しいコミットが作成されますしたがって、dev
ブランチには、C5
とC6
が事実上存在しなくなりました。これらは現在C5'
とC6'
です。 Origin/dev
にプッシュすると、gitはC5'
およびC6'
を新しいコミットとして認識し、履歴の最後に追加します。実際、C5
のC5'
とOrigin/dev
の違いを見ると、内容は同じですが、行番号が異なる可能性があり、コミットのハッシュが異なることがわかります。
Pro Gitルールを再度説明します。ローカルリポジトリ以外に存在するコミットをリベースしないでください。代わりにマージを使用してください。
git Push
を実行したという事実を省略し、次のエラーを受け取った後、git pull
を実行しました。
To [email protected]:username/test1.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to Push some refs to '[email protected]:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git Push --help' for details.
Gitは役立つように努めていますが、「git pull」アドバイスは、おそらくあなたがしたいことではありません。
あなたがいる場合:
git Push --force
を実行して、ポストリベースでリモートを更新できます。コミット( ser4405677の回答による )。git rebase
を使用しないでください。 dev
からの変更を使用してmaster
を更新するには、git rebase master dev
を実行する代わりに、dev
を実行しながらgit merge master
を実行する必要があります(- Justin's answer )。Gitの各コミットハッシュはいくつかの要因に基づいており、その1つはその前にあるコミットのハッシュです。
コミットの順序を変更すると、コミットハッシュが変更されます。リベース(何かを行うとき)はコミットハッシュを変更します。それにより、git rebase master dev
を実行した結果、dev
はmaster
と同期しなくなり、同じコンテンツでnewコミット(つまりハッシュ)が作成されます。 dev
にあるものと同じですが、master
にコミットが挿入されています。
このような状況に陥るには、さまざまな方法があります。私が考えることができる2つの方法:
master
にコミットして、dev
に基づいて作業することができます。dev
のコミットを使用して、変更を続行できます(コミットメッセージの書き換え、コミットの並べ替え、コミットのスカッシュなど)。何が起こったのかをよりよく理解しましょう。例を示します。
リポジトリがあります:
2a2e220 (HEAD, master) C5
ab1bda4 C4
3cb46a9 C3
85f59ab C2
4516164 C1
0e783a3 C0
次に、コミットの変更に進みます。
git rebase --interactive HEAD~3 # Three commits before where HEAD is pointing
(これはあなたが私のWordを受け入れなければならない場所です。Gitでコミットを変更する方法はいくつかあります。この例では、C3
の時間を変更しましたが、新しいコミットを挿入し、コミットを変更しますメッセージ、コミットの順序変更、コミットのスカッシュなど)
ba7688a (HEAD, master) C5
44085d5 C4
961390d C3
85f59ab C2
4516164 C1
0e783a3 C0
ここで、コミットハッシュが異なることに注意することが重要です。あなたはそれらについて何か(何でも)を変更したので、これは予想される動作です。これは大丈夫ですが、しかし:
プッシュしようとすると、エラーが表示されます(git pull
を実行する必要があるというヒントも表示されます)。
$ git Push Origin master
To [email protected]:username/test1.git
! [rejected] master -> master (non-fast-forward)
error: failed to Push some refs to '[email protected]:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git Push --help' for details.
git pull
を実行すると、次のログが表示されます。
7df65f2 (HEAD, master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 (Origin/master) C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0
または、別の方法を示します:
そして今、ローカルに重複したコミットがあります。 git Push
を実行する場合、サーバーに送信します。
この段階に到達しないようにするには、git Push --force
(代わりにgit pull
を実行しました)を実行することもできます。これにより、新しいハッシュを使用したコミットが問題なくサーバーに送信されます。この段階で問題を修正するには、git pull
を実行する前にリセットします。
Reflog(git reflog
)を見て、コミットハッシュが何であったかを確認しますbefore実行しましたgit pull
。
070e71d HEAD@{1}: pull: Merge made by the 'recursive' strategy.
ba7688a HEAD@{2}: rebase -i (finish): returning to refs/heads/master
ba7688a HEAD@{3}: rebase -i (pick): C5
44085d5 HEAD@{4}: rebase -i (pick): C4
961390d HEAD@{5}: commit (amend): C3
3cb46a9 HEAD@{6}: cherry-pick: fast-forward
85f59ab HEAD@{7}: rebase -i (start): checkout HEAD~~~
2a2e220 HEAD@{8}: rebase -i (finish): returning to refs/heads/master
2a2e220 HEAD@{9}: rebase -i (start): checkout refs/remotes/Origin/master
2a2e220 HEAD@{10}: commit: C5
ab1bda4 HEAD@{11}: commit: C4
3cb46a9 HEAD@{12}: commit: C3
85f59ab HEAD@{13}: commit: C2
4516164 HEAD@{14}: commit: C1
0e783a3 HEAD@{15}: commit (initial): C0
上記では、ba7688a
がgit pull
を実行する前にコミットしていたことがわかります。そのコミットハッシュを手に入れたら、その(git reset --hard ba7688a
)にリセットしてからgit Push --force
を実行できます。
これで完了です。
コミットが重複していることに気づかず、重複したコミットの上で作業を続行した場合は、本当に混乱していることになります。混乱のサイズは、重複の上にあるコミットの数に比例します。
これはどのように見えるか:
3b959b4 (HEAD, master) C10
8f84379 C9
0110e93 C8
6c4a525 C7
630e7b4 C6
070e71d (Origin/master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0
または、別の方法を示します:
このシナリオでは、重複したコミットを削除しますが、それらに基づいたコミットは保持します。C6〜C10を保持します。ほとんどの場合と同様に、これについてはいくつかの方法があります。
どちらか:
cherry-pick
その新しいブランチへの各コミット(C6からC10を含む)、およびその新しいブランチを正規として扱います。git rebase --interactive $commit
を実行します。ここで、$commit
はコミットですprior両方の重複したコミットに対して2。ここで、重複する行を完全に削除できます。1 ba7688a
と2a2e220
のどちらを選択しても問題ありません。
2 この例では、85f59ab
になります。
advice.pushNonFastForward
をfalse
に設定:
git config --global advice.pushNonFastForward false
手順を説明するときに重要な詳細を省略したと思います。より具体的には、通常、非早送りの変更をプッシュできないため、最後のステップであるgit Push
on devは、実際にエラーを表示します。
したがって、最後のプッシュの前にgit pull
を実行すると、C6とC6 'が親としてマージコミットされ、両方がログにリストされたままになります。よりきれいなログ形式は、それらが重複したコミットのマージされたブランチであることをより明らかにしたかもしれません。
または、代わりにgit pull --rebase
を作成しました(または、構成で暗黙的に示されている場合は明示的な--rebase
なし)。これにより、元のC5およびC6がローカルdevに戻されました(さらに、新しいハッシュ、C7 'C5' 'C6' ')。
これの1つの方法は、エラーが発生したときにプッシュを強制し、OriginからC5 C6をワイプするgit Push -f
でしたが、ワイプする前に他の誰かがそれらを引っ張った場合は、全体が入りますさらに多くのトラブル...基本的にC5 C6を持つすべての人はそれらを取り除くために特別な手順を行う必要があります。既に公開されているものはリベースしないでくださいと彼らが言うのはまさにそのためです。ただし、「パブリッシング」が小さなチーム内で行われていれば、それでも実行可能です。
私の場合、これはGit構成の問題の結果であることがわかりました。 (プルとマージを含む)
問題の説明:
Sympthoms:リベース後の子ブランチで重複したコミット。リベース中およびリベース後の多数のマージを意味します。
Workflow:私が実行していたワークフローのステップは次のとおりです。
このワークフローの結果として、以前のリベース以降の「機能ブランチ」のすべてのコミットの複製... :
---(この問題は、リベース前の子ブランチの変更のプルによるものでした。 Gitのデフォルトのプル設定は「マージ」です。これは、子ブランチで実行されたコミットのインデックスを変更しています。
解決策:Git構成ファイルで、リベースモードで動作するようにプルを構成します。
...
[pull]
rebase = preserve
...
JN Grxに役立つことを願って