gitでいろいろな差分を見る

## はじめに
Advent Calendar用の記事です。
なにかgitネタで書くと宣言し(てしまっ)たので"ファイルの差分"というテーマにしてみました。
(gitで困ったこと、こういうのどうするの？みたいなのがあれば記事にしますと言っていたのですが、誰も声をかけてくれませんでした。みんなシャイですね。何を書こうか悩みました。)

ウォークスルー形式で進めます。

## 基本
この辺はさらっと流し見て構いません。
以下のようなファイルがあります。

```
$ git ls-files text/
text/A.txt

$ git show HEAD:./text/A.txt
Hello
Good-bye
```

編集して保存します。

```
$ vim text/A.txt # edit and save
Hello
How aer you?
Good-bye
```

差分を見ます。

```
$ git status
HEAD detached from f2a9634
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   text/A.txt

no changes added to commit (use "git add" and/or "git commit -a")

$ git diff
diff --git a/text/A.txt b/text/A.txt
index 423514f..ea8e95f 100644
--- a/text/A.txt
+++ b/text/A.txt
@@ -1,2 +1,3 @@
 Hello
+How aer you?
 Good-bye
```

`git diff`で差分を見ます。`+How aer you?`が差分です。(typoしています。)

```
$ git add text/A.txt

$ git diff --cached
diff --git a/text/A.txt b/text/A.txt
index 423514f..1e8e901 100644
--- a/text/A.txt
+++ b/text/A.txt
@@ -1,2 +1,3 @@
 Hello
+How aer you?
 Good-bye
```

`add`でステージしたファイルの差分は`diff --cached`で見ます。シノニムに`--staged`があります。
とりあえずコミットして、その内容を確認します。

```
$ git status
HEAD detached from f2a9634
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   text/A.txt

$ git commit -m "Update A.txt"
[detached HEAD 689a77d] Update A.txt
 1 file changed, 1 insertion(+)

$ git log -1 -p
commit 689a77da0b2565b6a93e8dabcbd85c561de5dd49 (HEAD)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 00:30:50 2020 +0900

    Update A.txt

diff --git a/text/A.txt b/text/A.txt
index 423514f..1e8e901 100644
--- a/text/A.txt
+++ b/text/A.txt
@@ -1,2 +1,3 @@
 Hello
+How aer you?
 Good-bye
```

ここで`log`のオプションを2つ使っています。

* `--patch` `-p`: コミットの差分を表示
* `-<N>`: `N`個のコミットログを表示

ここでは`git log -1 -p`なので、ヘッドのコミット1個分の`+How aer you?`まで表示しています。
`log`のオプションには差分のサマリーを表示するオプションもあります。

```
$ git log -1 --stat
commit 689a77da0b2565b6a93e8dabcbd85c561de5dd49 (HEAD)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 00:30:50 2020 +0900

    Update A.txt

 text/A.txt | 1 +
 1 file changed, 1 insertion(+)
```

* `--stat`: ファイルとその差分のサマリーを表示
    * `diff`でも指定可能

## 単語単位/正規表現
`aer`とtypoしていたので直します。

```
$ vim text/A.txt # fix and save
Hello
How are you?
Good-bye

$ git diff
diff --git a/text/A.txt b/text/A.txt
index 1e8e901..ea8e95f 100644
--- a/text/A.txt
+++ b/text/A.txt
@@ -1,3 +1,3 @@
 Hello
-How aer you?
+How are you?
 Good-bye
```

`diff`には単語単位で差分を見るオプションがあります。

```
$ git diff --word-diff
diff --git a/text/A.txt b/text/A.txt
index 1e8e901..ea8e95f 100644
--- a/text/A.txt
+++ b/text/A.txt
@@ -1,3 +1,3 @@
Hello
How [-aer-]{+are+} you?
Good-bye
```

`[-aer-]{+are+}`の部分が単語単位の差分です。`aer`が削除分、`are`が追加分です。(慣れていないと読みにくいかもしれません。) `--word-diff`を指定しない場合と比べれば、変更のある行内のどの部分が変更されたかが分かります。行が長い場合などに効果的です。

単語単位ではなく、正規表現にマッチした部分を表示することもできます。

```
$ git diff --word-diff-regex=.
diff --git a/text/A.txt b/text/A.txt
index 1e8e901..ea8e95f 100644
--- a/text/A.txt
+++ b/text/A.txt
@@ -1,3 +1,3 @@
Hello
How a[-e-]r{+e+} you?
Good-bye
```

パターンが`.`なので1文字単位で差分を表示します。`r`の前の`e`が削除され、後に`e`が追加されています。
パターンにマッチしない場合は表示上は差分が無いように見えます。

```
$ git diff --word-diff-regex=x
diff --git a/text/A.txt b/text/A.txt
index 1e8e901..ea8e95f 100644
--- a/text/A.txt
+++ b/text/A.txt
@@ -1,3 +1,3 @@
Hello
How are you?
Good-bye
```

* `--word-diff`: 単語単位で差分を表示
    * `--word-diff=plain`がデフォルト
    * `--word-diff=color`は色のみで`[--]`、`{++}`がつかない
* `--word-diff-regex=<pattern>`: 正規表現でマッチした部分を表示
* どちらも`log -p`と併せて指定可能
    * ex.) `git log -p --word-diff`

### 使いどころ
ファイル名の変更を含むコミットの差分を見る時に有効です。
以下、雑なサンプル。

```
$ git ls-files java/
java/main/sample/Sample.java
java/main/sample/util/MessageManager.java

$ git show HEAD:./java/main/sample/Sample.java
package sample;

import sample.util.MessageManager;

public class Sample {
    public static void main(String... args) {
        MessageManager.send("hello");
        MessageManager.receive(1);
        MessageManager.clear();
    }
}

$ git show HEAD:./java/main/sample/util/MessageManager.java
package sample.util;

public class MessageManager {
    public static int send(String message) {
        throw new UnsupportedOperationException("TODO: implement");
    }

    public static String receive(int count) {
        throw new UnsupportedOperationException("TODO: implement");
    }

    public static int clear() {
        throw new UnsupportedOperationException("TODO: implement");
    }
}
```

`MessageManager.java`を`Messages.java`に変更します。(途中は省略。)

```
$ git add java/

$ git status
HEAD detached from a00b52c
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   java/main/sample/Sample.java
        renamed:    java/main/sample/util/MessageManager.java -> java/main/sample/util/Messages.java

$ git diff --cached
diff --git a/java/main/sample/Sample.java b/java/main/sample/Sample.java
index 9af1ad9..1db16a4 100644
--- a/java/main/sample/Sample.java
+++ b/java/main/sample/Sample.java
@@ -1,11 +1,11 @@
 package sample;

-import sample.util.MessageManager;
+import sample.util.Messages;

 public class Sample {
     public static void main(String... args) {
-        MessageManager.send("hello");
-        MessageManager.receive(1);
-        MessageManager.clear();
+        Messages.send("hello");
+        Messages.receive(1);
+        Messages.clear();
     }
 }
diff --git a/java/main/sample/util/MessageManager.java b/java/main/sample/util/Messages.java
similarity index 92%
rename from java/main/sample/util/MessageManager.java
rename to java/main/sample/util/Messages.java
index 014c758..79b2e77 100644
--- a/java/main/sample/util/MessageManager.java
+++ b/java/main/sample/util/Messages.java
@@ -1,6 +1,6 @@
 package sample.util;

-public class MessageManager {
+public class Messages {
     public static int send(String message) {
         throw new UnsupportedOperationException("TODO: implement");
     }
```

これを`--word-diff-regex=.`で表示すると以下のようになります。(記事上は色がつかないのでちょっと読みにくいです。)

```
$ git diff --cached --word-diff-regex=.
diff --git a/java/main/sample/Sample.java b/java/main/sample/Sample.java
index 9af1ad9..1db16a4 100644
--- a/java/main/sample/Sample.java
+++ b/java/main/sample/Sample.java
@@ -1,11 +1,11 @@
package sample;

import sample.util.Message[-Manager-]{+s+};

public class Sample {
    public static void main(String... args) {
        Message[-Manager-]{+s+}.send("hello");
        Message[-Manager-]{+s+}.receive(1);
        Message[-Manager-]{+s+}.clear();
    }
}
diff --git a/java/main/sample/util/MessageManager.java b/java/main/sample/util/Messages.java
similarity index 92%
rename from java/main/sample/util/MessageManager.java
rename to java/main/sample/util/Messages.java
index 014c758..79b2e77 100644
--- a/java/main/sample/util/MessageManager.java
+++ b/java/main/sample/util/Messages.java
@@ -1,6 +1,6 @@
package sample.util;

public class Message[-Manager-]{+s+} {
    public static int send(String message) {
        throw new UnsupportedOperationException("TODO: implement");
    }
```

`[-Manager-]{+s+}`の部分が差分になります。この例ではあまりうまみがあるようには見えませんが、量が多くなると差分が読みやすくなることがあります。
(ならないこともあります。`.`を`...`等に変えるなどしてちょうどいいところを探すこともあります。)
(`--word-diff`ではないのは"word"の単位が自然でないから。
`[-MessageManager-]{+Messages+}.send("hello");`となりそうなところが
`[-MessageManager.send("hello");-]{+Messages.send("hello");+}`となります。記号が区切りにならない……。なんで？)

## ファイル名変更の追跡
上のファイル名(パス)変更をコミットして履歴を見てみます。

```
$ git log -p java/main/sample/util/Messages.java
commit 8334bd7b68d6feae63e2335aa71e7fb3d2e9ca22 (HEAD)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 02:02:02 2020 +0900

    Rename file

diff --git a/java/main/sample/util/Messages.java b/java/main/sample/util/Messages.java
new file mode 100644
index 0000000..79b2e77
--- /dev/null
+++ b/java/main/sample/util/Messages.java
@@ -0,0 +1,15 @@
+package sample.util;
+
+public class Messages {
+    public static int send(String message) {
+        throw new UnsupportedOperationException("TODO: implement");
+    }
+
+    public static String receive(int count) {
+        throw new UnsupportedOperationException("TODO: implement");
+    }
+
+    public static int clear() {
+        throw new UnsupportedOperationException("TODO: implement");
+    }
+}
```

コミット1個分しか表示されません。コミット作成時の`status`では

```
renamed:    java/main/sample/util/MessageManager.java -> java/main/sample/util/Messages.java
```

とありましたが、

```
new file mode 100644
index 0000000..79b2e77
--- /dev/null
+++ b/java/main/sample/util/Messages.java
```

と、新規ファイルとしての履歴しかありません。
そこで`--follow`オプションを使います。

 ```
$ git log -p --follow java/main/sample/util/Messages.java
commit 8334bd7b68d6feae63e2335aa71e7fb3d2e9ca22 (HEAD)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 02:02:02 2020 +0900

    Rename file

diff --git a/java/main/sample/util/MessageManager.java b/java/main/sample/util/Messages.java
similarity index 92%
rename from java/main/sample/util/MessageManager.java
rename to java/main/sample/util/Messages.java
index 014c758..79b2e77 100644
--- a/java/main/sample/util/MessageManager.java
+++ b/java/main/sample/util/Messages.java
@@ -1,6 +1,6 @@
 package sample.util;

-public class MessageManager {
+public class Messages {
     public static int send(String message) {
         throw new UnsupportedOperationException("TODO: implement");
     }

commit 2132e1b4e45bbd777bef2572981299306deae8f2
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 01:30:37 2020 +0900

    Add java sample code

diff --git a/java/main/sample/util/MessageManager.java b/java/main/sample/util/MessageManager.java
new file mode 100644
index 0000000..014c758
--- /dev/null
+++ b/java/main/sample/util/MessageManager.java
@@ -0,0 +1,15 @@
+package sample.util;
+
+public class MessageManager {
+    public static int send(String message) {
+        throw new UnsupportedOperationException("TODO: implement");
+    }
+
+    public static String receive(int count) {
+        throw new UnsupportedOperationException("TODO: implement");
+    }
+
+    public static int clear() {
+        throw new UnsupportedOperationException("TODO: implement");
+    }
+}

```

これでコミット2個分の履歴が出ました。ファイル名変更に対し、オプションを指定しない場合は、ファイル名変更したコミットがファイルの最古の履歴となりますが、`--follow`オプションでファイル名変更を追跡することが可能です。(`-p`は必須ではありませんが、だいたいセットで使っています。)

* `--follow`: ファイル名変更を追跡
    * ファイル単位でのみ利用可能
    * 複数ファイル指定、ディレクトリ指定はできない

## ファイル名変更の追跡 - 差分の割合
ファイル名を変更した上に、その内容も変更した場合の差分と履歴を追跡するケースを紹介します。
以下のようなファイルがあります。(内容に意味はありません。)

```
$ git ls-files ./text/
text/A.txt
text/B.txt

$ git show HEAD:./text/B.txt
1. aaa bbb ccc
2. aaa bbb
3. bbb ccc
4. bbb aaa ccc
5. ccc aaa ddd eee
6. ddd aaa bbb aaa eee
7. aaa eee bbb
8. eee ccc ddd fff
9. eee fff aaa ccc
10. fff ccc
11. fff eee ddd bbb aaa
```

`B.txt`を`C.txt`に変更 + 内容変更をします。

```
$ mv ./text/B.txt ./text/C.txt

$ sed -i 's/\<aaa\>/xxx/g' text/C.txt  # `sed -i ""` if mac
```

`aaa`を`xxx`に書き換えます。もはや`3. ...`、`8. ...`、`10. ...`の行しか変更のない行はありません。
これをステージして差分を見てみます。

```
$ git add text/

$ git status
HEAD detached from 7556ed3
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        deleted:    text/B.txt
        new file:   text/C.txt

$ git diff --cached
diff --git a/text/B.txt b/text/B.txt
deleted file mode 100644
index a373951..0000000
--- a/text/B.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-1. aaa bbb ccc
-2. aaa bbb
-3. bbb ccc
-4. bbb aaa ccc
-5. ccc aaa ddd eee
-6. ddd aaa bbb aaa eee
-7. aaa eee bbb
-8. eee ccc ddd fff
-9. eee fff aaa ccc
-10. fff ccc
-11. fff eee ddd bbb aaa
diff --git a/text/C.txt b/text/C.txt
new file mode 100644
index 0000000..e033c8b
--- /dev/null
+++ b/text/C.txt
@@ -0,0 +1,11 @@
+1. xxx bbb ccc
+2. xxx bbb
+3. bbb ccc
+4. bbb xxx ccc
+5. ccc xxx ddd eee
+6. ddd xxx bbb xxx eee
+7. xxx eee bbb
+8. eee ccc ddd fff
+9. eee fff xxx ccc
+10. fff ccc
+11. fff eee ddd bbb xxx
```

操作としてはファイル名変更 + 内容変更ですが、git上では`B.txt`の削除と`C.txt`の追加と認識されました。gitは削除と追加とのペアがあると、それをファイル名の変更とみなすか、そのまま削除 & 追加とみなすかの割合があります。デフォルトで内容の類似が50%以上ならファイル名変更、そうでなければ削除 & 追加とみなします。
そこで類似の割合を下げてみます。

```
$ git diff --cached -M20%
diff --git a/text/B.txt b/text/C.txt
similarity index 22%
rename from text/B.txt
rename to text/C.txt
index a373951..e033c8b 100644
--- a/text/B.txt
+++ b/text/C.txt
@@ -1,11 +1,11 @@
-1. aaa bbb ccc
-2. aaa bbb
+1. xxx bbb ccc
+2. xxx bbb
 3. bbb ccc
-4. bbb aaa ccc
-5. ccc aaa ddd eee
-6. ddd aaa bbb aaa eee
-7. aaa eee bbb
+4. bbb xxx ccc
+5. ccc xxx ddd eee
+6. ddd xxx bbb xxx eee
+7. xxx eee bbb
 8. eee ccc ddd fff
-9. eee fff aaa ccc
+9. eee fff xxx ccc
 10. fff ccc
-11. fff eee ddd bbb aaa
+11. fff eee ddd bbb xxx
```

`3. ...`、`8. ...`、`10. ...`の行が変更なしとして差分を表示できました。このオプションは`status`、`log`でも使えます。

```
$ git status -M2  # as same as `-M20%`
HEAD detached from 7556ed3
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    text/B.txt -> text/C.txt

$ git commit -m "Rename B.txt to C.txt, and change content"
[detached HEAD 7556ed3] Rename B.txt to C.txt, and change content
 2 files changed, 11 insertions(+), 11 deletions(-)
 delete mode 100644 text/B.txt
 create mode 100644 text/C.txt

$ git log -1 -p -M2
commit 7556ed3467875734b03e6eacc1cca944eec33d7b (HEAD)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 06:22:29 2020 +0900

    Rename B.txt to C.txt, and change content

diff --git a/text/B.txt b/text/C.txt
similarity index 22%
rename from text/B.txt
rename to text/C.txt
index a373951..e033c8b 100644
--- a/text/B.txt
+++ b/text/C.txt
@@ -1,11 +1,11 @@
-1. aaa bbb ccc
-2. aaa bbb
+1. xxx bbb ccc
+2. xxx bbb
 3. bbb ccc
-4. bbb aaa ccc
-5. ccc aaa ddd eee
-6. ddd aaa bbb aaa eee
-7. aaa eee bbb
+4. bbb xxx ccc
+5. ccc xxx ddd eee
+6. ddd xxx bbb xxx eee
+7. xxx eee bbb
 8. eee ccc ddd fff
-9. eee fff aaa ccc
+9. eee fff xxx ccc
 10. fff ccc
-11. fff eee ddd bbb aaa
+11. fff eee ddd bbb xxx
```

`--word-diff`と併せます。

```
$ git log -1 -p --word-diff -M2
commit 7556ed3467875734b03e6eacc1cca944eec33d7b (HEAD)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 06:22:29 2020 +0900

    Rename B.txt to C.txt, and change content

diff --git a/text/B.txt b/text/C.txt
similarity index 22%
rename from text/B.txt
rename to text/C.txt
index a373951..e033c8b 100644
--- a/text/B.txt
+++ b/text/C.txt
@@ -1,11 +1,11 @@
1. [-aaa-]{+xxx+} bbb ccc
2. [-aaa-]{+xxx+} bbb
3. bbb ccc
4. bbb [-aaa-]{+xxx+} ccc
5. ccc [-aaa-]{+xxx+} ddd eee
6. ddd [-aaa-]{+xxx+} bbb [-aaa-]{+xxx+} eee
7. [-aaa-]{+xxx+} eee bbb
8. eee ccc ddd fff
9. eee fff [-aaa-]{+xxx+} ccc
10. fff ccc
11. fff eee ddd bbb [-aaa-]{+xxx+}
```

* `--find-renames=<rate>` `-M<rate>`: ファイル名変更を追跡する類似の割合
    * `diff`、`status`、`log -p`、`log --stat`で指定可能
    * `-M20%` = `-M2`
    * デフォルトは50% (`git help diff`参照)
    * あまり小さい割合を指定するとまったく関係ないファイルどうしが`rename`として表示されることがある[^1]

[^1]: (小言) ファイル名変更をする場合、ファイル名変更とファイル内容変更とはコミットを分ける方がいいでしょう。`--follow`オプションで追跡できないほどの内容変更があると、デフォルト(類似50%)では履歴が途切れてしまいます。履歴からどういう意図でどのように変更されてきたかを読むのが困難になります。また`-M10%`のように割合を小さくするほど、`Y.txt -> Z.txt`の意図で変更したはずのファイルの履歴が`X.txt -> Z.txt`のような履歴として出てくるなど、本来変更されたファイルと違うファイルを追跡してしまい、これもまた履歴を辿りにくくします。

## コミット間の差分
### 単純な差分
これまでの履歴が以下の通りあります。

```
$ git log --oneline
7556ed3 (HEAD) Rename B.txt to C.txt, and change content
7b8a7c7 Add B.txt
aa343a0 Rename file
...
```

`7b8a7c7`で`B.txt`の追加、`7556ed3`で`C.txt`にファイル名を変更しました。`git log -p`ではその内容をコミットごとに表示できました。

次は`aa343a0`から`7556ed3`までの差分をまとめて表示します。

```
$ git diff aa343a0 7556ed3
diff --git a/text/C.txt b/text/C.txt
new file mode 100644
index 0000000..e033c8b
--- /dev/null
+++ b/text/C.txt
@@ -0,0 +1,11 @@
+1. xxx bbb ccc
+2. xxx bbb
+3. bbb ccc
+4. bbb xxx ccc
+5. ccc xxx ddd eee
+6. ddd xxx bbb xxx eee
+7. xxx eee bbb
+8. eee ccc ddd fff
+9. eee fff xxx ccc
+10. fff ccc
+11. fff eee ddd bbb xxx
```

途中の`B.txt`の履歴は出ません。単純に2つのコミットの間にある差分のみが表示されます。なので`C.txt`が新規ファイルとして追加、という差分になっています。

### ブランチ間の差分
これまでの`C.txt`に変更を以下のように加えました。

```
$ git log --graph --oneline -3 work1 work2
* 3ab0563 (HEAD -> work2) Fix C.txt line 8
| * 07cfb3f (work1) Fix C.txt line 3
|/
* 7556ed3 Rename B.txt to C.txt, and change content

$ git log -1 -p work1
commit 07cfb3f43f1602decebc376b194573ade74b8ab5 (work1)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 03:47:02 2020 +0900

    Fix C.txt line 3

diff --git a/text/C.txt b/text/C.txt
index e033c8b..542480e 100644
--- a/text/C.txt
+++ b/text/C.txt
@@ -1,6 +1,6 @@
 1. xxx bbb ccc
 2. xxx bbb
-3. bbb ccc
+3. yyy ccc
 4. bbb xxx ccc
 5. ccc xxx ddd eee
 6. ddd xxx bbb xxx eee

$ git log -1 -p work2
commit 3ab056358c5c5946b01b02734ba683a3c651c6d8 (HEAD -> work2)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 03:47:36 2020 +0900

    Fix C.txt line 8

diff --git a/text/C.txt b/text/C.txt
index e033c8b..fcd254a 100644
--- a/text/C.txt
+++ b/text/C.txt
@@ -5,7 +5,7 @@
 5. ccc xxx ddd eee
 6. ddd xxx bbb xxx eee
 7. xxx eee bbb
-8. eee ccc ddd fff
+8. eee yyy ddd fff
 9. eee fff xxx ccc
 10. fff ccc
 11. fff eee ddd bbb xxx
```

`work1`と`work2`の各ブランチで別の箇所に変更を加えています。
`work1`のヘッドと`work2`のヘッドの差分を見てます。

```
$ git diff work1 work2
diff --git a/text/C.txt b/text/C.txt
index 542480e..fcd254a 100644
--- a/text/C.txt
+++ b/text/C.txt
@@ -1,11 +1,11 @@
 1. xxx bbb ccc
 2. xxx bbb
-3. yyy ccc
+3. bbb ccc
 4. bbb xxx ccc
 5. ccc xxx ddd eee
 6. ddd xxx bbb xxx eee
 7. xxx eee bbb
-8. eee ccc ddd fff
+8. eee yyy ddd fff
 9. eee fff xxx ccc
 10. fff ccc
 11. fff eee ddd bbb xxx
```

`work1`(`07cfb3f`)では

```
-3. bbb ccc
+3. yyy ccc
```

の変更が

```
-3. yyy ccc
+3. bbb ccc
```

となっています。つまり`07cfb3f`の変更が元に戻す差分として表示されています。

### マージ後の状態
`work2`を`work1`にマージする場合にどのような変更になるかを、マージせずに見る方法があります。ピンとくる方もいると思いますが、プルリクエスト(マージリクエスト)の際の差分を見る方法です。

```
$ git diff work1...work2
diff --git a/text/C.txt b/text/C.txt
index e033c8b..fcd254a 100644
--- a/text/C.txt
+++ b/text/C.txt
@@ -5,7 +5,7 @@
 5. ccc xxx ddd eee
 6. ddd xxx bbb xxx eee
 7. xxx eee bbb
-8. eee ccc ddd fff
+8. eee yyy ddd fff
 9. eee fff xxx ccc
 10. fff ccc
 11. fff eee ddd bbb xxx
```

これで`work2`(`3ab0563`)の分だけが表示されました。

* `diff <merge-base>...<merge-target>`: マージ時の差分表示
    * コンフリクトがある場合は`<merge-target>`側がマージ後の状態として表示される

## マージコミットの差分
`work2`を`work1`にマージします。

```
$ git checkout work1
Switched to branch 'work1'

$ git merge work2
Auto-merging text/C.txt
Merge made by the 'recursive' strategy.
 text/C.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git log -1 -p
commit 3f844aae20b295603f7843bfb2be2539c238811e (HEAD -> work1)
Merge: 07cfb3f 3ab0563
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 06:32:58 2020 +0900

    Merge branch 'work2' into work1
```

マージコミットは`-p`では差分が表示されません。マージコミットでの差分も表示する場合は`-m`オプションを追加します。

```
$ git log -1 -p -m
commit 3f844aae20b295603f7843bfb2be2539c238811e (from 07cfb3f43f1602decebc376b194573ade74b8ab5) (HEAD -> work1)
Merge: 07cfb3f 3ab0563
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 06:32:58 2020 +0900

    Merge branch 'work2' into work1

diff --git a/text/C.txt b/text/C.txt
index 542480e..b4d4bd7 100644
--- a/text/C.txt
+++ b/text/C.txt
@@ -5,7 +5,7 @@
 5. ccc xxx ddd eee
 6. ddd xxx bbb xxx eee
 7. xxx eee bbb
-8. eee ccc ddd fff
+8. eee yyy ddd fff
 9. eee fff xxx ccc
 10. fff ccc
 11. fff eee ddd bbb xxx

commit 3f844aae20b295603f7843bfb2be2539c238811e (from 3ab056358c5c5946b01b02734ba683a3c651c6d8) (HEAD -> work1)
Merge: 07cfb3f 3ab0563
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 06:32:58 2020 +0900

    Merge branch 'work2' into work1

diff --git a/text/C.txt b/text/C.txt
index fcd254a..b4d4bd7 100644
--- a/text/C.txt
+++ b/text/C.txt
@@ -1,6 +1,6 @@
 1. xxx bbb ccc
 2. xxx bbb
-3. bbb ccc
+3. yyy ccc
 4. bbb xxx ccc
 5. ccc xxx ddd eee
 6. ddd xxx bbb xxx eee
```

* `-m`: マージコミットの差分を表示
    * `-p`と併せて指定

## 差分の前後
差分表示はデフォルトで前後3行分が表示されます。この差分の前後を任意の行数で表示できます。

```
$ git log -1 -p 3ab0563
commit 3ab056358c5c5946b01b02734ba683a3c651c6d8 (work2)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 03:47:36 2020 +0900

    Fix C.txt line 8

diff --git a/text/C.txt b/text/C.txt
index e033c8b..fcd254a 100644
--- a/text/C.txt
+++ b/text/C.txt
@@ -5,7 +5,7 @@
 5. ccc xxx ddd eee
 6. ddd xxx bbb xxx eee
 7. xxx eee bbb
-8. eee ccc ddd fff
+8. eee yyy ddd fff
 9. eee fff xxx ccc
 10. fff ccc
 11. fff eee ddd bbb xxx

$ git log -1 -p -U7 3ab0563
commit 3ab056358c5c5946b01b02734ba683a3c651c6d8 (work2)
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 03:47:36 2020 +0900

    Fix C.txt line 8

diff --git a/text/C.txt b/text/C.txt
index e033c8b..fcd254a 100644
--- a/text/C.txt
+++ b/text/C.txt
@@ -1,11 +1,11 @@
 1. xxx bbb ccc
 2. xxx bbb
 3. bbb ccc
 4. bbb xxx ccc
 5. ccc xxx ddd eee
 6. ddd xxx bbb xxx eee
 7. xxx eee bbb
-8. eee ccc ddd fff
+8. eee yyy ddd fff
 9. eee fff xxx ccc
 10. fff ccc
 11. fff eee ddd bbb xxx
```

変更箇所だけでなく、ファイルの多くを表示したい場合に`-U100`などで使っています。

* `-U<N>`: 差分の前後`N`行表示
    * `diff`、`log -p`で指定可能
    * デフォルトは3行
    * `config`でデフォルトを変更可能
        * ex.) `git config --global diff.context 7`

## 特定箇所の履歴
`log -p`では対象ファイルのすべての差分が表示されますが、特定箇所の差分の履歴を表示する場合は`-L`を使います。
以下は`C.txt`の1行目から3行目のみの履歴を表示します。

```
$ git log -L 1,3:./text/C.txt
commit 07cfb3f43f1602decebc376b194573ade74b8ab5
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 03:47:02 2020 +0900

    Fix C.txt line 3

diff --git a/text/C.txt b/text/C.txt
--- a/text/C.txt
+++ b/text/C.txt
@@ -1,3 +1,3 @@
 1. xxx bbb ccc
 2. xxx bbb
-3. bbb ccc
+3. yyy ccc

commit 7556ed3467875734b03e6eacc1cca944eec33d7b
Author: Kikuchi.M <wildflower.pink0102@gmail.com>
Date:   Mon Dec 7 06:22:29 2020 +0900

    Rename B.txt to C.txt, and change content

diff --git a/text/C.txt b/text/C.txt
--- /dev/null
+++ b/text/C.txt
@@ -0,0 +1,3 @@
+1. xxx bbb ccc
+2. xxx bbb
+3. bbb ccc
```

ファイル名変更を追跡していないので、ファイル名変更のコミットを含む2コミット分が表示されました。`git log -p ./text/C.txt`ではマージコミットを含む4コミット分が表示されます。(省略)

* `-L <start>,<end>:<file>`: `file`の`start`行から`end`行の部分のみの履歴を表示
* `-L :<function>:<file>`: `file`の`function`の履歴を表示

## まとめ
ファイルの差分、履歴を辿る場合に普段使うオプションを紹介しました。`git log -p --word-diff -U10 -m -M2 path/to/file/Foo.java`のように組み合わせて使えるので、どのような差分を調べたいかによってうまく使いわけるといいでしょう。
また説明なしに使った`show`、`ls-files`もトラックしているファイルを表示するのにも便利ですので興味のある方は使ってみてください。