git rebase
を実行した際に、同じファイルの同じ箇所を別々のブランチで変更していれば、コンフリクトが発生します。リベース(rebase)では、1つ1つのコミットに対してコンフリクトを検出し、コンフリクトがあればその都度コンフリクトを解消します。
この記事では「git rebaseでコンフリクトした際の解消方法」と「git rebaseの取り消し方法」について、図を用いて分かりやすく説明するように心掛けています。ご参考になれば幸いです。
git rebaseでコンフリクトした際の解消方法
git rebase
実行後にコンフリクトが発生した場合、以下の手順でコンフリクトを解消することができます。
- コンフリクトが発生しているファイルを修正する
- ファイルの修正が終わったら、
git add
で対象のファイルをステージに追加する git rebase --continue
を実行する- コミットメッセージを作成する
- 別コミットでもコンフリクトが発生していれば、1~4を繰り返す
ではこれから、意図的にコンフリクトを発生させて、コンフリクトを解消してみましょう。
意図的にコンフリクトを発生させる方法
意図的にコンフリクトを発生させる状況を作ります(main
ブランチとfeature
ブランチで同じファイルの同じ行を編集することで意図的にコンフリクトを発生させます)。
まず、プロジェクトディレクトリでgitを初期化します。カレントディレクトリがプロジェクトディレクトリであることを確認したら、以下のコマンドを実行してください。
$ git init
Initialized empty Git repository in C:/test/.git/
次に、main
ブランチでtest01.txt
とtest02.txt
を作成して、コミットします。以下のコマンドを実行してください。
$ echo abc >> test01.txt
$ echo def >> test02.txt
$ git add .
$ git commit -m "[main]A"
[main (root-commit) d7e79fb] [main]A
2 files changed, 2 insertions(+)
create mode 100644 test01.txt
create mode 100644 test02.txt
上記のコマンドの実行結果を見ると、コミットメッセージ[main]A
のコミットIDがd7e79fb...
であることが分かります。
次に、feature
ブランチを作成して切り替えます。以下のコマンドを実行してください。
$ git checkout -b feature
Switched to a new branch 'feature'
feature
ブランチでtest01.txt
を変更して、コミットします。以下のコマンドを実行してください。
$ echo 123 >> test01.txt
$ git add .
$ git commit -m "[feature]B"
[feature 97a2337] [feature]B
1 file changed, 1 insertion(+)
上記のコマンドの実行結果を見ると、コミットメッセージ[feature]B
のコミットIDが97a2337...
であることが分かります。
次に、feature
ブランチでtest02.txt
を変更して、コミットします。以下のコマンドを実行してください。
$ echo 456 >> test02.txt
$ git add .
$ git commit -m "[feature]C"
[feature 336808d] [feature]C
1 file changed, 1 insertion(+)
上記のコマンドの実行結果を見ると、コミットメッセージ[feature]C
のコミットIDが336808d...
であることが分かります。
次に、main
ブランチに切り替えます。以下のコマンドを実行してください。
$ git checkout main
Switched to branch 'main'
main
ブランチでtest01.txt
を変更して、コミットします。以下のコマンドを実行してください。feature
ブランチとmain
ブランチで同じファイル(test01.txt
)の同じ行を編集しているので、コンフリクトを発生させています。
$ echo ghi >> test01.txt
$ git add .
$ git commit -m "[main]D"
[main 09f6b76] [main]D
1 file changed, 1 insertion(+)
上記のコマンドの実行結果を見ると、コミットメッセージ[main]D
のコミットIDが09f6b76...
であることが分かります。
次に、main
ブランチでtest02.txt
を変更して、コミットします。以下のコマンドを実行してください。feature
ブランチとmain
ブランチで同じファイル(test02.txt
)の同じ行を編集しているので、コンフリクトを発生させています。
$ echo jkl >> test02.txt
$ git add .
$ git commit -m "[main]E"
[main d4aff40] [main]E
1 file changed, 1 insertion(+)
上記のコマンドの実行結果を見ると、コミットメッセージ[main]E
のコミットIDがd4aff40...
であることが分かります。
次に、feature
ブランチに切り替えます。以下のコマンドを実行してください。
$ git checkout feature
Switched to branch 'feature'
これで事前準備終了です。この時点でログを確認すると以下のようになっています。
$ git log --oneline --all --graph
* d4aff40 (main) [main]E
* 09f6b76 [main]D
| * 336808d (HEAD -> feature) [feature]C
| * 97a2337 [feature]B
|/
* d7e79fb [main]A
上記の実行結果を図で表すと以下のようになります。
では、実際にgit rebase
コマンドを実行してfeature
ブランチをmain
ブランチにリベースしてみましょう。以下のコマンドを実行してください。
$ git rebase main
Auto-merging test01.txt
CONFLICT (content): Merge conflict in test01.txt # ← test01.txtでコンフリクトが発生
error: could not apply 97a2337... [feature]B
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 97a2337... [feature]B
今回、2つのコミットでコンフリクトが発生するようにしました。リベースでは各コミットごとにコンフリクトを解消する必要があります。なお、上記の実行結果を見ると、feature
ブランチの1回目のコミット(コミットメッセージ: [feature]B
、コミットID: 97a2337...
)でコンフリクトが発生しており、コンフリクトしているファイルがtest01.txt
であることが分かります。
ではこれから、コンフリクトが発生している箇所を修正していきましょう。
コンフリクトが発生しているファイルを修正する
test01.txt
を開くと、以下のようにコンフリクトが発生している箇所が表示されます。
abc
<<<<<<< HEAD
ghi
=======
123
>>>>>>> 97a2337 ([feature]A)
<<<<<<< HEAD
から =======
までが、リベース先のブランチ(main
ブランチ)で競合している箇所です。=======
から >>>>>>> 97a2337 ([feature]A)
までが、リベースしたいブランチ(feature
ブランチ)で競合している箇所です。
今回はmain
ブランチとfeature
ブランチの両方の変更を取り込むことにして以下のように修正して保存します。
abc
ghi
123
ファイルの修正が終わったら、git addで対象のファイルをステージに追加する。
以下のコマンドを実行して、修正したtest01.txt
をステージに追加します。
$ git add test01.txt
git rebase --continue を実行する
対象のファイルをステージに追加したら、以下のコマンドを実行します。
$ git rebase --continue
コミットメッセージを作成する
ステージに追加したtest01.txtを
コミットするので、以下のようなコミットメッセージの編集画面が表示されます。コミットメッセージを編集するには、iキーを押してインサートモードに切り替えます。インサートモードでコミットメッセージの編集が終了したら esc キーを押し、:wqと入力し、Enterキーを押して保存します。1行目のデフォルトのコミットメッセージをそのまま使う場合は、インサートモードに切り替えずに:wqと入力し、Enterキーを押して保存します。
[feature]A
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# interactive rebase in progress; onto d4aff40
# Last command done (1 command done):
# pick 97a2337 [feature]B
# Next command to do (1 remaining command):
# pick 336808d [feature]C
# You are currently rebasing branch 'feature' on 'd4aff40'.
#
# Changes to be committed:
# modified: test01.txt
#
別コミットでもコンフリクトが発生していれば、1~4を繰り返す
1回目のコミット(コミットメッセージ: [feature]B
、コミットID: 97a2337...
)でのコンフリクトの解消は終わりましたが、今回は、コンフリクトが発生するコミットがもう1つ(コミットメッセージ: [feature]C
、コミットID: 336808d...
)あるので以下のようなレスポンスが表示されます。
$ git rebase --continue
[detached HEAD 06e0012] [feature]B
1 file changed, 1 insertion(+)
Auto-merging test02.txt
CONFLICT (content): Merge conflict in test02.txt # ← test02.txtでコンフリクトが発生
error: could not apply 336808d... [feature]C
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 336808d... [feature]C
上記の実行結果を見ると、2回目のコミット(コミットメッセージ: [feature]C
、コミットID: 336808d...
)でコンフリクトが発生しており、コンフリクトしているファイルがtest02.txt
であることが分かります。
test02.txt
を開くと、以下のようにコンフリクトが発生している箇所が表示されます。
def
<<<<<<< HEAD
jkl
=======
456
>>>>>>> 336808d ([feature]C)
今回はmain
ブランチとfeature
ブランチの両方の変更を取り込むことにして以下のように修正して保存します。
def
jkl
456
以下のコマンドを実行して修正したtest02.txt
をステージに追加します。
$ git add test02.txt
対象のファイルをステージに追加したら、以下のコマンドを実行します。
$ git rebase --continue
ステージに追加したtest02.txt
をコミットするので、コミットメッセージの編集画面が表示されますが、手順は同じなので省略します。
コミットメッセージの編集が完了したら、以下のようなレスポンスが表示されます。Successfully rebased and updated
と表示されればコンフリクトの解消が成功です。
$ git rebase --continue
[detached HEAD 60e7aa8] [feature]C
1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/feature.
この時点でログを確認すると以下のようになり、リベースされていることが分かります。
$ git log --oneline --all --graph
* 60e7aa8 (HEAD -> feature) [feature]C
* 06e0012 [feature]A
* d4aff40 (main) [main]E
* 09f6b76 [main]D
* d7e79fb [main]A
上記の実行結果を図で表すと以下の様になります。
git rebase
でコンフリクトを解消することができました。
なお、リベース(rebase)は、実際にはコミットを移動しているのではなく、既存のコミットを破棄して、新しいコミットを作成しています。新しく作成されたコミットは見た目は元のコミットと似ていますが、実際には別のコミットです。そのため、コミットのハッシュ値(コミットID)が変わります。上記のログを見ると、コミットメッセージ[feature]B
のコミットIDはリベース前97a2337...
だったのが、リベースすることによって06e0012...
に変わっていることが分かります。
git rebaseの取り消し方法
コンフリクトの内容やコンフリクトの解消方法が分からない場合、リベースを取り消すことができます。以下のコマンドを実行すると、リベース操作が取り消され、リベース前の状態に戻ります。
git rebase --abort
本記事のまとめ
この記事では「git rebaseでコンフリクトした際の解消方法」と「git rebaseの取り消し方法」について、説明しました。
お読み頂きありがとうございました。