UnityYAMLMergeを使ってコンフリクト解消をしてみる

Unityをgit管理している時にコンフリクト解消するの難しくないですか？

yamlに書かれたプロパティを見て、調整するの難しいですよね。
今回は、そんなコンフリクト解消が楽になるUnityYAMLMergeを使ってみようと思います。

# UnityYAMLMergeとは
Unityの視点でコードをマージしてくれるツールです。

Unityのゲームオブジェクトは、yaml形式で管理しています。

しかし、gitで管理してると別々のコンポーネントなのにymlでの書かれている位置が同じなためにコンフリクトが起きてしまい、大分扱いづらくなってしまいます。

そのようなUnity側から見てマージ可能なものを判断してマージしてくれるツールのことです。

# 設定方法
`UnityYAMLMerge`の存在するパスは以下のようになっています。

`/Applications/Unity/Unity.app/Contents/Tools/UnityYAMLMerge`
or
'/Applications/Unity/Hub/Editor/[version]/Unity.app/Contents/Tools/UnityYAMLMerge'

こちらのパスをgitconfigに以下のように追加します。

```.gitconfig
[merge]
	tool = unityyamlmerge
[mergetool "unityyamlmerge"]
	trustExitCode = false
    cmd='/Applications/Unity/Hub/Editor/6000.0.36f1/Unity.app/Contents/Tools/UnityYAMLMerge' merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"
```

実行するには以下のコマンドを実行します。

```zsh
git mergetool
```
こうすることでUnityYAMLMergeを実行してくれます。

# CIで組み込むことができるのか
今回GithubActionsにこのツールを組み込むことが可能かどうかを調べてみました。

本来`UnityYAMLMerge`はUnityEditorに埋め込まれてるツールなので、actionsで使うにはactionsのホストランナーにUnityをインストールする必要があります。

ただそれは大分インストールに時間がかかるので、今回はUnityYAMLMergeだけを切り出したツールがHomebrewにあったのでそちらを使ってみようと思います。

https://github.com/capyvara/unityyamlmerge

workflowは以下のようになりました。

```yml
name: ymlmerge

on:
  push:
    branches:
      - "**"
  pull_request:
    branches:
      - main
    types:
      - opened
      - reopened
      - synchronize

jobs:
  merge:
    runs-on: "macos-latest"
    permissions:
      contents: write
      pull-requests: write
    steps:
      - name: checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up Homebrew
        uses: Homebrew/actions/setup-homebrew@master
      - name: install ymlmerge
        run: |
          brew tap "capyvara/tap"
          brew install "unityyamlmerge"
      - name: Check installation
        run: |
          which uyaml
          find /usr/local -name "uyaml" -o -name "unityyamlmerge"
          brew list capyvara/tap/unityyamlmerge
      - name: Setup Git config
        run: |
          git config --global merge.tool unityyamlmerge
          git config --global mergetool.unityyamlmerge.trustExitCode false
          git config --global mergetool.unityyamlmerge.cmd '/opt/homebrew/Cellar/unityyamlmerge/1.0.2/bin/uyaml -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"'
          git config  --global user.email ${{secrets.EMAIL}}
          git config --global user.name ${{secrets.NAME}}
          git config --global mergetool.keepBackup false
      - name: merge
        run: |
          git merge origin/main || true
      - name: unityyamlmerge
        run: echo y | git mergetool
      - name: fix yml
        uses: dev-hato/actions-diff-pr-management@v1
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          branch-name-prefix: fix-yml
```

## 実際に動かしてみる

### 別々のコライダーをアタッチしてコンフリクトした場合
HEADでBoxColliderをアタッチ、mainブランチでSphereColliderをアタッチして下のようにコンフリクトしたとします。

```
<<<<<<< HEAD
--- !u!65 &508932815
BoxCollider:
=======
--- !u!135 &508932815
SphereCollider:
>>>>>>> origin/main
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 508932812}
  m_Material: {fileID: 0}
  m_IncludeLayers:
    serializedVersion: 2
    m_Bits: 0
  m_ExcludeLayers:
    serializedVersion: 2
    m_Bits: 0
  m_LayerOverridePriority: 0
  m_IsTrigger: 0
  m_ProvidesContacts: 0
  m_Enabled: 1
  serializedVersion: 3
<<<<<<< HEAD
```

この場合にCIが動くと下のようにマージしてくれます。

```
--- !u!135 &508932815
SphereCollider:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 508932812}
  m_Material: {fileID: 0}
  m_IncludeLayers:
    serializedVersion: 2
    m_Bits: 0
  m_ExcludeLayers:
    serializedVersion: 2
    m_Bits: 0
  m_LayerOverridePriority: 0
  m_IsTrigger: 0
  m_ProvidesContacts: 0
  m_Enabled: 1
  serializedVersion: 3
  m_Radius: 0.5
  m_Center: {x: 0, y: 0, z: 0}
--- !u!65 &508932816
BoxCollider:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
```

### Transformコンポーネントのpositionのパラメーターを変更した場合

パラメーターのコンフリクトは解消することができないので、下のようにコンフリクトした状態でマージされてしまいます。
マージエディターは、コンフリクトが起きないと起動されません。
なのでローカルにpullして直接修正することになります。
```
<<<<<<< HEAD
  m_LocalRotation: {x: 0.08715578, y: 0, z: 0, w: 0.9961947}
=======
  m_LocalRotation: {x: 0.0086933505, y: -0.00076056994, z: 0.087152466, w: 0.99615675}
>>>>>>> origin/main
  m_LocalPosition: {x: 10, y: 0, z: 0}
  m_LocalScale: {x: 10, y: 10, z: 1}
  m_ConstrainProportionsScale: 0
  m_Children: []
  m_Father: {fileID: 0}
<<<<<<< HEAD
  m_LocalEulerAnglesHint: {x: 10, y: 0, z: 0}
=======
  m_LocalEulerAnglesHint: {x: 1, y: 0, z: 10}
>>>>>>> origin/main
--- !u!54 &508932814
```

これらの結果からCIで使うのが難しそうなので、ローカル上で使用するのがベストかなと思いました。
ぜひコンフリクト解消で活用してください!