Collaborate using Git VCS
Published 2 years ago
Unity specific git workflows
Git is a free open source, distributed versions control system. With git, all team members have a full copy of the game as it existed at some point in history and in some multiverse(branch). In order to maximize the efficiency of your team it's important to understand what git can and should do, and what actions you should stay away from. This article uses git from the command line, but many GUI tools are available (often with the option to open the command line for advanced features). This article assumes git is setup on your system, if it isn't install it now. We also assume you have some simple knowledge of using git, if you don't you may wish to practice on a simple practice project where you just manage a few text/image files.

Git Setup

Git has a concept of a local and a remote version of the repository. A repository is just the name given to the files under git's management. There are two types of repository, a normal repository and a bare repository. If you use an online git service it uses a bare repository under the hood. Technically normal git repositories can use each other as remotes, but upstream bare repositories are much easier to use because they don't require any special action on the part of the "remote" user. Git can communicate over several protocols, but he most capable one is SSH. If you will be using a service like Github or Bitbucket follow their setup instructions for your team. If you will be using your company's own server, create a bare repository on that server, and use the scheme
as the url of the remote.

IMPORTANT: Unity .gitignore

The gitignore file tells git what file changes it should not track, use the following gitignore derived form the one at github .
#Ignore these directories under the root of the project, they are generated from Assets/ [Ll]ibrary/ [Tt]emp/ [Oo]bj/ [Bb]uild/ [Bb]uilds/ Assets/AssetStoreTools* # Visual Studio cache directory .vs/ # Autogenerated VS/MD/Consulo solution and project files ExportedObj/ .consulo/ *.csproj *.unityproj *.sln *.suo *.tmp *.user *.userprefs *.pidb *.booproj *.svd *.pdb *.opendb *.VC.db # Unity3D generated meta files *.pidb.meta *.pdb.meta # Unity3D Generated File On Crash Reports sysinfo.txt # Builds *.apk *.unitypackage *.unitypackage.meta
The lines beginning with # are comments, the other lines are regexes that will match pathnames relative to the directory of the gitignore file. You can also have gitignore files specific to a particular directory, and one just for your machine. For example, if you are on a Mac, the Finder will automatically create metadata files called .DS_Store. Let's tell git to ignore these files across any repo we create under our username on this machine.
#Tell git to always look in ~/.gitignore_global for exclude paths git config --global core.excludesfile \ ~/.gitignore_global #Create the hidden file .gitignore_global in your home directory if necessary and append .DS_Store to it. echo .DS_Store >> ~/.gitignore_global

Repository Initialization

IMPORTANT: In the Unity Editor Settings, set the
  • Asset serialization mode to "Force Text"
  • Version Control to "Visible Meta Files.
This will allow unity to reconstruct the project when it is copied from one machine to another through version control.
If you don't have a remote repository, create one now. Use your preferred web service, or create a repo on one of your own servers using git init --bare . If your unity project is not created do so now. Insure your working directory is the root of your unity project.
git init . #Manage this directory using git #Create a file called .gitignore using your favourite programmer text editor and stick it in the root of your unity project. Copy the contents of the block listed above in the section Unity .gitignore into this file and save. #Track the newly created gitignore file, if it failed you screwed up somewhere git add .gitignore git add . #track other files in the repo execept those listed in .gitignore git commit -m "Initial commit" #Add the default remote git remote add origin ssh://path/to/your/repo #Push the default branch(master) to the remote (origin) git push -u origin master
Pushing the first time can take a while depending on the size of your files. If you have access to your remote server on your LAN, it is recommended that you do your initial push from on your LAN using a wired connection.
Now each team member can pull the newly created initial commit from the repository. Again if they can do this on the same LAN as the server that would be ideal. If they can't, you can bypass the server entirely for the first replication (subsequent replication is faster because only changes are transmitted).
If your Unity project is small just run:
git clone ssh://path/to/repo
To bypass the server, simply copy the project folder from one machine to another. Remember that git is designed to work distributed, so it looks the same on every machine (except local changes). Don't worry about authentication, that's all stored outside the actual repository, and will be different on the target machine. You should use a file synchronization tool from one machine to another. If you can over a share of two machines wired together, otherwise over an external drive. Wifi is considerably slower, but faster than copying form a server far away. But for the amount of lost productivity, you might as well have cloned from the origin, and saved some setup complexity.
Once you have copied from one machine to another you need to take care of any hiccups that might have occurred in the copy, and sync up with any team member that may have gone ahead of you. Run the following commands
#Remove any odd files that were created, but are not tracked (excluding those files which are ignored) git clean -f git fetch #update the local refs from any remote changes git pull #update the local data git reset --hard origin/master #make sure your repo is on the most recent commit removing any final odd files.
Open Unity and allow it to rebuild any missing files, run the game and insure it is working as intended. If not, check you did not skip any of the above setup steps.
Great, if you add any team members have them do the above steps, now lets look at how to actually use git in Unity.

Git workflows with Unity

If you're not familiar with how to use git, it is sufficient to know that your workflow should be:
git pull #Get the latest changes before starting work #Do work git pull #Get the latest changes before pushing git add ./path/to/your/changes #stage your changes #commit to your changes git commit -m "Your commit message" git pull #grab any last minute changes #Resolve any conflicts git push #Push your changes upstream
Ideally over time you should familiarize yourself with the tool, it's not very big and you can familiarize yourself over the course of an afternoon.

1.Use git flow

The git-flow paradigm is an invaluable tool for teams, it allows iterations of your game to become progressively better without introducing too much strange behaviour into a team members workflow.

2. Avoid conflict

Git is not a replacement for communication between your team, it is an auditor that verifies that communication did not introduce an error. No one likes resolving merge conflicts, the easiest way to resolve them is not to have them. Unity projects have lots of binary files like art assets, they do not merge easily like text files. Avoid conflicting edits on these files at all costs.

3. Edit the same scene separately

For a given scene, create a separate scene file for any editors. Provide each programmer and designer a sandbox scene in which to test creations. Unity can open multiple scenes at once, so to have multiple designers work on the same scene have the designers open the latest version of the "merged scene", when a change is ready to be shown to other team members move it from the team members scene into the merged scene by dragging it from one to the other. The scene merging should proceed exactly like "git flow". Stable changes are moved into a production scene, unstable changes into a merged develop scene, and individual features in the team member's scene.
When you start working on a scene, it may be useful to block out roughly where object should be, to allow collaborating designers to decide roughly where to position objects. This can always be tweaked later.

4. Code files are easily mergeable, but avoid monolithic Behaviours.

Split behaviours into simple individual components. Ultimately, to make your game your game and not just a loose collection of assets and behaviours, there are usually a few monolithic behaviours, that script how a scene plays out. These types of behaviours can't really be generalized, but they can be split and should be the exception not the rule.

Fix badness

Something bad happened, I guess you could just freak out about it, but that won't help fix anything. Lets look at specific types of badness which are counterintuitive to fix.

Pushed insensitive upstream badness

Whoops you pushed some bad commit upstream, better fix that before someone decides to do work on top of it. You can't back out of a bad commit if you've already pushed it, since that would corrupt the remote history. Instead you have to commit on top of your bad commit doing your bad changes in reverse.
You might think that this is the job of the git revert command, but actually git revert is usually overkill for lots of changes, and it doesn't know how to handle merges. Instead let's rollback to an earlier commit. It is a good idea to quit Unity now, since you'll be changing a lot and can't be sure it will be in the same state when you're done.
  1. Note the hash of your current git commit or git-tag it temporarily to allow you to easily refer to it.
  2. If your working tree isn't clean git-stash the changes you will restore later.
  3. git reset --hard nameOfTheLatestGoodCommit
  4. Above we discarded all changes in the HEAD, and moved the HEAD to the known good commit, in other words the working tree is at the point it was when the project was last known to be working. Now reset the index to the bad head we were at before, but don't change the working tree git reset --soft nameOfCommitInStep1
  5. git commit -m "Rollback bad changes"
  6. git push
  7. Hope that there are no conflicts, otherwise merge if possible, this should be possible unless a colleague did stuff specifically right on top of the thing that you did incorrectly. In practice that will never happen as long as you separate scene and asset editing, and code can be merged, so it's not a problem.

Pushed sensitive badness

If you put something sensitive on a public remote like a private key, it is best to reset or rebase now, and ask questions later. You can't use the above strategy because you need those commits to vanish from history. If the introduction of the sensitive material was recent simply reset the problematic branch and force a push with git push -f this will make everyone else on the team irritated because they won't be able to push upstream on that branch now that you've rewritten remote history, but you won't have compromised security, so tradeoffs I guess. As above team members can reset their HEAD to the new origin/branchname you have created using git reset --mixed origin/branchname , this will incorporate your changes but keep any changes they have done in their working tree. If they care about retaining their commits, they should now replay them on top of the new head, using git-rebase. If the commit subtree they were working on became inaccessible when they did the above step, they can find their local commits using git-reflog inaccessible commits are kept locally until garbage collected. This will be a pain for everyone so only do it if you absolutely have to.

Introduced badness but don't know where

If you somehow broke something but you don't know how, use git-bisect. This too uses a binary search to determine where the problematic commit occurred, simply tell it whether the current commit has a problem or not using git bisect good and git bisect bad respectively, repeating until the bad commit is found.
Abram Wiebe
Freelance Software Developer - Programmer