Git Custom Merge Driver

# 環境
|Envrionment| Version|
|:--|----:|
|Ubuntu| 20.04.2 LTS|
|Windows| 10.0.19041
|Git|2.25.1 |

# Git Custom Merge Driverとは
Gitのマージ処理を低レベルでカスタマイズできるGitに備わっている機構のこと。
強力な反面、事故りやすいので理解したうえで利用が必要。

# 作り方

1. Custom Merge Driverの設定
2. 適用ファイルの指定

## Custom Merge Driverの設定
２つの方法がある。

* Inline
* sh

Driverが呼び出される際に利用可能な変数に`%O`, `%A`, `%B`がある。他にもあるが、よくわからなかった。気になる方は[リファレンス](https://git-scm.com/docs/gitattributes)読み解いて下さい。
Driverの終了時に終了ステータスが

* 0（正常）の場合、`％A`をマージ後のファイル内容として採用する。
* 0（正常）以外の場合、コンフリクトが発生したとする。

### %O
祖先のバージョンの内容を持つ一時ファイル名。

### %A
oursのバージョンの内容を持つ一時ファイル名。
マージ後のファイル内容として採用する。

### %B
theirsのバージョンの内容を持つ一時ファイル名

### 設定コマンド
`git config merge.{戦略名}.name '戦略名でも概要でも'`
`git config merge.{戦略名}.driver '{Inlineコマンド or sh}'`

## 適用ファイルの指定
.gitattributesへ追記する。

### フォーマット
```　.gitattributes
ファイルパス merge={戦略名}
```
#### 注意
`./{filename}`この書き方だと動かなかった。

`{filename}`ルート直下のreadme.mdならば、以下のように記載する。

```
readme.md merge=alwaysTheirs
```

# 実際に作ってみる。
いつでもマージする側（featureブランチ）が優先されるマージ戦略を作る。
対象ファイルは、readme.txtにする。

## Custom Merge Driverの設定
### Sh作成
```mergeAlwaysTheirs.sh
#!/bin/sh
echo "`date` custom merge start" >> mergeAlwaysTheirs.log

# 動作イメージをつかみたいときに
# O="$1" # prev version
# A="$2" # ours version
# B="$3" # theirs version
# cat $A > A.txt
# cat $O > O.txt
# cat $B > B.txt

# always theirs ($B)
# $B -> $A
cp -f $B $A
echo "`date` custom merge end" >> mergeAlwaysTheirs.log
exit 0
```

### merge戦略の追加
`git config merge.alwaysTheirs.name 'always theirs'`
`git config merge.alwaysTheirs.driver 'PATH=$PATH:`pwd` && mergeAlwaysTheirs.sh %O %A %B'`

2行目は、リポジトリ直下でマージ操作を行う前提だが、pathをいちいち通すのも面倒なので。
shへパス通しているなら不要。

#### 確認
`git config -l`

```
merge.alwaysTheirs.name=always theirs
merge.alwaysTheirs.driver=PATH=$PATH:`pwd` && mergeAlwaysTheirs.sh %O %A %B
```

merge用のshは、Gitリポジトリに入れる。

## 適用ファイルの指定
```PORJECT_ROOT/.gitattributes
.readme.md merge="alwaysTheirs"
```

# 実際の動き
## masterブランチの内容
```readme.md
# Title
## Header

```

## featureブランチの更新内容
```readme.md
# Title2
## Header2

```

## masterブランチの更新内容
```readme.md
# Title1
## Header1

```

## Merge実行
### GUIツールベース
手持ちのIntelliJでpull featureブランチすると、問題なく、Theirs（featureブランチ）で上書き出来た。

### コマンドベース
`git pull origin feature/test`

#### コマンドベースだと、プロンプトが立ち上がる
`git merge`時に、マージコミットを編集するプロントが立ち上がる。

```
Merge branch 'feature/test' of https://github.com/{User}/git-test
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
```

一行目のコミットメッセージで良いならば、上書き更新（viならESC->:wqとかの保存操作）。

### merge後のmaster
```readme.md
# Title2
## Header2

```

# DriverをInlineで記載する例
ours, theirs程度の操作なら、InlineでOK。

## 常にtheirsを採用
`git config merge.alwaysTheirs.name 'always theirs'`
`git config merge.alwaysTheirs.driver 'cp -f %B %A'`

## 常にoursを採用
`git config merge.alwaysOurs.name 'always ours'`
`git config merge.alwaysOurs.driver 'exit 0'`

# Windows
Git Bash上やそれを呼び出すようになっているTortoiseGitならまったく同じコマンドラインで可能。

# 参考
[git-mergeの挙動をカスタマイズする](https://shogo82148.github.io/blog/2015/12/16/customize-git-merge/)
[git-merge](https://git-scm.com/docs/git-merge)
[gitattributes](https://git-scm.com/docs/gitattributes)
[cpコマンド](https://www.atmarkit.co.jp/ait/articles/1605/31/news019.html)
[複数コマンドをAND演算で実行する &&](https://qiita.com/_ydah/items/528aefd1a1cab51285c3#%E8%A4%87%E6%95%B0%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92and%E6%BC%94%E7%AE%97%E3%81%A7%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B-)
[「>」と「>>」はどちらもファイル出力使うコマンド](https://jj-blues.com/cms/column-differencekagikakko/#:~:text=%C2%A0-,%E3%80%8C%3E%E3%80%8D%E3%81%A8%E3%80%8C%3E%3E%E3%80%8D%E3%81%AF%E3%81%A9%E3%81%A1%E3%82%89%E3%82%82%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%87%BA%E5%8A%9B%E4%BD%BF%E3%81%86%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89,-%E3%83%90%E3%83%83%E3%83%81%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E3%81%8A%E3%81%84%E3%81%A6)