概要UnityYAMLMerge
こんにちは。ちょびえです。最近ひまつぶしにUnityをはじめてみました。
ということで少し前に連載していたMySQLプロトコルはひとやすみしつつ、今日はUnityらへんのことでも書いてみようかなぁ、と思います。
最近遊んでいるのがUnity5で入ったSmartMerge、もといUnityYAMLMergeなのですが説明のなさっぷりが素晴らしく、面白半分で検証を行っています。
SmartMarge - UnityYAMLMerge
http://docs.unity3d.com/Manual/SmartMerge.html
Unity incorporates a tool called UnityYAMLMerge that can merge scene and prefab files in a semantically correct way. The tool can be accessed from the command line and is also available to third party version control software.
割愛しますと、UnityはUnityYAMLMergeというPrefabとSceneファイルを意味合い的に正しくマージするツールを用意してくれたのです。
コマンドラインでも、VCSからでも利用できるんでぜひ使ってね!というツールです
設定方法
Windowsの場合デフォルトではC:\Program Files\Unity\Editor\Data\ToolsにUnityYAMLMergeというexeファイルがあります。
Windowsの場合パス選択するのが面倒なので環境変数のPathにでも通しておきましょう。
Windows
1 |
C:\Program Files\Unity\Editor\Data\Tools\UnityYAMLMerge.exe |
MaxOS Xの場合は下記の場所となります。
1 |
/Applications/Unity/Unity.app/Contents/Tools/UnityYAMLMerge |
UnityYAMLMergeを引数なしで実行するとヘルプが表示されます。今回はまず評価ということなのでVCSでの設定はスキップするものとします。
設定ファイルの追加
UnityYAMLMergeは実行時にカレントディレクトリにautoという設定ファイルがあることを期待するようです。
全く説明がなくて最初からハードルが上がるのですが、よくみてみるとToolsディレクトリにあるmergespecfile.txtがUnityYAMLMergeのfallbaack用の設定ファイルのサンプルとなるようです。
今回は無償で使えるP4Mergeを設定しようとおもうので、autoファイル(中身はただのテキストです)に
1 |
* use "%programs%\Perforce\p4merge.exe" "%b" "%l" "%r" "%d" |
を設定します。きっとアスタリスクが先頭につくようなので忘れないようにしてください。
実験
https://gist.github.com/chobie/c95fe0e05858b8819639
上記リポジトリに初期状態のQuadをそのままPrefab化したのものをコピーしてTransformの値を書き換えたファイル達を用意しました。
Ancestorが祖先ファイルで、Localがローカルで編集したものと仮定しており。RemoteがAncestorをほかの人が更新してPush済みのものでLocalとコンフリクトするものとします。
内容をdiff3で確認しますとこんな具合にコンフリクトしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ diff3 Local.prefab Ancestor.prefab Remote.prefab ====3 1:27c 2:27c m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 3:27c m_LocalRotation: {x: 0, y: 1, z: 0, w: -1.62920685e-07} ==== 1:29c m_LocalScale: {x: 1, y: 2, z: 1} 2:29c m_LocalScale: {x: 1, y: 1, z: 1} 3:29c m_LocalScale: {x: 1, y: 10, z: 1} |
まずは検証ということでコマンドラインから実行を行います
1 |
> UnityYAMLMerge.exe merge -p .\Ancestor.prefab .\Remote.prefab .\Local.prefab Output.prefab |
-pというオプションが何かというと
For doing a normal premerge use the -p option. This will create a new left
(theirs) file that contains all non-conflicting merges and the left side
values for where there are conflicts. The same will be done the base file and
right file and put into a temporary file unless [premerge base dest] /
[premerge right dest] is specified in which case that will be used instead.
As a result the fallback merge tool will not show the original left,right or base
files but the premerged ones which contains only conflicts that this tool
could not handle.
コンフリクトしていない部分に関しては先にmergeしたらLocalとRemoteのファイルを作成し、fallbackツールでUnityYAMLMergeが解決できないコンフリクトを人間に選んでもらって解決するという方式です。特に競合が発生しない状況であればセマンティクス的に正しくMergeを実行してくれます。
今回のようなdiffの場合だと典型的な3-way mergeでm_LocalRotationは解決できますが、m_LocalScaleはコンフリクトします。
この場合にUnityYamlMergeはどのように動いてくれるかというと、先ほどつくったautoの設定ファイルで指定したFallbackツールが起動します。
見てわかる通り、問題ない部分は正しく先にマージされていますね。
このようにUnityYAMLMergeはautoファイルから設定を読み込み、指定したFallbackツール(P4Merge)でコンフリクト箇所を指定できるようになっています。
OSX版のopendiffで試した場合は終了待ちせずに数秒でexitしてくれやがったんですが、Windows版では正しく動いているようです。
ひとまず10を選択して保存します。つづいてdiffを見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ diff -uAncestor.prefab Output.prefab ---Ancestor.prefab Thu Apr 16 00:21:22 2015 +++ Output.prefab Thu Apr 16 01:00:53 2015 @@ -24,9 +24,9 @@ m_PrefabParentObject: {fileID: 0} m_PrefabInternal: {fileID: 100100000} m_GameObject: {fileID: 122172} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: 0, y: 1, z: 0, w: -1.62920685e-07} m_LocalPosition: {x: 0, y: 0, z: -70} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalScale: {x: 1, y: 10, z: 1} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 |
(設定によっては改行コードの問題でどばっとdiffがでるかもしれませんがその場合は適度に直してください)
Ancestorと最終結果のdiffをとってみてもわかる通り問題なくMergeが行えているようですね!
とはいえ、これはどうみても通常のスリーウェイマージでも処理できる内容でおもしろくありませn。
続いて、具体的にSemanticsが正しいMergeとはどういうものなのかを検証していきましょう。
Semantics的に正しいYamlMerge
ちょっといたずらをしてLocal.prefabとRemote.prefabのTransformのプロパティの順番をぐちゃぐちゃにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$ diff -u Local.prefab Remote.prefab --- Local.prefab Thu Apr 16 01:03:07 2015 +++ Remote.prefab Thu Apr 16 01:03:28 2015 @@ -21,15 +21,15 @@ --- !u!4 &409072 Transform: m_ObjectHideFlags: 1 + m_Children: [] m_PrefabInternal: {fileID: 100100000} m_GameObject: {fileID: 122172} + m_LocalRotation: {x: 0, y: 1, z: 0, w: -1.62920685e-07} m_LocalPosition: {x: 0, y: 0, z: -70} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalScale: {x: 1, y: 2, z: 1} - m_Children: [] + m_PrefabParentObject: {fileID: 0} m_Father: {fileID: 0} m_RootOrder: 0 - m_PrefabParentObject: {fileID: 0} + m_LocalScale: {x: 1, y: 10, z: 1} --- !u!23 &2313158 MeshRenderer: m_ObjectHideFlags: 1 |
みるのもいやなdiffになるのはわかっているんですが、diff3で見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
$ diff3 Local.prefabAncestor.prefab Remote.prefab ==== 1:23a 2:24c m_PrefabParentObject: {fileID: 0} 3:24c m_Children: [] ==== 1:25a 2:27c m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 3:27c m_LocalRotation: {x: 0, y: 1, z: 0, w: -1.62920685e-07} ==== 1:27,29c m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalScale: {x: 1, y: 2, z: 1} m_Children: [] 2:29,30c m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] 3:29c m_PrefabParentObject: {fileID: 0} ==== 1:32c m_PrefabParentObject: {fileID: 0} 2:32a 3:32c m_LocalScale: {x: 1, y: 10, z: 1} |
あーもうやるきでないですねー。小さなPrefabだったらまだしもシーンファイルでこられたら泣く泣くやっていくしかありません。
でもUnityYAMLMergeなら大丈夫。Semantics的に正しくpremergeしてから本当にConflictしているところだけを提案してくれます。
1 2 |
> rm Output.prefab > UnityYAMLMerge.exe merge -p .\Ancestor.prefab .\Remote.prefab .\Local.prefab Output.prefab |
やったね!
念のためdiffで確認してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ diff -u Ancestor.prefab Output.prefab Ancestor.prefab Remote --- Ancestor.prefab Thu Apr 16 00:21:22 2015 +++ Output.prefab Thu Apr 16 01:22:53 2015 @@ -24,9 +24,9 @@ m_PrefabParentObject: {fileID: 0} m_PrefabInternal: {fileID: 100100000} m_GameObject: {fileID: 122172} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: 0, y: 1, z: 0, w: -1.62920685e-07} m_LocalPosition: {x: 0, y: 0, z: -70} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalScale: {x: 1, y: 10, z: 1} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 |
ぐっ!
UnityYAMLMergeの課題
と、Semantics的に正しくMergeしてくれるUnityYAMLMergeなのですが大きな問題がありまして
圧倒的、人柱感!!!!
UnityYAMLMergeを引数なしで実行するとヘルプ出してくれるんですが、どうオプションを指定していいのかまったくわかりません。
組み合わせがわからんのだよ…
よくある質問
Q: sceneやprefabのyamlをマージするなんてこわいよ
A: だいじょうぶ、こわくないよ。
おわりに
ぼくといっしょにバグを踏み抜いてくれるUnityお友達募集しています。
ぜんぜんShaderとかよくわからないんですが、ShaderLabのProgramとSubProgramってどういう役割なのか?っていうのが最近の悩みです。だいたい想像つくけど、はっきり説明してと言われるとなんとも。
そいでは、Happy Hacking!