gitのdiff, status, logを極限までコンパクト化＋便利化する


gitで色表示が有効になっていることを前提としています。まだの方は下記のコマンドで設定してください。

```bash:
git config --global color.ui auto   # 出力先が端末のときに色をつけて表示する
```

## git diffを見やすくする

### git diff --color-words で差分を小さく表示する

通常のgit diffは行単位なので、例えば変数名を一括変更した場合見づらいです。

--color-wordsを指定すると記号やスペースで区切られた単語単位でのdiffを表示できます。gitの設定は不要です。

```bash:
git diff --color-words  # 差分を単語単位で色分けで表示する
```

![diff-by-words-color-words.png](https://qiita-image-store.s3.amazonaws.com/0/4153/eec562da-e3aa-aa72-0308-82f62cb282e6.png "diff-by-words-color-words.png")

より細かな表示のカスタマイズも可能です。man git-diffで--word-diffを検索してみてください。
※ただし、変更が複雑な場合は、通常のgit diffのほうが見やすいこともあります。

### .gitattributesを設置してもっと小さく表示する

.gitattributesファイルを設置することで、言語文法に基づいて変数名、関数名といった単位でdiffを表示できます
ファイル設置後にgit diff --color-wordsとすると、下記のようにさらに小さく表示できます。（下記はjsの例です）

![diff-by-words-with-gitattributes.png](https://qiita-image-store.s3.amazonaws.com/0/4153/1bd8af9a-cd2e-d07b-505e-a69d51b619f6.png "diff-by-words-with-gitattributes.png")

設定のために、リポジトリのルートに.gitattributesを作成する必要があります。例えば下記のような内容にします。

```.gitattributes
*.c diff=cpp
*.h diff=cpp
*.cpp diff=cpp
*.hpp diff=cpp
*.m diff=objc
*.java diff=java
*.html diff=html
*.pl diff=perl
*.pm diff=perl
*.t diff=perl
*.php diff=php
*.py diff=python
*.rb diff=ruby
*.js diff=java
```

※全てのリポジトリに適用するには下記を参照ください。

### .gitattributesや.gitignoreを全てのリポジトリに適用する

同じフォーマットのファイルをホームディレクトリなどに置いておき、~/.gitconfigに設定しておきます。

```bash:
git config --global core.attributesfile ~/.gitattributes_global
git config --global core.excludesfile ~/.gitignore_global
```

## git statusを見やすくする

### git status --short --branchで現状をコンパクトに表示する

git statusはlsよりも使う（当社比）コマンドなのに、表示が長いので、コンパクトにします。

![status-short-branch.png](https://qiita-image-store.s3.amazonaws.com/0/4153/e165f014-ce9a-112e-0a84-134898c6a3fc.png "status-short-branch.png")

- ``--short``：今回のキモです
- ``--branch``：一番上の"##"の行にbranch名を表示します

MやUの表示の代表例は下記のとおりです。

| 凡例  | 説明                                                              |
|:------|:------------------------------------------------------------------|
| M\_   | 変更がgit addされている (staged)                                  |
| \_M   | 変更がgit addされていない (modified)                              |
| A\_   | 新しいファイルがgit addされている                                 |
| \_D   | rmされているけどgit addやgit rmはされていない                     |
| ??    | gitに登録されていない（untracked）                                |
| UU    | mergeするbranchも手元も変更がありconflictした (unmerged)          |
| DU    | mergeするbranchで変更されたが手元では削除されいるのでconflictした |

左がmergeされるbranchまたはadd済みで、右がmergeするbranchまたはローカル変更であることを覚えておくと便利です。

## git logを見やすくする

（※自分が使ってる一番普通のgit logに近いコマンドは`git log --stat --decorate --find-renames`でした）

### git log --graph --decorate --onelineでGUIツールっぽくcommit/merge履歴を表示する

[EGit(eclipse)](http://www.eclipse.org/egit/)や[GitX](http://gitx.laullon.com/)、[tig](http://qiita.com/tags/tig)で表示されるgit log情報は、1行に要約されてmerge関係も表示されるので、
commitの一覧を確認するためにとても便利です。これをgitコマンドでも実現できます。

```bash:
git log --graph --decorate --oneline
```

![log-graph-decorate-oneline.png](https://qiita-image-store.s3.amazonaws.com/0/4153/914454e8-c6a2-fb55-d2f3-f43749f0ea7e.png "log-graph-decorate-oneline.png")

- ``--graph``：commit履歴をグラフ形式で表示します。merge関係の表示に便利です。
  ※指定しない場合のlogの順番はcommit時間が新しい順になります。
- ``--decorate``：commitごとにbranch名、タグ名を表示します。
- ``--oneline``：1行で見やすい表示にします。詳細はman git-logのCommit Formattingの節を参照のこと。

### git log -p --statで差分や変更されたファイルも一緒に見る

```bash:
git log -p      # 差分を表示
git log --stat  # ファイルごとの変更行数を表示
```

## ファイル名変更を追跡して、log -pやdiffをコンパクト化する

ファイル名変更差分は、通常だと全delete＋全addとして表示されますが、git diffやgit log -pに-Mもしくは--find-renamesを追加すると、rename＋内容の差分で表示してくれるのでコンパクトになります。

便利なことにこれをデフォルト化するオプションがあります。

```bash:
git config --global diff.renames true
```

git log --stat --find-renamesは下記のように表示されます。

```
path/to/{file.pl => renamed_file.pl} | 21 ++++++++++++++++-----
```

git diff --find-renamesやgit log -p --find-renamesは下記のように表示されます。

```
diff --git path/to/file.pl path/to/renamed_file.pl
similarity index 82%
rename from path/to/file.pl
rename to path/to/renamed_file.pl
index 357d760..2e18a5c 100644
--- path/to/file.pl
+++ path/to/renamed_file.pl
（ファイルの内容の差分）
```

参照: http://qiita.com/kenbeese/items/31383bb77848b98161bb

## ファイル名変更を追跡して、log \[filename]を1回で済ませる

特定ファイルの履歴を見たいとき、 `git log [filename]` を使うことができますが、履歴の途中でファイル名が変更された時には履歴が途切れてしまいます。
変更前のファイル名でもう1回git logするのが定番かと思いますが、 `git log --follow [filename]` とすることで、ファイル名変更を自動で追跡することができます。

## git diffとgit logの範囲指定を簡単にする

### git log A..Bで特定範囲のcommit履歴を表示する

例えば、下記のような使い方ができます。

```bash:
git log 1.0.0..             # version 1.0.0から現在のbranchへのcommit履歴を表示
git log master..feature     # feature branchに積まれたcommit履歴を表示（masterが進んでいてもOK）
```

### git diff A...Bでbranch全体の差分を表示する

branchに積まれた差分全体のdiffを表示するために、git logでcommit番号を確認する必要がなくなります。

```bash:
git diff master...feature   # feature branchに積まれた差分を表示（masterが進んでいてもOK）
```

どのファイルが変更されたのかを見たい場合は、``--name-only``をつけます。

補足：

- ``git diff A..B``はAとB間で普通のdiffを表示します。
- ``git diff A``はgit diff A..HEADと等価です。
- 枝分かれしたcommit番号自体を知りたいときは、git merge-base A Bを使います。
  ``git diff A...B``は``git diff `git merge-base A B`..B``と等価です。

## 差分をテストしやすくする

### git diff --name-onlyで変更されたファイル一覧を取得する

変更されたファイルだけテストを実行したい場合、例えば下記のように実行することができます。

```bash:
prove `git diff --name-only | egrep '\.js$|\.pm$'`   # perlのテストを実行
jshint `git diff --name-only | egrep '\.js$'`        # jshintを実行
```

もちろん、git addしたファイルを表示する--cachedや、git diff \[branch名\]のような指定も可能です。

```bash:
your-test-tool `git diff --name-only --cached`           # commit予定の差分をテスト
your-test-tool `git diff --name-only origin/master...`   # origin/masterにmerge予定の差分をテスト
```

## 上記の全部を便利にする

### .gitconfigにaliasを作成する

例えば、commit/merge履歴をグラフで表示するコマンドをgit graphにしたい場合、下記のコマンドでOKです。

```bash:
git config --global alias.graph 'log --graph --decorate --oneline'
```

削除は下記のとおりです。

```bash:
git config --global --unset alias.graph
```

※よく使うものは.bashrcや.zshrcのaliasにしたほうが便利です。