Automatic scene and prefab merging in Unity3D with Git

Working with a revision control system and Unity3D requires a bit more organisation than just making a working application. To get going with a team, you’ll need both organisation and the proper tools to be as efficient as possible.

With my distrust of GUI Git clients, I wanted to take some time to get some sort of automatic scene and prefab merging to work with my regular Git cli installation. It’s also a good opportunity to learn how to use Git in the case of complex merging situations since that’s the kind of thing that is bound to happen to you in production.

Unity3D and Git

You should probably be using some sort of version control for your Unity3D projects, especially if you aspire to work in a professional context, I can’t stress that hard enough. If you aren’t doing that already this article won’t teach you the basics. It’s usually just a matter of having a proper Unity3D .gitignore. Something like this. To keep track of the changes inside the scenes and prefabs, you also have to set the Asset Serialization mode to “Force Text” in the Editor Setting of the Unity3D editor.

For most source files, scripts and shaders, when you don’t have too much conflicts, Git alone will work things out well enough, but for scenes and prefabs things get a bit more complicated.

Merging scenes and prefabs

Unity3D scenes and prefabs use a specific file format (YAML) along with dynamically generated ID’s which makes automated merging pretty hard without a specific tool to do that. Before Unity5, this free tool did the job.

Since Unity5 however, standard installations of Unity3D come bundled with a handful of tools including one called UnityYAMLMerge, a tool made specifically to allow semi-automated merging of these complicated files. This entry on the official Unity3D documentation explains how to set it up with Tortoise, however, like stated earlier, we are going to see how to set all that up with the more standard and official Git distrubution.

This article assumes that you are using Git for Windows but as long as you’re aware of which directories and path to change, this should give you an idea on how to proceed with Unity on Mac OS X and with the Linux beta.

How Git handles merge tools

Before setting everything up we should take a few minutes to look at the way git handles automatic merging through external tools.

When encountering a conflicting situation during a merging operation that Git itself cannot sort out, it fallbacks on external tools. This usually happens when the same file has been modified by two different sources at the same time.

Git comes bundled with several mergetools and depending on your platform (vimdiff for the Windows Git distribution). You can specify additional tools in your global (or local) git configuration file (.gitconfig for the global configuration, .git/config for the local one).

As detailed here, automatic diffing and merging strategies can be set up in the .gitattributes if you want to have them keep them in your repository, otherwise you can set them up in .git/info/attributes.

One important thing to note is that in itself, Git as no way to select the merge tool to use based on the file extension or type when git mergetool is explicitly invoked manually. In that case, it is the responsibility of the developer to set up a script to select which merge tool is to be used. Certain merge tools include a mechanism to do that and luckily enough, fortunately for us, UnityYAMLMerge is one of them.

Adding UnityYAMLMerge as a merge tool for Git

In order to handle scene and prefab merging in Git, we must specify UnityYAMLMerge as a possible mergetool in the Git configuration files.

Edit the Git configuration file

To specify the new mergetool, you can enter the following command in your git bash:

git config --global --edit

Get rid of the --global if you want to specify that mergetool locally for one repository.

Add the definition of the new mergetool to your configuration file:

[merge]
    tool = unityyamlmerge
[mergetool "unityyamlmerge"]
    trustExitCode = false
    keepTemporaries = true
    keepBackup = false
    path = /C/Program Files (x86)/Unity/Editor/UnityYAMLMerge.exe
    cmd = \"C:/Program Files (x86)/Unity/Editor/Data/Tools/UnityYAMLMerge.exe\" merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

Don’t forget to change the path to the UnityYAMLMerge.exe executable according to where it is located on your own machine.

Configuring UnityYAMLMerge

UnityYAMLMerge can sometimes fail or encounter conflict that it cannot sort out without your input. To handle this. When this happens, UnityYAMLMerge will fall through a list of predefined fallback diff tools. This is set up in the mergespecfile.txt file located in C:\Program Files (x86)\Unity\Editor\Data\Tools. This file contains already a basic configuration, you have to add a few lines to specify the fallback rules.

My personal mergespecfile.txt is here.

Here I specified that every file that fails should be opened with Kdiff. This is done at several lines, specifically:

unity use "%programs%/KDiff3/kdiff3.exe" "%b" "%l" "%r" -o "%d"
prefab use "%programs%/KDiff3/kdiff3.exe" "%b" "%l" "%r" -o "%d"

and

* use "%programs%/KDiff3/kdiff3.exe" "%b" "%l" "%r" -o "%d"

You can set up another diff tool like Meld of WinMerge however, the syntax, the parameters and their orders will probably change depending on the tool you’ll pick, also keep in mind that the order of the specified tools is important.

Testing out

If you did everything properly up to that point, you should be able to use UnityYAMLMerge as a mergetool manually and automatically. However, to get acquainted with the merging procedures we are now going to create a simple test project to demonstrate create a conflict resolution.

Create a new Unity3D project named SceneMergingTest, set the editor settings to serialize assets as text files, create a scene, put something in it (a cube is fine), and save it as MainScene.unity. Open a Git bash window at the root of your project’s directory, from there, initialize the repository:

Original scene

git init

and do a first commit:

git commit -m "First commit."

Make a local clone of that newly created repository:

cd ..
git clone SceneMergingTest ./SceneMergingTest_clone

You now have two identical projects, this is good enough for our test case. To be more realistic we could have two clones acting as two different dev machines and the original acting as the distant repository resembling the traditional use-case with your Git hosting platform of choice (GitHub, Bitbucket, Gogs, Gitlab or other…).

Modify the original scene, add the changes and commit them:

Modified original scene

git add .
git commit -m "Modified the scene in the origin."

Modify the clone, add the changes and commit them too:

Modified clone scene

git add .
git commit -m "Modified the scene in the clone."

From the clone, pull from the original repository:

git pull

Since the source have changed, Git will attempt an automatic merge, however if the changes you made were complex enough, you’ll encounter a merge fail:

remote: Counting objects: 4, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
From D:/Users/XXX/Desktop/Workspace/SceneMergingTest
   e74b48a..ed7be1b  master     -> origin/master
Auto-merging Assets/MainScene.unity
CONFLICT (content): Merge conflict in Assets/MainScene.unity
Automatic merge failed; fix conflicts and then commit the result.

When encountering a conflict during merging, you can tell Git to use UnityYAMLMerge to resolve the merge by entering:

git mergetool --tool=unityyamlmerge 

This will output the following:

Merging:
Assets/MainScene.unity

Normal merge conflict for 'Assets/MainScene.unity':
  {local}: modified file
  {remote}: modified file
Conflicts:
Conflict handling:
Assets/MainScene.unity seems unchanged.
Was the merge successful? [y/n] 

For some reason, sometimes the scene “seems unchanged”, you can open up the scene in Unity3D to check that everything was merged properly. In case things went wrong you can answer no, otherwise, go with yes.

UnityYAMLMerge created .orig files to save the original scenes. Before you finish the merge commit, delete these files, add the modified scene, commit it and now you’re done.

rm Assets/MainScene.unity.orig Assets/MainScene.unity.orig.meta
git add .
git commit -m "Merging the scenes."

Merge result

Congratulations you successfully merged two different scenes semi-automatically.

Conclusion

This is just an example of how to deal with problematic merge situations, a lot of that could be avoided with a smart branching policy as well as a bit of team management, but this covers the basic technical aspect of the merge tool provided by Unity Technologies.



Enter your desired user name/password and after your comment has been reviewed by an admin it will be posted and your account will be enabled. If you are already registered please login before posting.