For the last few weeks I’ve been experimenting with different approaches to collaborative development and version control with Unity. If you have any experience at all with Unity then you know that right out of the box, it’s not VCS-friendly at all: the Library folder is a version control system’s worst nightmare.
A couple versions back, Unity exposed a new editor property which changes the format of the library to something more friendly to external VCS like Subversion or Perforce: instead of incomprehensible metadata files in the Library folder, you’ll now get a .meta file alongside every asset in your Assets folder. This external VCS method is straightforward enough, but I ran into some fiddly problems with it, and thought it might prove useful to share my solutions.
Enabling External VCS
First, a quick review of properly setting up your Unity project. Before you set up external VCS, you need to go to Edit -> Project Settings -> Editor, and set Version Control to Meta Files. This setting generates the .meta files in the Assets folder like I just described.
In the same window, you’ll probably also want to set Asset Serialization to Force Text. Unity saves out assets like prefabs and scenes as binary files by default, which means you can’t merge or diff them; this setting makes Unity save prefabs and scenes (and some other things) as text files instead. You’ll still want to use caution when merging, but you at least have the option now!
I use the phenomenal TextMate for all my coding. (Yes, I know Unity ships with MonoDevelop, but I can’t stand MonoDevelop.) A typical way to set up a TextMate project is to add a folder, rather than individual files. Once you’ve enabled Meta Files, though, you’ll see those in the project view as well, and that sucks.
Fortunately, TextMate is awesome! If you right-click a folder in the project drawer and choose Show Information, you’ll see a window where you can set a File Pattern. This is just a regular expression filter on files that appear in that folder. All you need here is !meta to hide all the .meta files!
My first external VCS experiment was with Perforce. The major issue here is that Perforce uses your file system’s read-only flag to lock files that you don’t have checked out, but Unity doesn’t really care about that flag (it’ll just stomp it if you try to edit something). It’s therefore easy (and natural) to change a bunch of files that are marked read-only; when you switch back to Perforce, you won’t see any pending changes because you have nothing checked out. This easily leads to missed check-ins, which means someone who gets your changes is probably going to have a broken project.
You can kind of work around the issue by adopting a convention: before submitting any changelist, first diff the entire Assets and ProjectSettings folders (latest revision vs. workspace revision). This will catch files that have been added, removed, renamed, or modified, and give you an opportunity to mark for add, mark for delete, move, or check out as needed. You still have to do it manually (which means you can forget) and it’s kind of slow, especially if you have a) a large repository, b) a lot of changes, and/or c) many users accessing the server.
My second experiment was with Mercurial. Unlike Perforce, Mercurial is a distributed version control system, a concept which has been around for a few years but with which I had no prior experience.
For managing a Unity project, Mercurial (shorthand: hg) has so far beaten the pants off of Perforce.
The key benefit is that you don’t have to check anything out. Hg never marks anything read-only, and it effectively tracks your changes for you. Since it’s so easy to touch numerous files in Unity for even simple edits, this behavior is a godsend. I can make some changes in Unity, then start up my hg client and see the complete list of changes I made. There’s no opportunity to forget to check something out (or forget to submit it).
Another benefit is the ease with which you can create new repositories, and how those repositories are stored. In a VCS like Perforce you have a central server, and clients connect to it. The client (P4V) is easy enough to use, but the server is a complex piece of software with a ton of configuration and security options… setting one up is kind of a PITA. By contrast, an hg repository is just a folder on your computer (or on a remote computer). You effectively tag the folder as a repository (using your hg client), and boom! You’re done.
The first hg client I tried was MacHg. It’s free and open-source (like hg itself), fully-featured, well-designed and nicely polished. To create a new repository you just drag a folder from Finder and drop it in MacHg, answer a couple simple questions, and you’re good to go. You can clone that repository to a remote machine and voila, you have a server!
I don’t have anything bad to say about MacHg, but this weekend I discovered and promptly migrated to its big brother, SourceTree by Atlassian (they of JIRA fame). SourceTree is just as polished as MacHg (and it’s also free!) but it has a couple of features that really make it stand out for me.
First, SourceTree integrates natively with Atlassian’s online code-sharing community, Bitbucket. You can link SourceTree with your Bitbucket account and create and manage remote repositories right from within SourceTree, which is super-convenient.
The other great feature in SourceTree is its Bookmarks window, which serves as a sort of dashboard to all your repositories. Here’s what it looks like:
The thing I love about this is actually small, yet super-useful. See the icons and numbers beneath Fail-Deadly? Those indicate (from left to right) that I have one changed file and two new files since my last local commit, that my local repository currently reflects the “default” branch, and that I have one outstanding changeset that’s been committed to my local repository but not yet pushed to the remote repository (on Bitbucket in this case).
It seems like a small thing, but having this kind of information clearly exposed really helps cut down on stupid problems like failing to check in some files, and breaking everyone else’s project, because you didn’t realize those files had even been changed.
One thing you need to be aware of, regardless which external VCS you use, is that there are some files in the Unity project directory that you do not want under version control. When you’re using Meta Files, you do not want to put the Library folder under VCS, and you’ll want to make sure you’re excluding the Temp folder as well (which is auto-generated when Unity opens the project, and cleaned up when the project is closed). In SourceTree you can simply locate these two folders, right-click and choose Ignore. Those paths will be added to the .hgignore file in the root of the repository, and will no longer be managed by hg. (If you have other files in the Unity project folder that need to be ignored — I often keep a Builds folder for example — you can ignore them in the same way.)
Sharing and Backup
I’ve been worried for a while now that I didn’t have any kind of offsite backup of any of my projects. Naturally I have backups on external drives, but if my apartment building got vaporized by alien invaders (like they do) I’d still have lost everything.
There are two key differences though:
- Github only hosts git, while Bitbucket hosts both git and hg
- Github is free only for public repositories, while Bitbucket also offers free private repositories
Since I’m using hg, and want to keep my repositories private — at least for the time being — Bitbucket is the natural choice for me. (It’s worth noting, though, that the SourceTree client also supports git repositories and can natively connect to Github as well.)
Setting up a repository with SourceTree and Bitbucket is easy:
- Drag the Unity project folder into the SourceTree Bookmarks window to create the local repository
- Add and commit all the files in SourceTree
- Open the repository Settings (on the far right of the toolbar)
- Add a remote repository
- Enter the URL of your Bitbucket repository, and check “Default remote”
- Push your changes to populate the remote repository
Conclusion (For Now)
These experiments have only gone on for a few weeks (and I just started using SourceTree and Bitbucket today) so there are plenty of stones yet unturned, I’m sure. But it’s a start, and it makes me think that the dream of a properly version-controlled, collaborative Unity project that doesn’t rely on Unity Asset Server (and isn’t a giant headache) is actually achievable!
(Oh and by the way: I’m available for contract work for Unity programming and project development. Hit me up if you’re interested!)