Git LFS or Perforce for Unreal in 2024
Before we get into the topic, let's talk about my experience with the two technologies. I use Git at home for personal projects, and I have used both in professional contexts.
I personally prefer working with Git to Perforce, though that’s largely experience and cost related. Git is Free and Open-Source. That, combined with a plethora of self-hostable projects, makes it much easier to run locally on a shoestring budget. It’s also convenient since a lot of my non-game projects are entirely code.
With that said, let's get into which you should choose for a project in 2024.
Git LFS
The FOSS Giant
Git is a giant in the VCS space, and with good reason. When it comes to code, Git is hard to match. Where Git struggles is large binary file management, and the Git LFS project exists to help with this.
Combined with locking support, LFS gets pretty close to full support for a game development workflow. However, it’s not painless yet.
Pros
- Very large community
- Extensive support for CICD
- Branching support
- Submodule support
- Cost
- Servers often have a Web Frontends
Cons
- Locks support is lacking from many GUIs
- Unlocking is not automatic
- Unlock can be slow
- LFS can have significant diskspace overhead
In my personal use of LFS, locking support has been fine, though it lacks the polish of Perforce. Automatic unlocking was always a nice-to-have for me, but in a professional setting, this is a much bigger issue.
In production, we can't expect everyone to be comfortable with command line interfaces, and the lack of locks support in most prominent GUIs really hurts Git here. There isn’t a good way to release locks for non-technical users outside of a Git plugin in the editor.
I’m not considering plugins at this point, because the issue is none are perfect — this includes the Perforce support the engine ships with. When things go awry, Git becomes a bit of a chore to fix up.
That's also not my only gripe with locking support. I recently encountered an issue where a project using GitHub was taking multiple seconds to release each lock. I'm not sure of the root cause, but with over 8000 locks, unlocking was going to take me 12-24 hours, which is completely unacceptable. I've never encountered on my personal project in Gitea though, so your mileage may vary. I ended up writing an automatic unlock script submits many unlocks at once. It's not perfect, and 70% CPU utilisation during an unlock is excessive, but it works, unlocks quickly, and isn't too hard to have non-technical users run after a push.
Maintaining branches can be a pain, even with this tool, though. Locks are global in Git LFS, meaning two branches share locks, which is good as it prevents merge conflicts later, but let's look at an example of a main development branch and a feature branch. When working on the feature, the script above will unlock even though the main development branch doesn't have the changes. Having unlocked but not merged can still lead to a merge conflict later, which is inconvenient, though this can be somewhat rectified by not unlocking on the feature branch and rebase merging into the development branch.
Another issue I listed was LFS overhead. This is a minor to major issue depending on project size, though it is compounded by Epics recent support for partial checkouts of files from Perforce. Linux should support Copy-On-Write files, and Git should use them, though only Windows Dev Drives support this feature at this time.
Perforce
The Industry Standard
Despite my long use of Git, there's a reason Perforce is the industry standard VCS. It lacks feature that I personally want, like branch and submodule support, but it undeniably does binary version control very well and, possibly more importantly, very simply.
On the topic of submodules, Perforce does support Streams that can serve a similar purpose, but I find the git approach far easier to manage. It's convenient to have a way to track and update both private and public Unreal plugins. That might be a personal thing, and your mileage may very well vary here.
Pros
- Large community
- Automatic unlocking
- User friendly
- First-class integration with Unreal
- Support for partial assets
Cons
- Revert requires the network
- Lacks true branching support
- Licensed
Honestly, the biggest downside of Perforce is the licensing cost. It's not overly expensive, and teams up to 5 can even get it for free. There's a reason it's the industry standard.
Git LFS does provide a nicer revert flow with the local copy allowing for faster-than-network reverts, but that's not a dealbreaker by any stretch of the imagination. Reverts don't happen often enough to make it so.
Branch support is a double-edged sword. As a programmer, I like having the option to branch. It allows me to work on code, contribute assets, or test things without polluting the history as I can rebase and squash changes before I merge in. At the same time, this workflow isn't beginner friendly and is prone to error in bigger teams – or at least, the time to resolve the error feels proportionate to the team size. Perforce's more streamlined approach almost prevents these issues, at the cost of sometimes having stray assets get checked into the history. In production, I'd make a case that the latter is acceptable for the productivity boon the team as a whole will get.
Verdict
Both are good options, though I'd recommend Perforce, especially if your team includes anyone who isn’t familiar with Git and a command line. While Git clients have made Git far more accessible in recent years, Git's complexity still lurks beneath the surface, and has an unfortunate tendency of raising its head when LFS is involved.
If you don't mind a bit of wrangling to get Git LFS working and want a cost-saving, Git is a somewhat viable option in 2024. On the other hand, if you value ease-of-use or a standard and well-understood workflow, Perforce is worth the asking price.
Improving Git
Constructive Criticism
My comments on Git may appear relatively scathing for game development, and I'm aware of that. It's the unfortunate fact that in a commercial setting, we care a lot about the things Git isn't perfect at. I don't want that to take away from the amazing work of Git and Git LFS’s developers. They have done, and continue to do, amazing work. Git has gone from unusable for game development to a solid contender with growing pains in a relatively short period of time.
I’m going to continue to use Git for my personal projects. None of the issues are insurmountable for me, and I like the ability to put each plugin and game feature in its own repository.
So now, let's talk about what Git can do to address its biggest shortcoming: locking.
Realistically, we need better GUI support for locking. The LFS team is unfortunately limited in their ability to release on push by Git's lack of a post-push hook with which LFS could use to release locks. With that in mind, an option in popular GUIs for a push that releases locks that are both held by the local user and referenced in commits that were ahead of the remote would make things easier for non-technical users. I've written a script – linked above – but that doesn't reduce the burden far enough to make it accessible to non-programming members of a team.
The issue with the script is a meta one. The concept of branches complicates the unlocking process, but if we constrain our branch use to more closely match Perforce, we can actually get quite close in feature set.