[Git]コンフリクトをよりスマートに解消したい！

[ディップ Advent Calendar](http://qiita.com/advent-calendar/2016/dip-dev)の21日目です。

## はじめに
現在、うちのチームでは`Ruby on Rails`で開発を進めており、
ソースコード管理には`GitHub Enterprise`を使っています。

精鋭たちが「えいやーっ！」と並行開発している状況なので、
気を付けないと意図せず開催されてしまうんです...**コンフリクト祭り**が。

そんなとき、普通はローカルで修正→PUSHで解決すると思います。[こんな感じで。](#%E5%8F%82%E8%80%83%E3%81%84%E3%81%A4%E3%82%82%E3%82%84%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%82%B3%E3%83%B3%E3%83%95%E3%83%AA%E3%82%AF%E3%83%88%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95)


ですが、人の手でやることなので、量が多いと面倒だし、ツラいし、ミスも発生しやすくなります。
うちのチームでも、プルリクエスト内で時折こんなコメントを見かけたりします。

> コンフリクトマーカーが残ったままです！修正をお願いします！

...これは、レビューする人も直す人もツラい。
なので、よりスマートにコンフリクトを解消する術を改めてまとめてみました。


## 片方のブランチの変更内容を採用する
### チェックアウトしている側のブランチの変更を採用したい場合

```bash
$ git checkout --ours {ファイル名}
```

### 相手側のブランチの変更を採用したい場合

```bash
$ git checkout --theirs {ファイル名}
```

いちいちファイルを開いて修正して...ということをしなくてよいのでラクちんです。
でも、両方のブランチの変更内容を採用したいことの方が多いので、なかなか活用の機会がありません...

そこで考えたもう一つの策がこちら↓


## [Gitフック](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF)を活用し、人的ミスをコミット前に防ぐ

完全に人の手でやろうとするからツラいのです。
ならば、可能な限り機械に任せてしまいたい！

### コンフリクトマーカーが残っていないか確認する方法

みなさん同じ悩みを抱えているようで、既に素敵な方法が世の中には存在しました！
なので、ありがたく活用させていただくことにしました。

- [Blocking merge conflicts in commits with Git](https://jondowdle.com/2015/02/block-merge-conflicts-in-commits/)

上記のサイトを参考に、私は少しカスタマイズして使っています。

- `=======` のチェックも追加したかったので、17行目を修正


```bash:pre-commit
#!/bin/sh

## pre-commit script to prevent merge markers from being committed. 
## Author: Jon Dowdle <jdowdle@gmail.com>

##
## This simply searches the files that you are about to commit.
##

changed=$(git diff --cached --name-only)

if [[ -z "$changed" ]]
then
    exit 0
fi

echo $changed | xargs egrep '[><=]{7}' -H -I --line-number

## If the egrep command has any hits - echo a warning and exit with non-zero status.
if [ $? == 0 ]
then
    echo "WARNING: You have merge markers in the above files, lines. Fix them before committing."
    exit 1    
fi

```

### 使い方
- `.git/hooks/pre-commit`に上記の処理を書く
- 実行権限を付与する

これだけ！

すると、`git commit`の処理前に上記の処理が走り、
もしコンフリクトマーカー（`>>>>>>>`,`<<<<<<<`,`=======`）が存在したらコミットを中止します。

これでもう、

> コンフリクトマーカーが残ったままです！修正をお願いします！

なんていうコメントは撲滅できそうです。スバラシイ！

もしGitフックを無効にしてコミットしたい場合は、`git commit --no-verify`とすればよい。


## おわりに
もっと賢くコンフリクト対応できる術はないかな...引き続きいろいろ試してみようと思います。

それから、Gitフック！これまであまり活用できていなかったのですが可能性が広がりました。
デフォルトでもいろいろ用意されているので、うまくカスタマイズしたいなー。
それに、世の中には活用されてる事例が既にたくさんあるので、今後もっと勉強して活用したいと思います！


# Appendix
## 参考にしたサイト
- [Blocking merge conflicts in commits with Git](https://jondowdle.com/2015/02/block-merge-conflicts-in-commits/)
- [Git - Git フック](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF)


## 参考：いつもやっているコンフリクト解決方法
### 1. ローカルリポジトリでブランチの最新化

ローカルにブランチを作成するところから始めると以下のような感じから。

```bash
$ git fetch origin
$ git checkout -b feature/branch-name origin/feature/branch-name
```

既に`feature/branch-name` がローカルに存在する場合は、以下の対応をすると良い。

```bash
$ git fetch origin

# developブランチの最新化
$ git checkout develop
$ git merge origin/develop

# featureブランチの最新化
$ git checkout feature/branch-name
$ git merge origin/feature/branch-name
```

### 2. featureブランチにdevelopブランチの変更をマージする

```bash
$ git checkout feature/branch-name

$ git merge develop
```

ここでコンフリクトが起きるはずなので、
`CONFLICT (content): Merge conflict in {ファイル名}` 
と表示されたファイルを1つずつ修正する。

ファイルを開いて直接変更。

```
<<<<<<< HEAD

{featureブランチで変更した内容}

=======

{developブランチで変更した内容}

>>>>>>> develop
```

### 3. すべて解決できたらコミットを続ける

```bash
$ git add .
$ git commit
```

### 4. リモートヘPUSH

```bash
$ git push origin feature/branch-name
```