リベースを実行した後、同じブランチで Git コミットが重複する 質問する

リベースを実行した後、同じブランチで Git コミットが重複する 質問する

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/devGitX/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 つの点を知りたいと思います。

  1. C5なぜ Git はリベース中にこれらのコミットを複製するのでしょうか? 単にとを適用したC6後でこれを行うのではなく、これを行う特別な理由があるのでしょうかC7?
  2. どうすればそれを回避できますか? それを実行するのが賢明でしょうか?

ベストアンサー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 rebasedev。からの変更を更新するにはmaster、 を実行する代わりに、を でgit rebase master dev実行する必要があります( Justin の回答に従って)。git merge masterdev

少し長めの説明

Git の各コミット ハッシュはいくつかの要素に基づいており、そのうちの 1 つは、その前のコミットのハッシュです。

コミットの順序を変更すると、コミット ハッシュが変更されます。また、リベース (何かを実行する場合) によってコミット ハッシュが変更されます。そのため、 がと同期していない状態で を実行した結果git rebase master dev、のコミットと同じ内容の新しいコミット (およびハッシュ)が作成されますが、その前に のコミットが挿入されます。devmasterdevmaster

このような状況に陥るには、さまざまな方法があります。私が考えられるのは 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

重複したコミットの上に線形コミットを表示する Git ログ

あるいは、別の方法で示します:

重複したコミットの上に線形コミットを表示するロググラフ

このシナリオでは、重複したコミットを削除し、それに基づくコミット (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

おすすめ記事