Resolving conflicts in Unity scenes with git and UnityYAMLMerge

Tulenber 1 May, 2020 ⸱ Intermediate ⸱ 8 min ⸱ 2019.3.11f1 ⸱

Since I did not manage to commit before my comrade, it’s time to fulfill the promise given in the article about configuring git to work with Unity and figure out how to use UnityYAMLMerge to resolve the conflicts.

In the previous article, "Custom Elements in the Unity Editor,“ we briefly mentioned the problems that may arise when using all sorts of visual editors in a distributed manner, such as blueprints in Unreal. In Unity, this problem is fully applicable to the scenes and prefabs.

UnityYAMLMerge

To resolve conflicts with scenes and prefabs, Unity offers to use a utility called UnityYAMLMerge. Documentation provides all the necessary information on using the tool. Still, it is a bit concise and does not focus on the essential aspects of its use, which we will analyze in detail.

We can divide such conflicts in Unity into two constituent: conflicts of objects and conflicts of parameters. Respectively, their solution divided into two stages:

  1. Objects combined on the scene with UnityYAMLMerge, so you can use an editor to resolve it
  2. Parameter conflicts, not resolved after the first stage, fixed by default git tools

Preparation

The article "Unity and git" provides the initial git configuration necessary to start any project. To resolve object conflicts, we will additionally need to configure UnityYAMLMerge as a git mergetool. Also, for parameter conflicts, we need a tool with three-way merge capabilities called through mergespecfile.txt.

All settings will be performed on local repositories, as they are specific to Unity, and their global setup may affect working with other types of projects.

After resolving the conflict, git creates files containing the original data, *.orig, which happen to secure the process, but you definitely do not need to store it further. And in general, you can always rollback the merge without using this file. So, to disable their generation, use the setting:

1
2
# Disable the generation of * .orig files in the repository
git config --add mergetool.keepBackup false

Setting UnityYAMLMerge as a merge tool for git:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Add UnityYAMLMerge as a merge tool
git config --add merge.tool unityyamlmerge

# Do not trusts to exit code of UnityYAMLMerge
git config --add mergetool.unityyamlmerge.trustExitCode false

# For macOs
# <path_to_unity_app> - path to the current location of the Unity editor
git config --add mergetool.unityyamlmerge.cmd '<path_to_unity_app>/Unity.app/Contents/Tools/UnityYAMLMerge merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"'

# For Windows
# git config --add mergetool.unityyamlmerge.cmd 'C:\Program Files (x86)\Unity\Editor\Data\Tools\UnityYAMLMerge.exe merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"'

Standard Merge Tools

It is also necessary to configure the work of standard merge tools, and this is where the main difficulties begin =)

Near the UnityYAMLMerge executable, you can find the mergespecfile.txt file. It is responsible for the merge tool, which will be used as a fallback if there is a conflict of parameters that cannot be solved by the main tool.

This article was written under the macOs, and I couldn’t get to work the standard opendiff(FileMerge) merge tool normally. Even if it pretended that everything was fine, in practice, when you ​​change the initial values, the merge brokes. Other tools, such as VSCode, Atom, Rider from Jet Brains, Sublime Text, Sublime-Merge, KDiff3, or Diffuse, did not work for various reasons. Some refuse to work according to the scheme proposed by UnityYAMLMerge; some need additional libraries. In the end, the most working solution was DiffMerge, included in mergespecfile.txt by default. It started up immediately out of the box, apparently because it is one of the tested tools. P4Merge (Perforce merge) also works, but not quite stable. So if you encounter problems on macOs, be sure to comment out opendiff and configure the alternative.

 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# UnityYAMLMerge fallback file


# Modify the next two lines if scene or prefab files should fallback
# on other that the default fallbacks listed below.

# %l is replaced with the path of you local version
# %r is replaced with the path of the incoming remote version
# %b is replaced with the common base version
# %d is replaced with a path where the result should be written to
# On Windows %programs% is replaced with "C:\Program Files" and "C:\Program Files (x86)" there by resulting in two entries to try out
# On OSX %programs% is replaced with "/Applications" and "$HOME/Applications" thereby resulting in two entries to try out

unity use "%programs%\YouFallbackMergeToolForScenesHere.exe" "%l" "%r" "%b" "%d"
prefab use "%programs%\YouFallbackMergeToolForPrefabsHere.exe" "%l" "%r" "%b" "%d"

# Default fallbacks for unknown files. First tool found is used.

# Apple File Merge
# Almost did not work at the time of writing, so I advise you to comment and use DiffMerge
#* use "/usr/bin/opendiff" %r %l -ancestor %b -merge %d

# Beyond Compare
* use "%programs%\Beyond Compare 4\bcomp.exe" "%r" "%l" "%b" "%d"
* use "%programs%\Beyond Compare 3\bcomp.exe" "%r" "%l" "%b" "%d"
* use "%programs%/Beyond Compare.app/Contents/MacOS/bcomp" "%r" "%l" "%b" "%d"
* use "/usr/bin/bcompare" "%r" "%l" "%b" "%d"

# Araxis Merge
* use "%programs%\Araxis\Araxis Merge\compare.exe" /3 /a2 /wait /title1:"Other" /title2:"Base" /title3:"Local" "%l" "%b" "%r" "%d"
* use "%programs%/Araxis Merge.app/Contents/Utilities/compare" -3 -a2 -wait -title1:"Other" -title2:"Base" -title3:"Local" "%l" "%b" "%r" "%d"

# Perforce merge
* use "%programs%\Perforce\p4merge.exe" "%b" "%r" "%l" "%d"
* use "%programs%/p4merge.app/Contents/Resources/launchp4merge" "%b" "%r" "%l" "%d"

# PlasticSCM merge
* use "%programs%\PlasticSCM5\client\mergetool.exe" -b=%b -s=%l -d=%r -r=%d
* use "%programs%\PlasticSCM4\client\mergetool.exe" -b=%b -s=%l -d=%r -r=%d
* use "%programs%/PlasticSCM/client/mergetool" -b=%b -s=%l -d=%r -r=%d
* use "/opt/plasticscm/client/mergetool" -b=%b -s=%l -d=%r -r=%d
* use "/opt/plasticscm4/client/mergetool" -b=%b -s=%l -d=%r -r=%d

# SourceGear DiffMerge
* use "%programs%\SourceGear\DiffMerge\DiffMerge.exe" --nosplash -m -t1="Incoming Changes"  -t2="Base" -t3="Working Copy" -r="%d" "%l" "%b" "%r"
* use "%programs%\SourceGear\Common\DiffMerge\sgdm.exe" --nosplash -m -t1="Incoming Changes"  -t2="Base" -t3="Working Copy" -r="%d" "%l" "%b" "%r"
* use "%programs%/DiffMerge.app/Contents/MacOS/DiffMerge" --nosplash -m -t1="Incoming Changes"  -t2="Base" -t3="Working Copy" -r="%d" "%l" "%b" "%r"
* use "%programs%/Utilities/DiffMerge.app/Contents/MacOS/DiffMerge" --nosplash -m -t1="Incoming Changes"  -t2="Base" -t3="Working Copy" -r="%d" "%l" "%b" "%r"

VSCode in their plans for 2020 also wants to add full support for the three-way merger; let’s hope that it will integrate with UnityYAMLMerge without troubles.

Test project

We create a test project with two branches green_spheres:
Green spheres

red_cubes:
Red cubes

Trying to merge red_cubes branch into green_spheres leads to conflict

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
tulenber@mac-tulenber Merge % git status
On branch red_cubes
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   Assets/Scenes/MergeScene.unity

no changes added to commit (use "git add" and/or "git commit -a")

Usage

The git mergetool call try to merge objects into a single scene

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
tulenber@mac-tulenber Merge % git mergetool --tool=unityyamlmerge
Merging:
Merge/Assets/Scenes/MergeScene.unity

Normal merge conflict for 'Merge/Assets/Scenes/MergeScene.unity':
  {local}: modified file
  {remote}: modified file
Conflicts:
Left  705507994.Light.m_Color
    r change to 0
    g change to 0.735849
    b change to 0.01896981
Right 705507994.Light.m_Color
    g change to 0.41037738
    b change to 0.44684783
Conflict handling:

Unresolved parameter conflicts cause call of DiffMerge:
DiffMerge

Result

Objects from both branches are editable, and the parameters depend on the merge in DiffMerge:
Merge red

Merge green

Sourcetree

Sourcetree - git client, developed by Atlassian, which works great on both Windows and macOs. Bitbucket is an essential service for it, but Github has almost the same seamless integration. It is a common tool, so let’s show how to set it up too.

The setup is similar to the console option. Adding UnityYAMLMerge to the External Merge tool - Preferences > Diff > External Diff / Merge:

  • Merge command - 〈path_to_unity_app〉/Unity.app/Contents/Tools/UnityYAMLMerge
  • Arguments - merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

Sourcetree settings

Also, do not forget about setting up integration with other editors in mergespecfile.txt, which was mentioned above in the console version; nothing will work without this.

Usage:
Sourcetree external

During operation, as well as when working with the console, DiffMerge will be called, and we will get the same result.

Alternatives

  1. The main method of dealing with conflicts in Unity is to prevent them:
    1. Dividing the scene into a larger number of prefabs will lead to the separation of object settings at different levels. As a result, the scene will not be affected when they are changed, and conflicts will occur much less often.
    2. A minimum of simultaneous work with scenes is the easiest and most affordable way to avoid conflicts =)
  2. For large (and rich) teams, there are also alternatives in the form of plugins that allow simultaneous remote editing of scenes, for example, Scene Fusion. You can watch an example of the work with it on Youtube.

Conclusion

Merging branches and resolving conflicts may not be a trivial task per se. Unity adds another level of difficulty with their setting up of UnityYAMLMerge, mergespecfile.txt, and your favorite merge utilities. But it’s a predictable tradeoff for using high-level object editors. I hope this article showed: “The devil is not so black as he is painted.” The main thing is to understand the principles of work and set up suitable (and working) tools. Conflict less. Cheers! =)



Privacy policyCookie policyTerms of service
Tulenber 2020