技術メモ

神奈川在住のITエンジニアの備忘録。おもにプログラミングやネットワーク技術について、学んだことを自分の中で整理するためにゆるゆると書いています。ちゃんと検証できていない部分もあるのでご参考程度となりますが、誰かのお役に立てれば幸いです。

git rebase -i でコミット履歴をいじる時の注意点

git rebase -i <コミットID> で履歴をいじる際にコンフリクトが発生することがある。この時、コンフリクトの解消の仕方次第では、コミットが消えることがある、というのが今回の話題。

今回取り上げる題材は以下。

$ cat test.txt
111
222
333
444
$ git log --oneline
6c0d1f1 (HEAD -> master) add 444
7e2024d add 333
338ac95 add 222
7a366fb add 111

上記において、「338ac95 add 222」のコミットと「6c0d1f1 add 444」のコミットを纏めてみる。

まずは、上記の状態にするための前準備。

$ git init
Initialized empty Git repository in xxxxx

$ touch test.txt

$ echo 111 >> test.txt

$ git add test.txt

$ git commit -m "add 111"
[master (root-commit) 7a366fb] add 111
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt

$ echo 222 >> test.txt

$ git add test.txt

$ git commit -m "add 222"
[master 338ac95] add 222
 1 file changed, 1 insertion(+)

$ echo 333 >> test.txt

$ git add test.txt

$ git commit -m "add 333"
[master 7e2024d] add 333
 1 file changed, 1 insertion(+)

$ echo 444 >> test.txt

$ git add test.txt

$ git commit -m "add 444"
[master 6c0d1f1] add 444
 1 file changed, 1 insertion(+)


ここで、222 を追加した時のコミットと 444 を追加した時のコミットを纏めるために、以下のように git rebase -i を実行する。git rebase -i で指定するコミットIDは、纏めたいコミットの1個前のコミットの ID であることに注意。

$ git rebase -i 7a366fb

上記を実行すると、以下が開く。

pick 338ac95 add 222
pick 7e2024d add 333
pick 6c0d1f1 add 444

これを以下のように変更する。

pick 338ac95 add 222
s 6c0d1f1 add 444
pick 7e2024d add 333

「38ac95 add 222」のコミットに、「6c0d1f1 add 444」のコミットを squash して統合している。

すると、以下のようにコンフリクトが発生する。

Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
error: could not apply 6c0d1f1... add 444
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 6c0d1f1... add 444

対象のファイル test.txt の中身は以下になっている。

$ cat test.txt
111
222
<<<<<<< HEAD
=======
333
444
>>>>>>> 6c0d1f1 (add 444)

コンフリクトを解消するため、これを以下のように編集する。

$ cat test.txt
111
222
444

ここで「333」の行を残すと、rebase終了後に「7e2024d add 333」のコミットが消える。 (test.txt の最終的な内容は同じにはなるが。)
おそらく、「333」を残すと、git 的には「7e2024d add 333」のコミットは不要と判断するのだろう。なので、rebase する際は、今適用を試みているコミット適用後、ファイルがどんな状態になるべきか、を考える必要がある。

test.txt の編集後、rebase を継続する。

$ git add test.txt

$ git rebase --continue
[detached HEAD eb59bb4] add 222 & 444
 Date: Wed Apr 6 21:29:12 2022 +0900
 1 file changed, 2 insertions(+)
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
error: could not apply 7e2024d... add 333
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 7e2024d... add 333

すると、上記のように、再度コンフリクトが発生するので、以下のように解消する。

$ cat test.txt
111
222
<<<<<<< HEAD
444
=======
333
>>>>>>> 7e2024d (add 333)

$ cat test.txt
111
222
333
444

ここも、前に適用したコミット (add 222 & 444) の後、このコミット (add 333) を適用すると、ファイルがコミット適用後にどのような状態になるべきかを考える必要がある。

test.txt の編集後、rebase を継続する。

$ git add test.txt

$ git rebase --continue
[detached HEAD 976efc5] add 333
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/master.

以上で rebase は完了した。ここで rebase 結果の確認を行う。

$ cat test.txt
111
222
333
444

$ git log --oneline
976efc5 (HEAD -> master) add 333
eb59bb4 add 222 & 444
7a366fb add 111

$ git diff 7a366fb eb59bb4
diff --git a/test.txt b/test.txt
index 58c9bdf..65672a7 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,3 @@
 111
+222
+444

$ git diff eb59bb4 976efc5
diff --git a/test.txt b/test.txt
index 65672a7..337d413 100644
--- a/test.txt
+++ b/test.txt
@@ -1,3 +1,4 @@
 111
 222
+333
 444

上記の通り、意図した通りの test.txt およびコミットログになっている。

途中でも述べた通り、git rebase -i でコミット履歴をいじる時にコンフリクトしたら、今適用しようとしているコミットが適用された後、対象のファイルがどのようになっているべきかを考えながら、コンフリクト対象のファイルの修正を行う必要がある。