Pro Git のThe Perils of Rebasingで紹介されているシナリオは理解できます。著者は基本的に、コミットの重複を避ける方法を説明しています。
パブリックリポジトリにプッシュしたコミットをリベースしないでください。
私の特定の状況についてお話しします。これは 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
origin/dev
GitX/gitk でコミットの履歴を表示すると、2 つの同一のコミットが含まれておりC5'
、 Git とは異なることがわかります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
なぜ Git はリベース中にこれらのコミットを複製するのでしょうか? 単にとを適用したC6
後でこれを行うのではなく、これを行う特別な理由があるのでしょうかC7
?- どうすればそれを回避できますか? それを実行するのが賢明でしょうか?
ベストアンサー1
短い答え
を実行したという事実を省略し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
で作業している場合は、実行して、リベース後のコミットでリモートを更新できます( user4405677 の回答に従って)。 - 複数の開発者が同時にブランチで作業している場合、そもそもを使用すべきではありません
git rebase
dev
。からの変更を更新するにはmaster
、 を実行する代わりに、を でgit rebase master dev
実行する必要があります( Justin の回答に従って)。git merge master
dev
少し長めの説明
Git の各コミット ハッシュはいくつかの要素に基づいており、そのうちの 1 つは、その前のコミットのハッシュです。
コミットの順序を変更すると、コミット ハッシュが変更されます。また、リベース (何かを実行する場合) によってコミット ハッシュが変更されます。そのため、 がと同期していない状態で を実行した結果git rebase master dev
、のコミットと同じ内容の新しいコミット (およびハッシュ)が作成されますが、その前に のコミットが挿入されます。dev
master
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
(ここは私の言うことを信じてもらうしかないのですが、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
コミット ハッシュを確認します。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 まで) は保持します。ほとんどの場合と同様に、これを行うにはいくつかの方法があります。
どちらか:
- 最後に複製されたコミット1で新しいブランチを作成し、
cherry-pick
各コミット (C6 から C10 までを含む) をその新しいブランチに追加し、その新しいブランチを正規のものとして扱います。 - または
git rebase --interactive $commit
、 を実行します。ここで、 は重複したコミット2の前の$commit
コミットです。ここで、重複した行を完全に削除できます。
1どちらを選択しても問題ありません。どちらでも問題なくba7688a
動作2a2e220
します。
2この例では、 となります85f59ab
。
要約
advice.pushNonFastForward
に設定false
:
git config --global advice.pushNonFastForward false