Skip to content

Using Git with Unity

Original guide by j-mai. Modified by Dr. Wang to work with CS 480.

What are the problems?

  • Noise: Unity editor has lots of temporary files that we don’t want git to track
  • Broken object references: Unity keeps track of objects created by using random GUIDs, and if they aren’t tracked using .meta files then there can be conflicts that really break your project
  • Unresolvable merge conflicts: files like scene files that are written in confusing languages (like YAML) that are supposed to be translations of Unity editor actions into code. Most likely, you cannot resolve using Git, and the only way to resolve merge conflicts is to open it in a text editor and resolve them manually while hoping you don't mess anything up because these files are confusing and not meant to be manipulated directly.
  • Large files: Sometimes assets are large and take up a lot of storage space

A few things we can do to help prevent some of these problems:

  1. Add Unity specific .gitignore
  2. Configure Unity for version control
  3. Configure Git to use Unity Smart Merge
  4. Use Git Large File Storage
  5. Use a Git GUI client to make life easier

0. Project Setup πŸ’»

Download and install Git: https://git-scm.com/downloads

Also download and install Git LFS: https://git-lfs.com/

For Each Project:

First, create an empty repository on GitHub (no readme, license, etc.). Guide here.

Next, setup your Unity project for Git. Use the Terminal or Command Prompt to go into your project's root directory and run git init.

Use git remote add origin <GIT_URL_FROM_GITHUB> to add the repo URL as a remote connection, replacing <GIT_URL_FROM_GITHUB> with your repository's URL (something like https://github.com/Isaac-W/unity-example.git).

1. Adding a Unity Specific .gitignore

.gitignore files are used to mark which files (or types of files) should not be included in version control. These include temporary files, files that are generated by Unity, builds, etc.

I have created a gitignore that works nicely with Unity: .gitignore

Download the file and put it in the root of your project.

Note

You may need to rename the file back to .gitignore if your OS decided to rename it to just gitignore. On Windows, you may need to name the file .gitignore. (with a trailing dot), which will allow you to get the correct filename (because Windows is dumb).

Once you have your .gitignore, commit it to the repository:

git add .gitignore
git commit -m "Add .gitignore"

2. Configuring Unity for Version Control

  1. Open the project settings window.
    • Edit > Project Settings
  2. Use plain text serialization to avoid unresolvable merge conflicts.
    • EditorAsset Serialization / Mode: "Force Text"
  3. Make .meta files visible to avoid broken object references.
    • Version ControlMode: "Visible Meta Files"
  4. Save your changes.
    • File > Save Project

3. Configuring Git to use Unity Smart Merge for merging

By default, if two people work on a scene simultaneously and try to merge changes through Git, you will get pretty nasty conflicts that can be impossible to resolve (I learned this the hard way). In general, try to only have 1-2 people edit a scene at a time.

You should use Unity's Smart Merge (also called UnityYAMLMerge) tool, shipped with the Unity editor. It is a pretty decent tool to use for resolving merge conflicts that would be really hard to merge maually (e.g. scene files). You can read more about it at the following links.

Assuming Unity is installed in the standard location, the path to UnityYAMLMerge will be...

...on Windows:

C:\Program Files\Unity\Hub\Editor\<YOUR UNITY VERSION>\Editor\Data\Tools\UnityYAMLMerge.exe

or

C:\Program Files (x86)\Unity\Hub\Editor\<YOUR UNITY VERSION>\Editor\Data\Tools\UnityYAMLMerge.exe

...on Mac OSX:

/Applications/Unity/Hub/Editor/<YOUR UNITY VERSION>/Unity.app/Contents/Tools/UnityYAMLMerge

πŸš€ Important: Run these commands once per computer to configure the tool with Git (replace with the correct path as found above):

git config --global merge.tool unityyamlmerge
git config --global mergetool.unityyamlmerge.cmd "'<PATH TO UNITYYAMLMERGE>' merge -p '$BASE' '$REMOTE' '$LOCAL' '$MERGED'"
git config --global mergetool.unityyamlmerge.trustExitCode false

Or run git config --global --edit and and add the following lines:

[merge]
tool = unityyamlmerge

[mergetool "unityyamlmerge"]
trustExitCode = false
cmd = '<PATH TO UNITYYAMLMERGE>' merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

Next, create a .gitattributes file and include the following lines:

# For line ending normalization between Unix and Windows
* text=auto

# Unity 
*.cs diff=csharp text
*.cginc text
*.shader text

*.mat -text merge=unityyamlmerge diff
*.anim -text merge=unityyamlmerge diff
*.unity -text merge=unityyamlmerge diff
*.prefab -text merge=unityyamlmerge diff
*.physicsMaterial2D -text merge=unityyamlmerge diff
*.physicMaterial -text merge=unityyamlmerge diff
*.asset -text merge=unityyamlmerge diff
*.meta -text merge=unityyamlmerge diff
*.controller -text merge=unityyamlmerge diff

This tells git to use UnityYAMLMerge when resolving conflicts for meta files, scenes, prefabs, etc. This is based off of https://gist.github.com/nemotoo/b8a1c3a0f1225bb9231979f389fd4f3f.

Once you have your .gitattributes, commit it to the repository:

git add .gitattributes
git commit -m "Add UnityYAMLMerge to .gitattributes"

βš”οΈ Resolving Merge Conflicts

Typically, try to have only one person editing a scene at a time. And make sure to "pull" any changes from the remote repository before you start working! Hopefully with tools like UnityYAMLMerge, manual merge conflict resolution will no longer be needed.

But if things come to this, you can always open the file in Visual Studio Code (or worst case, a text editor) and manually resolve merge conflicts that way.

If you have a merge conflict involving a scene (with the .unity extention), instead of manually fixing it, type the command:

git mergetool

The tool will then resolve the conflicts for you automatically, if there is any conflict that requires input in order to be fixed, you can open the conflicting file using Visual Studio Code, or even a regular text editor and resolve them manually.

See the guide from GitHub on resolving conflicts manually.

Warning: Make sure that in the file (which is in YAML format), there are no duplicate lines caused by the merge conflict.

4. Using Git Large File Storage System (Git LFS)

Git LFS uses .gitattributes to track large files with git.

Instructions to install and use are here.

Add the following lines to your .gitattributes:

# 3D models
*.3dm filter=lfs diff=lfs merge=lfs -text
*.3ds filter=lfs diff=lfs merge=lfs -text
*.blend filter=lfs diff=lfs merge=lfs -text
*.c4d filter=lfs diff=lfs merge=lfs -text
*.collada filter=lfs diff=lfs merge=lfs -text
*.dae filter=lfs diff=lfs merge=lfs -text
*.dxf filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text
*.jas filter=lfs diff=lfs merge=lfs -text
*.lws filter=lfs diff=lfs merge=lfs -text
*.lxo filter=lfs diff=lfs merge=lfs -text
*.ma filter=lfs diff=lfs merge=lfs -text
*.max filter=lfs diff=lfs merge=lfs -text
*.mb filter=lfs diff=lfs merge=lfs -text
*.obj filter=lfs diff=lfs merge=lfs -text
*.ply filter=lfs diff=lfs merge=lfs -text
*.skp filter=lfs diff=lfs merge=lfs -text
*.stl filter=lfs diff=lfs merge=lfs -text
*.ztl filter=lfs diff=lfs merge=lfs -text
# Audio
*.aif filter=lfs diff=lfs merge=lfs -text
*.aiff filter=lfs diff=lfs merge=lfs -text
*.it filter=lfs diff=lfs merge=lfs -text
*.mod filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.s3m filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.xm filter=lfs diff=lfs merge=lfs -text
# Fonts
*.otf filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
# Images
*.bmp filter=lfs diff=lfs merge=lfs -text
*.exr filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.hdr filter=lfs diff=lfs merge=lfs -text
*.iff filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.pict filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.tga filter=lfs diff=lfs merge=lfs -text
*.tif filter=lfs diff=lfs merge=lfs -text
*.tiff filter=lfs diff=lfs merge=lfs -text

# Collapse Unity-generated files on GitHub
*.asset linguist-generated
*.mat linguist-generated
*.meta linguist-generated
*.prefab linguist-generated
*.unity linguist-generated

Once you've made your changes to .gitattributes, commit it to the repository:

git add .gitattributes
git commit -m "Add Git LFS types to .gitattributes"

5. Use a Git GUI client to make life easier

You can install an unofficial Unity extension that lets you access Git from inside Unity. It also has some helpful features like file locking which makes collaboration easier: https://github.com/spoiledcat/git-for-unity. Use at your own risk!

If you are uncomfortable with using git on the commandline/terminal, there are tools that offer a visualized Git workflow.

I recommend GitHub Desktop, TortoiseGit, and GitKraken.

🀝 Git Workflow

The best way to merge scene files is to avoid merge conflicts in the first place. Merge conflicts in YAML files are pretty much impossible to read and thus very, very difficult to manually resolve those conflicts. There are generally two ways we can avoid merge conflicts in scene files:

  1. Break up large scenes into prefabs
  2. Use Additive Scene Loading

1. Breaking up large scenes into prefabs

We can break up scenes into many smaller areas and make these areas prefabs. Then as long as there is only one person working on an area prefab at a time, we can have multiple people working on the scene at once.

A video on this approach: https://www.youtube.com/watch?v=YgoCp2tzRh0

2. Using additive scene loading

This approach is similar to the prefabs approach but instead of breaking up the scenes into different prefabs, we break up the scene into mini scenes and use additive scene loading to put them together at runtime. Scene loading may cause a dip in framerate temporarily though.

You can load scenes additively in the editor by dragging a scene into the Hierarchy. However, this only affects the editor. You will need to write code to load all the appropriate scenes additively so that it will work when actually running: https://docs.unity3d.com/ScriptReference/SceneManagement.LoadSceneMode.Additive.html.

This is also a great idea for implementing certain games. See some example uses here: https://www.reddit.com/r/Unity3D/comments/mbjkie/what_are_some_reasons_that_you_guys_use_additive/

GitHub Locking

When using Git for Unity, you can lock specific files. If you choose to have a workflow where only one person can edit a scene at a time, you can lock the scene and prevent others from editing it.

πŸš€ Here is a guide on locks in GitHub: https://github.com/spoiledcat/git-for-unity/blob/main/docs/using/locking-files.md.

But if you didn't want to go to a separate webpage, here are the instructions:

  • To view which scenes are locked, make sure the GitHub window is opened in Unity, then press the Locks tab in the GitHub window.
  • To lock a scene, right click on the scene and click Request Lock
  • To release a lock, there are 3 ways:
    • Right click on the locked file and click Release Lock
    • From the GitHub tab under the Locks view, right-click to open the context menu and select to Release Lock
    • Select the file to unlock and go to select the menu option Assets -> Release Lock

πŸš€ Note: There are 2 options to release locks: Release Lock and Release Lock (Forced). Always choose Release Lock first, as Release Lock (Forced) can release someone else's lock.

Make sure you release your locks after you are done working on them so other people can work on them too!

✏️ Tips for Workflow

  1. Communication: As with all collaborative projects, communication is important, but with development in Unity, communication is especially important. Tools like UnityYAMLMerge should make collaboration much easier, but it is still really important to communicate which scenes each person is working on.
  2. Only Stage Actual Changes: When you are doing git add, only add the files that you actually changed!
  3. Making New Branches: When starting new features, to make sure that you are as up-to-date with master as possible, make new branches from the master branch and not other branches
  4. Follow the Flow: Always, commit, pull, then push in that order!

Sources and More Information: