Toolbox Thursday

Version Control for Solo Game Devs: Git With LFS, Unity Version Control, Diversion and Perforce Compared

Git works great for code but breaks on game art. Compares Git with LFS, Unity Version Control, Diversion, and Perforce Helix Core for solo devs and small teams.

Tools · By Game Design Jar · · P.10

Version control feels solved once you’ve used Git for a while at a day job. Then you point it at a folder full of .png files, Unity scene files, and a couple of gigabytes of .wav recordings, and it falls over in ways your code projects never did. This is the specific problem game projects have, and the four tools below each solve it a different way.

The problem: game projects break Git in ways code projects don’t

Git was mostly built to track changes in text files. It compares files line by line, works out what changed, and stitches two people’s changes together automatically most of the time. That’s what a merge is: Git reading two versions of a file and combining them because it can tell which lines are new.

A game project isn’t mostly text. It’s sprite sheets, tilemaps, audio clips, 3D models, and Unity’s own scene and prefab files, which are technically text (YAML, if you’re curious) but get rewritten by the editor every time you touch them, even if you only moved one object. None of that compares line by line in any useful way. On top of that, those files are often huge, and Git was never designed to store huge files efficiently. It keeps every version of every file in the repo’s history, forever, which is fine for a 200-line C# script and a disaster for a 40MB .psd.

That’s the whole problem in one sentence: Git assumes files are small and mergeable, and most of a game project is neither.

What “binary files don’t merge” actually means for your art and scenes

When two people edit the same text file, Git can usually merge their changes because it understands lines. When two people edit the same binary file, like a .png or an .aseprite file, Git can’t read inside it. It can’t tell what changed, only that the file is different. So when both versions get committed, Git can’t merge them. It just asks you to pick one, and whoever’s version loses has to redo the work by hand. Then again, if two artists are both tweaking the same sprite, you might have a communication problem rather than a source control one.

That’s annoying for a sprite. It’s much worse for a Unity scene, because a scene file changes almost every time anyone opens it in the editor, even without meaningful edits, since Unity reorders and regenerates internal IDs as part of normal editing. Two people who both touched the same scene in the same day will almost always produce a conflict Git can’t resolve cleanly, even if their actual changes don’t overlap at all.

The practical fix isn’t a smarter merge tool. It’s not merging those files at all: only one person edits a given scene or prefab at a time, and everyone else waits or works in a different scene. That’s the exact feature Git doesn’t have built in, and it’s the main thing that separates the tools below.

Git with LFS: how it works, what it costs you in setup, who it suits

Git LFS (Large File Storage) is an extension that fixes the storage side of the problem, not the merge side. When you tell it to track a file type, Git stores a small pointer file in the actual repo and pushes the real file content to a separate LFS storage server. Your repo history stays small even though your project isn’t.

Setup is a few commands: install git-lfs, run git lfs install once per machine, then git lfs track for each file type you want handled this way (more on the actual patterns further down). The tracking rules get saved into a .gitattributes file that you commit along with everything else, so anyone who clones the repo gets the same behavior automatically.

GitHub, GitLab, and similar hosts include a modest amount of LFS storage and bandwidth for free, then charge for more as your repo grows, usually through storage add-ons or higher-tier plans.

I use Git with LFS for Crown Code Chronicles, and the first version of that setup was wrong. I committed a batch of .aseprite files before I’d added the tracking rule, which meant they went into the repo as regular Git objects instead of LFS pointers. Fixing it meant rewriting history to pull those files back out and re-add them properly, which is not a fun afternoon. If you’re setting this up from scratch, write your .gitattributes file before your first commit of any art asset, not after.

The limitation to know going in: LFS solves storage, not locking. Two people can still “win” a conflict on the same scene file and overwrite each other’s work, exactly like plain Git, because LFS doesn’t add any concept of one person having exclusive access to a file. Git does have an LFS locking feature, but it’s not something most teams set up or remember to use consistently.

Who it suits: solo devs and small teams who already know Git from other work, are comfortable with a command line or a Git GUI, and whose projects are more code-heavy than asset-heavy. If most of your team’s day is spent in .cs files with a manageable amount of art and audio, this is the easiest option to live with.

Unity Version Control (formerly Plastic SCM): file locking and the Unity editor integration

Unity Version Control is Unity’s own source control product, built into the editor as a Version Control window rather than a command line tool you switch to separately. The feature that actually matters for game projects is file locking: you check out a scene or prefab before editing it, which locks it for everyone else on the project until you check it back in. Nobody else can accidentally overwrite your changes, because the tool won’t let them save over a file you have locked.

That directly solves the problem plain Git doesn’t: instead of discovering a conflict after two people have both edited the same scene, the second person finds out immediately that the file is locked and knows to work elsewhere until it’s free.

There’s a free tier aimed at small teams and small repositories, with paid tiers scaling up by storage and user count. The tradeoff is that it’s a separate account and service from GitHub or GitLab, with its own workflow model that doesn’t map one-to-one onto Git branching if that’s what you’re used to, and there’s less community documentation and fewer Stack Overflow answers floating around compared to plain Git.

Who it suits: teams (even two people) working heavily in Unity’s scene and prefab files, and especially teams where not everyone is comfortable with a command line, since the check-in and check-out workflow lives entirely inside the Unity editor UI.

Diversion: cloud-first, no LFS setup, and what you give up

Diversion is a newer, cloud-native version control product aimed specifically at game teams. The pitch is that it handles large binary files natively, without you needing to configure LFS tracking patterns yourself, and it offers file locking similar to Unity Version Control but isn’t tied to Unity specifically, so it works the same way if part of your project lives outside the engine (external tools, marketing assets, whatever else).

Because it’s cloud-first, cloning and syncing large projects can be fast, and there’s less to set up than Git plus LFS plus a .gitattributes file you have to get right yourself.

What you give up is maturity. It’s a smaller, newer product, so there’s less community knowledge, fewer tutorials, and fewer people who’ve hit your specific edge case before you. You’re also more dependent on their cloud infrastructure for day-to-day work, which is a different kind of risk than local Git, where you always have a full copy of the repo on your own machine even offline.

Who it suits: small teams who want locking and low setup friction without wanting to become experts in Git internals, and who are comfortable trusting a newer company with their whole project history.

Perforce Helix Core: why studios use it and why a solo dev probably shouldn’t

Perforce Helix Core is the version control system most larger studios actually use, especially on Unreal Engine projects, since Epic ships built-in Perforce support and a lot of the engine’s own tooling assumes it. It works completely differently from Git: instead of everyone having a full copy of history (distributed), Perforce keeps one central server as the source of truth, and everyone checks files out from it and locks them while editing (centralized). Locking is core to how it works rather than an add-on, which is why it holds up on huge multi-terabyte projects with hundreds of contributors.

There’s a free tier for a small number of users, which is actually usable for a tiny team, but the overhead comes from needing an actual server, either self-hosted or through a hosted option, plus a workflow that’s meaningfully different from branch-and-merge if that’s what you’re used to.

For a solo dev, this is a lot of infrastructure for no real benefit over Git with LFS or Unity Version Control at that scale. It’s worth knowing about mainly because you’ll meet it if you ever work with Unreal or join a bigger team, not because you should set it up for a project you’re building alone.

The .gitignore and .gitattributes setup that saves you a broken repo later

If you’re going with Git and LFS, get these two files right before your first commit. This is written for Unity 2022 LTS and Unity 6; the folder structure Unity generates hasn’t changed across that range.

Your .gitignore should exclude everything Unity regenerates locally, since none of it needs to be tracked and all of it bloats the repo: [Ll]ibrary/ [Tt]emp/ [Oo]bj/ [Bb]uild/ [Bb]uilds/ [Ll]ogs/ [Uu]serSettings/ *.csproj *.sln .vs/

The brackets are worth keeping. Unity doesn’t always generate these folders with the same capitalization, and on macOS and Linux Obj/ won’t match a folder called obj/. On Windows you’d never notice, right up until someone on a Mac clones the repo.

One thing that must not go in there: .meta files. Unity creates one next to every asset, and they’re how it remembers which file is which. Ignoring or deleting them is the fastest way to break a project for everyone else on it, so let them get committed like any other tracked file.

Your .gitattributes should tell LFS which file types to store as pointers:

*.png filter=lfs diff=lfs merge=lfs -text *.psd filter=lfs diff=lfs merge=lfs -text *.aseprite filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text *.mp3 filter=lfs diff=lfs merge=lfs -text *.fbx filter=lfs diff=lfs merge=lfs -text

You can also point Unity’s own YAML merge tool at scenes and prefabs:

*.unity merge=unityyamlmerge -text *.prefab merge=unityyamlmerge -text

Two caveats on those last two lines, and the first one bites people. merge=unityyamlmerge is just a name: it does nothing until you register a merge driver called unityyamlmerge in your git config, pointing at the UnityYAMLMerge tool that ships inside your Unity install. Skip that step and Git won’t complain, it’ll just quietly keep failing merges the same way it did before, while your .gitattributes implies otherwise. Second, even set up correctly, it won’t resolve most real scene conflicts. Treat it as a fallback for the easy cases, not as permission to have two people editing one scene.

Commit both files before you add your first sprite or sound effect. If you’re pulling art from the site’s guide to pixel art tools or building out your audio from free sound effect libraries, those file types are exactly what needs to hit LFS from the start, not after your repo already has them baked in as regular Git objects.

Backups are not version control, and version control is not a backup

These get treated as the same thing and they aren’t. Version control tracks the history of changes and lets people work in parallel without stepping on each other. A backup protects you against losing everything at once, like a dead hard drive, a deleted account, or a corrupted repo.

A local Git repo on your laptop with no remote is not backed up. If the laptop dies, your history dies with it. Pushing to GitHub or GitLab covers most of that risk, but it’s not a full answer either, since accounts get suspended, mistakes get made, and “the cloud” is still a company’s server that can go wrong.

The fix doesn’t need to be complicated: push your work to a hosted remote as your day-to-day habit, and separately keep an occasional full copy of the project (a zipped folder is fine) somewhere else entirely, like a different cloud storage account or an external drive. That second copy is the one that saves you if the first one goes wrong in a way you didn’t expect.

Which should you pick: solo dev, two-person team, art-heavy project, tight budget

Solo dev, mostly code with a manageable amount of art: Git with LFS. It’s free at small scale, and if you already know Git from other work, there’s nothing new to learn except the .gitattributes setup above.

Solo dev who doesn’t want to touch a command line: Unity Version Control. The free tier covers a single person easily, and doing check-ins through the editor window is a smaller learning curve than Git if you’ve never used it.

Two-person team: Unity Version Control or Diversion, specifically for the locking. The moment a second person is editing scenes and prefabs alongside you, losing someone’s afternoon of work to an unresolved binary conflict stops being hypothetical.

Art-heavy project (lots of scenes, prefabs, big sprite sheets): whichever tool gives you locking, which rules out plain Git with LFS as your only line of defense. The actual pain point on art-heavy projects isn’t repo size, it’s people overwriting each other’s binary files, and only locking fixes that directly.

Tight budget: Git with LFS using GitHub or GitLab’s free tier, or Unity Version Control’s free small-team tier. Both cover a solo dev or a very small team without paying anything, as long as your repo doesn’t outgrow the free storage limits.

None of these tools fix a bad habit, though. Whatever you pick, commit often, in small chunks, and don’t let a week of unstaged changes pile up on your machine. That’s true whether you learned it from a tutorial or, like most of this list for me, from watching an .aseprite file get mangled by a history rewrite once and deciding not to let it happen twice.

version controlgitunitydev toolssolo dev workflow

Registration card

Register to receive one focused post every weekday — engine tutorials, marketing tactics, and design craft. Free, unsubscribe anytime.

No spam. We never share your address. No purchase necessary — it's free.