こんにちは。
エンジニアの遠藤です。

みなさんは、 local で git の差分を確認する時、どのように確認していますか?
私はいつも

$ git diff

と CLI で確認をしています。

ただ、これでは差分が分かりにくいケースに遭遇します。
まぁ、そういうものかなと半ば諦めていたのですが、先日difftasticというドンピシャな diff ツールを発見したのでご紹介します。

よくある差分

インデントの変更

まずは、 git でよく見かける差分の表示を見てみましよう。
変更前のコードは、 serverless framework の python テンプレートにしてみます。

import json


def hello(event, context):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

さて、 serverless framework を使っていて、よくある改修が、例外のキャッチです。
処理の途中で例外が発生してしまい、 500 が返ってくるけど何が起きているのか分からないので、 handler 全体を try ~ except で囲っておこう、というような改修ですね。

今回は、以下のように try ~ except を入れてみました。

def hello(event, context):
    response = {}
    try:
        body = {
            "message": "Go Serverless v1.0! Your function executed successfully!",
            "input": event
        }

        response = {
            "statusCode": 201,
            "body": json.dumps(body)
        }
    except Exception as e:
        print(e)

    return response

git の差分

では git の標準の diff を見てみましょう。

----------2024-03-07-23.31.37
はい、良く見る差分ですね。
try ~ except しているのでハンドラ内のコードが全体的にインデントが下がってしまい、ほぼ全量差分が出ています。

difftastic の差分

では、 difftastic の差分を見てみます。(git への設定方法は後述します)

----------2024-03-07-23.32.12

どうでしょうか?
追加した try ~ except の変更箇所が分かりやすくなった気がします。
また、それ以外に response の statusCode をこっそり変えていたのですが、こちらも分かりやすく表示されています。
皆さんは、 git の差分を見た際に気がつきましたでしょうか?

difftastic だと、バッチリ差分が出ていますね。
try ~ except を追加しただけのつもりだったけど、うっかりログを仕込んだの忘れてた!といったことも良くあります。

文字列の変更

また、最近はあまり書くこともないですが、 SQL をゴリゴリ文字列で書いているプロジェクト、ありますよね。

    query = 'SELECT * FROM status WHERE step != "A" AND step != "B" ' \
            'AND step != "C" AND step != "D" ' \
            'AND step != "E" AND step != "F" ' \
            'AND step != "G" ORDER BY created_at LIMIT 1;'

これを、以下のように書き換えてみます。

    query = 'SELECT * FROM status WHERE step != "A" AND step != "I" ' \
            'AND step != "J" AND step != "K" ' \
            'AND step != "L" AND step != "M" ' \
            'AND step != "N" ORDER BY created_at LIMIT 1;'

WHERE 条件の step の対象を変えていることが分かりますが、パッと見て何を変えたか分かりますでしょうか?

git の差分

git の差分はこちら。

----------2024-03-08-0.20.49

なるほど。
SQL を変えたことは分かりますが、パッと見てどこが変わっているか分かりませんね。

difftastic の差分

difftastic で見る差分。

----------2024-03-08-0.22.01

おお。
変更箇所に下線が引かれていて、何が変わっているか分かりやすいですね。

使いどころ

さて、便利そうなことは分かりましたが、一方で、インデントに変更が入ったことは示してくれません。
例えば、最初のコードを以下のように修正してみます。

def hello(event, context):
    body = {
      "message": "Go Serverless v1.0! Your function executed successfully!",
      "input": event
    }

    response = {
      "statusCode": 200,
      "body": json.dumps(body)
    }

    return response

git での差分は以下の通り、インデントのスペースが減ったことが分かります。

----------2024-03-07-23.38.26

一方、 difftastic では、差分はなし、と判定されます。
----------2024-03-07-23.39.17

特に python はインデントが重要な言語です。
インデントの差分が出ないと、 if の終了箇所が変わっていることなどにも気付けません。

というわけで、 difftastic のみを盲信するのは良くないので、上手に使い分けると良いと思います。
個人的には、 git で出ている差分が見辛いなという時に、念の為見てみる、くらいの使い方が良いのかなと思います。

git への設定方法

最後に、 local の git プロジェクトへの設定方法を説明して終わります。
手順自体は、公式も公開していますので、併せてご確認ください。
https://difftastic.wilfred.me.uk/git.html

まずは、 difftastic をインストールします。

$ brew install difftastic

ヘルプを叩いてインストールが正常終了したことを確認しましょう。

$ difft -h

ファイルの差分や、フォルダ以下の差分を確認することもできます。

$ difft file_a file_b
$ difft dir_a/ dir_b/

フォルダ以下の差分は、同一のファイル名を探して比較してくれるようです。

では、本題の git への設定に移ります。
設定するのは、 git の config へ以下を追記するだけで OK です。

[diff]
        tool = difftastic

[difftool]
        prompt = false

[difftool "difftastic"]
        cmd = difft "$LOCAL" "$REMOTE"

[pager]
        difftool = true

全ての git プロジェクトで使っていく場合は、 ~/.gitconfig に追記してください。
特定のプロジェクトだけで使いたい場合は、 your_project/.git/config に追記してください。

終わりに

自分が不便だなと思っていることは、世の中の多くの人も不便に感じていて、そして大抵の場合、すでにソリューションがあるんだなと改めて感じました。
他にも差分ツールを見かけたら積極的に試してみようと思いました。
皆様も、面白いツールを見つけたら、どんどん活用してみましょう。