302
215

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

【Git】同じコンフリクト解消を繰り返している人に教えたい「git rerere」

Last updated at Posted at 2024-07-05

はじめに

こんにちは、kenです。みなさんコンフリクト解消してますか!

チーム開発をしているとコンフリクトとは嫌でも向き合うことになりますが、コンフリクト解消って緊張感のある作業なのでやりたくないですよね。

そんなコンフリクト解消をちょっぴり楽にする(かもしれない)コマンドを最近知ったので今回はそれを紹介します、その名もgit rerereです。

git rerereとは

Gitの公式ドキュメント(日本語版)には次のように記載されています。

git rerere コマンドはベールに包まれた機能といってもいいでしょう。これは “reuse recorded resolution” の略です。その名が示すとおり、このコマンドは、コンフリクトがどのように解消されたかを記録してくれます。
そして、同じコンフリクトに次に出くわしたときに、自動で解消してくれるのです。

ここに書かれているように、git rerere以前に解決したコンフリクトを再利用することで、同じコンフリクトに再度遭遇した際、自動で解決してくれる機能です。

一般的にコンフリクトは、どの変更を採用するかを決めるために意図を理解している人が手動で解決する必要がありますが、同じコンフリクトが再発する場合は、前回と同じ解決策を適用できることがほとんどです。そんなときにgit rerereは非常に便利です。

しかし、このコマンドを使ったことがない人の中には「本当にコンフリクトを自動で解消してくれるの…?」と疑われる方もいらっしゃると思います(現に私がそうでした)。
そのため、次の章では実際に使っている様子をお見せしようと思います。

実際に使ってみる

本当に同じコンフリクト解消を自動で行ってくれるのか実際に試してみます。
まずはコンフリクトをわざと起こしますが、具体的なコンフリクト内容は知らなくてもいいので興味のある人だけ読んでください。

起こしたコンフリクトの詳細

このデモのために作ったリポジトリ上のmainfeatureブランチ間でコンフリクトを起こします。
最初mainブランチのhello.txtは次のような状態でした。

hello.txt(mainブランチ)
こんにちは
こんばんは

そこからfeatureブランチを切り、この2つの挨拶に伸ばし棒を付け加えました。

hello.txt(featureブランチ)
こんにちはー
こんばんはー

それに並行してmainブランチで2つの挨拶にビックリマークを付け加える変更をしました。

hello.txt(変更を加えた後のmainブランチ)
こんにちは!
こんばんは!

この状態でmainブランチをfeatureブランチにマージしようとするとコンフリクトが発生しますね。これで準備が完了です。
スクリーンショット 2024-06-23 19.52.55.png

コンフリクトが起こせる状態になったのでgit rerereの出番です。
ここまで書いていませんでしたが、git rereregit configで編集できるgitの設定のうちのひとつです。そのため、有効化したい場合は次のコマンドを打ちます。1

git config --global rerere.enabled true

まずは比較のためrerereが無効になっている状態(デフォルト)を見てみます。

featureブランチ上でgit merge origin/mainを実行しorigin/mainとのコンフリクトを解消。その後git reset --hard HEAD^でマージコミットを打ち消し、再度origin/mainとのマージを実行すると、二回目のマージ実行時も同じコンフリクトを解消しないといけなくなりました。これが普通ですよね。
false.gif

次はrerereを有効にした状態で同じ操作をしてみます。

すると1回目のマージ実行時には先ほどと同様にコンフリクトの解消が求められましたが、2回目のマージ実行時にはすでにコンフリクトは解消されており、手動でのコンフリクト解消は必要なくgit addしてcommitするだけになりました!

true.gif

よくみると2回目のマージ実行時にはResolved 'hello.txt' using previous resolution.と出ており、以前のコンフリクト解消の結果を使っていることがわかります。
スクリーンショット 2024-06-23 19.12.04.png

以前のコンフリクト解消の記録をどこに保存しているのか

このgit rerere、一体どんな仕組みで動いているのか気になると思うので、その中身についても少し触れようと思います。

git rerereを有効にしてコンフリクト解消を行うと、作業を行ったリポジトリの.git配下にrr-cacheというディレクトリが生成されていると思います。ここにコンフリクト解消の履歴は記録されていきます。
スクリーンショット_2024-06-23_20_02_51.png

たとえば先程のデモで行ったコンフリクト解消の後にこのrr-cacheディレクトリを覗くと、ハッシュ値の名前でフォルダが作られており、その中にはpostimagepreimageという名前のファイルが入っています。

スクリーンショット 2024-06-23 20.06.04.png

preimageコンフリクト発生時のファイルの状態 を記録します。先程の例で生成されたpreimageは以下の通りです。

<<<<<<<
こんにちはー
こんばんはー
=======
こんにちは!
こんばんは!
>>>>>>>

一方postimageは、コンフリクト解消後のファイルの状態を記録します。次に同じコンフリクトが発生したとき、このファイルの内容が再適用されます。先程の例で生成されたpostimageは以下の通りです。

こんにちはー!
こんばんはー!

まとめると、コンフリクト解消の履歴は.git/rr-cacheディレクトリに保存され、preimagepostimageがそれぞれコンフリクトの解消前・後を記録しているというわけです。2

やっていることはかなり原始的でしたね。

具体的な使用場面

ここまでで、同じコンフリクトの解消においてgit rerereが非常に便利であることをお伝えできたかと思います。最後に、このコマンドが有効に使える場面、すなわち具体的に同じコンフリクト解消が発生する場面についても少し触れておきたいと思います。これらの例は、冒頭でリンクを貼ったGitのドキュメントに書かれているものです。


  • 長期にわたって開発が続いているトピックブランチをいつでもmainブランチに問題なくマージしたいが、そのためのマージコミットが複数生まれるのを避けたいとき。
    (こまめにmainをマージしてはマージコミットを取り消すことで実現できますが、その際にrerereを有効にしておくと、同じコンフリクト解消を繰り返さなくて済みます。)

  • リベースする度に同じコンフリクトを処理することなく、ブランチをリベースされた状態に保っておきたいとき。
    (頻繁にリベースを行う場合でも、rerereを使うことで毎回同じコンフリクトを解消する手間が省けます。)

  • 多くのコンフリクト解消を乗り越えてようやくマージをしたあとに、リベースを使うことに方針を変更することになったとき。
    (最初にマージを行った際にrerereが有効になっていれば、リベースに方針を変更しても同じコンフリクトを再度解消する必要がなくなります。)

  • 開発中のトピックブランチをいくつもまとめてマージして、テスト可能なHEADを生成するとき。
    (テストが失敗した場合、マージを取り消して失敗の原因となったブランチを除外して再度テストを実行しますが、rerereを使うことでその際のコンフリクト解消が不要になります。)

これらの場面では、git rerereが効果的に機能し、開発効率を大幅に向上させることができると思います。

さいごに

今回はgit rerereについて書いてみました。
コンフリクト解消の履歴を記録して自動的に解消する以上、間違ったコンフリクト解消が記録されるとそれも勝手に適用されてしまうため注意が必要ですが、便利な機能であることは間違いないので使い所を見極めてうまくつかっていきたいですね。

間違いなどありましたらコメントにてご指摘ください、ここまで読んでいただきありがとうございました。

  1. もしくはrerereを有効にしたいリポジトリの.git配下にrr-cacheを作成することでもrerereを有効化することはできます。この有効化の仕方は特定のリポジトリでのみrerereを使いたい場合に便利です。git configrerereを有効化すると全リポジトリでrerereが有効化されます。

  2. ちなみにpreimagepostimageが入っていたディレクトリのフォルダ名がハッシュ値になっていましたが、これはpreimageをこねこねした結果をSHA1でハッシュ化した結果のようです。(詳しくはこちら

302
215
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
302
215

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?