|
|
@@ -0,0 +1,293 @@ |
|
|
# Using Git with Unity |
|
|
|
|
|
 |
|
|
|
|
|
[Git](https://git-scm.com/) is a popular free and open source distributed |
|
|
version control system. |
|
|
|
|
|
I am new to Unity, but as a long time git user, I wanted to use git for my |
|
|
Unity projects. The bottom line is... it doesn't work nearly as well as I |
|
|
would like it to. |
|
|
|
|
|
This page contains all the information I could gather on the subject. |
|
|
|
|
|
## Configure Unity |
|
|
|
|
|
### Smart Merge |
|
|
|
|
|
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 5 comes with a tool called _UnityYAMLMerge_ that can automatically merge |
|
|
scenes and prefabs stored in YAML format. |
|
|
If there is a conflict, it will open a standard merge tool |
|
|
(such as KDiff3 or Meld) for a 3-way merge where _BASE_, _LOCAL_ and |
|
|
_REMOTE_ will all have been preprocessed. |
|
|
|
|
|
Our objective is to configure git and Unity to take advantage of this tool. |
|
|
|
|
|
#### Custom Merge Tool (optional) |
|
|
|
|
|
You can configure _UnityYAMLMerge_ to use a merge tool |
|
|
of your choice. Edit the file `mergespecfile.txt` (located in `C:\Program Files\Unity\Editor\Data\Tools`) accordingly. For instance, to |
|
|
enable KDiff3, change the following two lines... |
|
|
|
|
|
``` |
|
|
unity use "%programs%/KDiff3/kdiff3.exe" "%b" "%l" "%r" -o "%d" |
|
|
prefab use "%programs%/KDiff3/kdiff3.exe" "%b" "%l" "%r" -o "%d" |
|
|
``` |
|
|
|
|
|
...and add the following line at the begining of the section |
|
|
'Default fallbacks for unknown files'. |
|
|
|
|
|
``` |
|
|
* use "%programs%/KDiff3/kdiff3.exe" "%b" "%l" "%r" -o "%d" |
|
|
``` |
|
|
|
|
|
_Edit the paths to match those on your environment._ |
|
|
|
|
|
### Asset Serialization |
|
|
http://docs.unity3d.com/Manual/class-EditorManager.html |
|
|
> To assist with version control merges, Unity can store scene files in a |
|
|
> text-based format (see the text scene format pages for further details). |
|
|
> If scene merges will not be performed then Unity can store scenes in a more |
|
|
> space efficient binary format or allow both text and binary scene files to |
|
|
> exist at the same time. |
|
|
|
|
|
In order for the _UnityYAMLMerge_ tool to work, assets must be stored in |
|
|
YAML format. Go into Edit > Project Settings > Editor, set the `Asset Serialization` |
|
|
option to `Force Text`. |
|
|
|
|
|
 |
|
|
|
|
|
If you do this on a large existing codebase, |
|
|
it can take some time (and even crash). |
|
|
|
|
|
## Configure Git |
|
|
|
|
|
### Git mergetool |
|
|
|
|
|
Now that everything is setup on the Unity side, we need to tell git to use |
|
|
the proper merge tool. |
|
|
|
|
|
Add the following to your .git/config: |
|
|
``` |
|
|
[merge] |
|
|
tool = unityyamlmerge |
|
|
|
|
|
[mergetool "unityyamlmerge"] |
|
|
trustExitCode = false |
|
|
keepTemporaries = true |
|
|
keepBackup = false |
|
|
path = 'C:\\Program Files\\Unity\\Editor\\Data\\Tools\\UnityYAMLMerge.exe' |
|
|
cmd = 'C:\\Program Files\\Unity\\Editor\\Data\\Tools\\UnityYAMLMerge.exe' merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED" |
|
|
``` |
|
|
|
|
|
_Edit the paths to match those on your environment._ |
|
|
|
|
|
Next, we want to prevent git from trying to merge some types of file on its own |
|
|
by declaring them as binary files. |
|
|
|
|
|
To do this, put the following in your .gitattributes: |
|
|
``` |
|
|
*.unity binary |
|
|
*.prefab binary |
|
|
*.asset binary |
|
|
``` |
|
|
|
|
|
The good news is that _UnityYAMLMerge_ will do all the merging for us. |
|
|
The bad news if that the file will be treated as on opaque blob as if it |
|
|
was still in binary format. |
|
|
|
|
|
### Human-redable diffs (optional) |
|
|
|
|
|
We can use a simple textconv script to retain basic, human-readable diffs. |
|
|
|
|
|
Add this to your .git/config: |
|
|
``` |
|
|
[diff "unity"] |
|
|
textconv='C:\\Users\\petit_v\\unityYamlTextConv.py' |
|
|
``` |
|
|
|
|
|
_Edit the paths to match those on your environment._ |
|
|
|
|
|
Then, edit your .gitattributes to read (note the added `diff=unity`): |
|
|
``` |
|
|
*.unity binary diff=unity |
|
|
*.prefab binary diff=unity |
|
|
*.asset binary diff=unity |
|
|
``` |
|
|
|
|
|
An example of a textconv script, `unityYamlTextConv.py`, could be: |
|
|
``` |
|
|
#!/usr/bin/env python |
|
|
|
|
|
import sys |
|
|
import yaml |
|
|
import pprint |
|
|
|
|
|
if len(sys.argv) < 2: |
|
|
sys.exit(-1) |
|
|
|
|
|
def tag_unity3d_com_ctor(loader, tag_suffix, node): |
|
|
values = loader.construct_mapping(node, deep=True) |
|
|
if 'Prefab' in values: |
|
|
if 'm_Modification' in values['Prefab']: |
|
|
del values['Prefab']['m_Modification'] |
|
|
return values |
|
|
|
|
|
class UnityParser(yaml.parser.Parser): |
|
|
DEFAULT_TAGS = {u'!u!': u'tag:unity3d.com,2011'} |
|
|
DEFAULT_TAGS.update(yaml.parser.Parser.DEFAULT_TAGS) |
|
|
|
|
|
class UnityLoader(yaml.reader.Reader, yaml.scanner.Scanner, UnityParser, yaml.composer.Composer, yaml.constructor.Constructor, yaml.resolver.Resolver): |
|
|
def __init__(self, stream): |
|
|
yaml.reader.Reader.__init__(self, stream) |
|
|
yaml.scanner.Scanner.__init__(self) |
|
|
UnityParser.__init__(self) |
|
|
yaml.composer.Composer.__init__(self) |
|
|
yaml.constructor.Constructor.__init__(self) |
|
|
yaml.resolver.Resolver.__init__(self) |
|
|
|
|
|
UnityLoader.add_multi_constructor('tag:unity3d.com', tag_unity3d_com_ctor) |
|
|
with open(sys.argv[1], 'r') as stream: |
|
|
docs = yaml.load_all(stream, Loader=UnityLoader) |
|
|
for doc in docs: |
|
|
pprint.pprint(doc, width=120, indent=1, depth=6) |
|
|
``` |
|
|
|
|
|
Now it's possible to see what changed inside of a prefab, asset or scene: |
|
|
|
|
|
 |
|
|
|
|
|
There are a lot of things that could be done to improve the output |
|
|
of the script to make it more meaningfull to us humans. |
|
|
|
|
|
**[rant]** |
|
|
You may have noticed that the script above will not display the `m_Modification` |
|
|
field inside of a Prefab. Apparently, someone at Unity thought it was |
|
|
a good idea to store a _complete_ list of all the modifications to a prefab |
|
|
inside of said prefab. **Why?**... I use a version constrol system whose sole |
|
|
purpose is to keep track of file changes, I don't need or want any of this. |
|
|
|
|
|
If you make a change to a Prefab in the Editor and then Ctrl-Z, |
|
|
you can get yourself into a situation like this one: |
|
|
``` |
|
|
$ git status |
|
|
On branch master |
|
|
Changes not staged for commit: |
|
|
(use "git add <file>..." to update what will be committed) |
|
|
(use "git checkout -- <file>..." to discard changes in working directory) |
|
|
|
|
|
modified: Assets/prefabs/Asteriod.prefab |
|
|
|
|
|
no changes added to commit (use "git add" and/or "git commit -a") |
|
|
|
|
|
$ git diff |
|
|
|
|
|
$ |
|
|
``` |
|
|
|
|
|
This is because `m_Modification` has been altered, even though the prefab |
|
|
is still the same in every way. |
|
|
At this point, you should `git chekout` the file back, unless you want to |
|
|
introduce needless non fast-forward merges in your history. In fact, |
|
|
the _UnityYAMLMerge_ tool seems to be ignoring this section entirely. |
|
|
|
|
|
This is **complete nonsense**, this is **madness**, and it makes me **sad**. |
|
|
**[/rant]** |
|
|
|
|
|
EDIT: |
|
|
Unfortunatly, PyYAML seems to choke on some Unity scenes. It's not celar to me |
|
|
weather PyYAML has a bug or if Unity is not following the spec. |
|
|
|
|
|
``` |
|
|
yaml.scanner.ScannerError: mapping values are not allowed here |
|
|
``` |
|
|
|
|
|
See: |
|
|
- http://answers.unity3d.com/questions/1071634/error-unable-to-parse-yaml-file.html |
|
|
- http://forum.unity3d.com/threads/scene-files-invalid-yaml.355653/ |
|
|
|
|
|
### .gitignore (optional) |
|
|
|
|
|
You may use the following .gitignore file. Up-to-date definitions can be found |
|
|
on [github](https://github.com/github/gitignore). |
|
|
|
|
|
Thanks to all the contributors for putting these files together. |
|
|
|
|
|
``` |
|
|
# |
|
|
# == Windows == |
|
|
# |
|
|
|
|
|
# Windows image file caches |
|
|
Thumbs.db |
|
|
ehthumbs.db |
|
|
|
|
|
# Folder config file |
|
|
Desktop.ini |
|
|
|
|
|
# Recycle Bin used on file shares |
|
|
$RECYCLE.BIN/ |
|
|
|
|
|
# Windows Installer files |
|
|
*.cab |
|
|
*.msi |
|
|
*.msm |
|
|
*.msp |
|
|
|
|
|
# Windows shortcuts |
|
|
*.lnk |
|
|
|
|
|
# |
|
|
# == Unity == |
|
|
# |
|
|
|
|
|
/[Ll]ibrary/ |
|
|
/[Tt]emp/ |
|
|
/[Oo]bj/ |
|
|
/[Bb]uild/ |
|
|
/[Bb]uilds/ |
|
|
/Assets/AssetStoreTools* |
|
|
|
|
|
# Autogenerated VS/MD solution and project files |
|
|
ExportedObj/ |
|
|
*.csproj |
|
|
*.unityproj |
|
|
*.sln |
|
|
*.suo |
|
|
*.tmp |
|
|
*.user |
|
|
*.userprefs |
|
|
*.pidb |
|
|
*.booproj |
|
|
*.svd |
|
|
|
|
|
|
|
|
# Unity3D generated meta files |
|
|
*.pidb.meta |
|
|
|
|
|
# Unity3D Generated File On Crash Reports |
|
|
sysinfo.txt |
|
|
|
|
|
# Builds |
|
|
*.apk |
|
|
*.unitypackage |
|
|
``` |
|
|
|
|
|
## Conclusion |
|
|
|
|
|
I can't believe how hard it is to diff and merge some types of files in Unity, |
|
|
even after doing all that. |
|
|
How do Unity developers review their changes before committing them? |
|
|
How do they handle even trivial merge conflicts? |
|
|
|
|
|
Git + Unity is full of spiders. And snakes. And they're on fire. |
|
|
|
|
|
Sources: |
|
|
- https://www.reddit.com/r/Unity3D/comments/39bdq5/how_to_solve_scene_conflicts_with_unitys_smart/ |
|
|
- http://kihira.uk/unity-and-git-getting-them-to-play-nicely/ |
|
|
- http://stackoverflow.com/questions/18706456/can-i-make-pyyaml-apply-one-tag-directive-to-multiple-documents-in-one-stream |