今回は、以前コーディングをしていた際にgit rerere
という機能に翻弄されたことがあったので他の方が同じような状況にならないよう対処法をメモしておきます。
その時の状況
- featureブランチに最新のdevelopブランチを反映させようとする (
git rebase origin/develop
) - コンフリクトが起きる
- コンフリクトを間違った解消方法で解決してしまう
- 正しい解消方法に修正するため、直前のコミットを削除して再度1を行う
git rerere
の機能でコンフリクトの自動解消(前回の解消方法で)が行われ、コンフリクトの差分が見えない状態になり、正しい解消方法に修正できない状態に…
この記事でできること
- コンフリクトが自動で解消されてしまう現象を解決できる
手順
対処法を説明していくために、まずはgit rerere
によりコンフリクトが自然解消されてしまう流れを見ていきます。
(結論を知りたい方はgit rerere の履歴を削除にスキップ)
git rerereを導入
# git rerereの適用
git config --global rerere.enabled true
# configを確認
git config --global --edit
# 下記設定が入っていればgit rerereが適用済
---
[rerere]
enabled = true
---
※--global
の部分は環境に合わせてよしなに変更をお願いします
コンフリクトさせる
# masterブランチで作業
git checkout master
vim test.sh
# 下記のようなファイルを作成
---
echo "Hello World"
---
# コミット
git add test.sh
git commit -m "make test.sh"
まずはmaster
ブランチに新規にファイルを作って、これを他のブランチで修正することを考えます。
# masterブランチから2つのブランチを切る
git checkout -b "develop1"
git checkout -b "develop2"
開発ブランチを切ります
# develop1で作業
git checkout develop1
vim test.sh
# test.shを以下のように修正
---
echo "Halo World"
---
# コミット
git add test.sh
git commit -m "Halo World"
# develop2で作業
git checkout develop2
vim test.sh
# test.shを以下のように修正
---
echo "Hello Word"
---
# コミット
git add test.sh
git commit -m "Hello Word"
微妙に修正を加えています。
それぞれのブランチのlogを確認してみましょう。
# develop1ブランチのlog
git checkout develop1
git log --oneline
---
6c86cbb (HEAD -> develop1) Halo World
c8ff4cd (master, develop2) make test.sh
7adac7b initial commit
---
# develop2ブランチのlog
git checkout develop2
git log --oneline
---
bc91ba1 (HEAD -> develop2) Hello Word
c8ff4cd (master) make test.sh
7adac7b initial commit
---
# masterブランチのlog
git checkout master
git log --oneline
---
c8ff4cd (master) make test.sh
7adac7b initial commit
---
コンフリクトさせる準備は完了です😎
予期せぬコンフリクトは怖いですが、自身でコンフリクトを発生させるのは楽しいですね
# それぞれのブランチをmasterにマージしていく
git checkout master
git merge develop1
git merge develop2
# 同じファイル(test.sh)を修正しているコミットがぶつかるため、git merge develop2のタイミングで下記メッセージが出ます。
---
Auto-merging test.sh
CONFLICT (content): Merge conflict in test.sh
Recorded preimage for 'test.sh'
Automatic merge failed; fix conflicts and then commit the result.
---
git merge develop1
は問題ないですが、
git merge develop2
で同じファイル(test.sh)を修正しているコミットがぶつかるため、このタイミングでコンフリクトが発生します。
↓エディタではこのような表示になります
次に間違ったコンフリクトの解消をしてみます。
今回は、test.shの内容が
echo "Halo World"
echo "Hello Word"
となることを目標として進めます。
(develop1ブランチとdevelop2ブランチの差分を両方取り込む形ですね)
間違ったコンフリクトの解消をする
vim test.sh
---
echo "Hallo World"
---
# 差分を確認
git diff
---
@@@ -1,1 -1,1 +1,1 @@@
- echo "Halo World"
-echo "Hello Word"
++echo "Hallo World"
---
# コンフリクトを解消する
git add test.sh
git commit -m "fix conflict"
# その際以下メッセージが出ることを確認(コンフリクトの履歴を保存したとのこと)
---
Recorded resolution for 'test.sh'.
---
この例では
echo "Hallo World"
で解消をしてしまい、意図せぬ結果になった…という想定。
ログはこのような状態
# masterブランチのlog
git log --oneline
---
265d844 (HEAD -> master) fix conflict
bc91ba1 (develop2) Hello Word
6c86cbb (develop1) Halo World
c8ff4cd make test.sh
7adac7b initial commit
---
コンフリクトが自動解消されてしまうことを確認
# コンフリクトの解消方法を間違ったので直前のコミットを削除
git reset --hard HEAD^
# 再度develop2ブランチのマージを実行
git merge develop2
# git merge develop2のタイミングで下記メッセージが出ます。
---
Auto-merging test.sh
CONFLICT (content): Merge conflict in test.sh
Resolved 'test.sh' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.
---
Resolved 'test.sh' using previous resolution.
前の解決方法でtest.shのコンフリクトを解消しましたよとメッセージがあります。
↓エディタでは下記のように自動解消された状態になってしまうのでコンフリクト時の差分が見えず、正しい解消方法に修正できない状態になります。
なのでこれをコンフリクト解消前の状態に戻します。
git rerere の履歴を削除
git rerere forget test.sh
git checkout -m test.sh
上記コマンドを実行すると履歴が消えて解消前の状態に戻ります。
後は正しいやり方で解消すればOK!!
参考文献
- https://git-scm.com/book/ja/v2/Git-のさまざまなツール-Rerere
- https://qiita.com/usamik26/items/00208bbcde31fcde9117
- https://makandracards.com/makandra/21795-git-how-to-forget-a-recorded-resolution
コメント