Git lfs file locking
This article is an accompaniment to a presentation I am giving at GDC 2024. The slides can be downloaded here.
Git lfs file locking is a key feature for game development using modern git, although it is currently underutilised.
A lockable file, in git terms, is a file which can only be edited by one person at a time - perfect for binary files, like art assets, and therefore an incredibly useful tool for game development.
A majority of large game development studios use Perforce over git, despite the fact that, nowadays, git can scale, and has the features to match Perforce, as well as a vibrant ecosystem of tooling.
My presentation, and this guide, are an attempt to provide an alternative - to show game developers that git is perfectly capable of handling what you need.
The below is primarily a guide for technical users, setting up and using git lfs file locks via command line, although there is a discussion in how to make this accessible for less-technical users in my presentation, as well as in the Making It Accessible section below.
LFS and File Locking
Git lfs file locking has been introduced in mainline git since 2020 - although an early version was released in Git 2.0.0 in 2016.
Git lfs has has been described in a range of places online, so I won't re-iterate here.
But as a short summary: a file which is managed by git lfs (Large File System) is actually stored as a "pointer", which is then de-referenced ("smudged") when you need the actual file contents.
This happens automatically with all modern git clients. In the past, it was common to run into issues and needing
Git lfs file locking, despite its name, does not require that a given file be marked as an lfs file in order to lock it, although interacting with the file locking mechanism is done through the git lfs
subcommand.
How locking works
A lockable file is read-only by default, and in order to edit the file you must ask the server if you can acquire the "lock". Only one person can acquire a lock, per file, at a time. If a second person requests the lock, the request will be rejected unless the first person has unlocked the file.
Naturally, commands interacting with the locking mechanism must typically query a server. All modern git hosts support both lfs and file locking.
One important caveat: lockable files in git are locked across all branches, as they are locked by filepath only. This is in contrast with Perforce, where files can be locked per branch.
Setting up
Files which you want to be lockable need to be marked as such in your .gitattributes
file.
You can do so manually, by editing the file to add the lockable
attribute to any relevant filetypes:
*.psd filter=lfs diff=lfs merge=lfs -text lockable
shared
sharedtextfile.txt lockable
Note: You don't need to mark a file as
lfs
in order to give it the lockable attribute!
Or via the command line:
git lfs track --lockable "*.psd"
git lfs track --lockable --filename "sharedtextfile.txt"
Note: the last command will actually mark the "sharedtextfile.txt" as both lfs and lockable - if you want to create a non-lfs lockable file, you will need to do so manually.
You can set the attribute for any set of files using any regex pattern - meaning this can apply to swathes of files by type, by folder structure, or by any other pattern.
The 3 daily commands
There are three important commands for git:
git lfs locks
- to see which locks are heldgit lfs lock <file>
- to try and acquire a lockgit lfs unlock <file>
- to release a lock
You can investigate these commands and their options by using --help
, like all git commands.
git lfs locks
This command is lists all of the currently locked files by all users.
You can optionally pass in a path --path=<path>
in order to check who owns a specific lock.
Another helpful option is --local
, which lists only your own locks which are cached locally. This can be done offline without a call to the server.
END ACCORDION
git lfs lock <file>
This command is as simple as it gets. You ask the server to "lock" the given file to your user. This blocks attempts by other users to lock or edit the file themselves, until the lock is released.
END ACCORDION
git lfs unlock <file>
Equally simple, this command lets the server know that you are done with the lock, and which to relinquish it.
If you have sufficient permissions, you can --force
the server to unlock a file held by another user. This should be used sparingly, but can be useful if, for example, someone is off on leave and forgot to unlock a file!
END ACCORDION
Bonus Options
As of git lfs 3.0, both lock and unlock support submitting multiple files.
Additionally, there is a HTTP based API if you want to integrate lock or unlock commands into a tool that you are building, and want to avoid command line calls to the git executable.
END ACCORDION
Switching to Rebase
Because git file locking works on file paths, and locks the file across all branches, this means that we are unfortunately unable to git merge
a locked file.
Naturally, this can cause some problems! If you need to update a feature branch or release branch (the target) but the branch you are trying to update from (the origin) has a change to a file which someone holds a lock for... well... you are then unable to lock the file, and therefore unable to use git merge
to update your branch.
Note: this is because of the way that git merge works. The merge commit created includes all of the changes that the origin branch has received since the target branch was last updated in this way.
Fortunately, git has rebase
in order to do what you need.
Instead of merging the updated state of the origin branch into your target branch, you reapply your target branch on top of the origin.
Rebase has been sufficiently explained elsewhere, and a multitude of git experts have explained the other benefits of switching to rebase, so we can move on.
Trunk Based Development and DevOps
One thing that you get, as a side effect of using a rebase-centric workflow, is easier access to being able to achieve trunk based development, which is one of the key technical capabilities of world class devops.
Making It Accessible
One tricky problem in game development is users who don't want or need to know about their version control internals, yet need to be able to work with it - your designers and artists.
For file locking, I would generally recommend that all lockable files have an integration in their DCC (Digital Content Creation) tool of choice, whether it's a game engine or anything else.
However, in general, less-technical users will want a GUI method to interact with git. Whatever method you select should include a way to run the above git lfs file locking commands. To date, I believe the best in this area is git-fork, although I have also had success with TortoiseGit. One interesting option is AnchorPoint, although note that it does not use git's implementation of file locking, but rather its own system.
In order to ensure that rebases are trivial, there are a few simple tricks:
- Reduce the number of required rebases
- Reuse your team's expertise in git through good visual documentation and live support
- Recycle* whatever rebases are still required by using automation
*It's actually just reduce how many user-initiated rebases are needed. but I really wanted to do the 3 R's
All of the above are described in greater detail in my presentation.
References
Most searchers for git lfs file locking stumble upon the proposal docs rather than the final docs, so part of this guide's purpose is to improve the SEO of the actual documentation pages (linked throughout, and below).