Managing Large Files with Git LFS
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
Managing Large Files with Git LFS: A Comprehensive Guide
Introduction: The Git Repository Problem
Git was originally designed to manage source code—text files that are relatively small, highly compressible, and line-based. Because Git tracks the history of every file by creating snapshots of its entire state, it excels at diffing text and merging changes. However, when you introduce large binary files into a Git repository, the performance of the system begins to degrade rapidly. If you store a 500MB high-resolution texture or a compiled binary library in a standard Git repository, every time you commit a change to that file, Git stores a new, full-sized copy of it in your .git directory. Over time, this leads to massive repository sizes that make cloning, pulling, and pushing operations painfully slow for every member of your team.
This is where Git Large File Storage (LFS) comes into play. Git LFS is an open-source extension for Git that replaces large files with tiny text pointers inside the repository. The actual file content is stored on a remote server, such as a specialized LFS server or a cloud storage provider, and only downloaded when you specifically check out the branch or pull the files. By offloading these heavy assets, Git LFS allows your repository to remain lightweight and fast, regardless of the size of the assets you are tracking. In this lesson, we will explore why this is essential for modern development, how to implement it, and how to maintain a healthy repository workflow.
Why Git LFS Matters
In modern software development, particularly in game development, data science, and machine learning, repositories are rarely limited to just source code. Developers frequently need to manage assets like 3D models, audio files, video clips, datasets, and container images. If these files were tracked directly by Git, the repository would quickly reach a size that makes it impossible to clone on standard internet connections.
Furthermore, Git is not optimized for binary file merging. When two people modify a text file simultaneously, Git can often help merge the changes automatically. When two people modify a 500MB binary file, Git cannot merge them; it must choose one version over the other, leading to data loss. Git LFS addresses these performance and storage issues by decoupling the file storage from the version control metadata. This keeps the .git folder slim and ensures that operations like git fetch remain performant.
Callout: The Pointer File Concept Git LFS works by replacing your large files with a small text file called a "pointer." This pointer contains a unique identifier (a SHA-256 hash) of the file, the file's size, and a reference to where the actual content lives on the remote server. When you run a command like
git checkout, Git LFS intercepts the process, checks the pointer, downloads the actual file from the remote storage, and places it in your working directory. This ensures you only ever download the large files you actually need for your current work.
Setting Up Git LFS
Implementing Git LFS is a straightforward process, but it requires coordination across your team. Everyone who interacts with the repository must have the Git LFS client installed on their local machine.
Step 1: Installing the Client
First, verify if you have Git LFS installed by running the following command in your terminal:
git lfs --version
If the command returns a version number, you are ready to go. If not, you will need to install it. On macOS, you can use Homebrew with brew install git-lfs. On Windows, the Git for Windows installer typically includes an option to enable LFS. On Linux systems, you can use your distribution's package manager, such as sudo apt-get install git-lfs.
Step 2: Initializing LFS for a Repository
Once installed, you must initialize Git LFS for each repository where you want to use it. This creates the necessary configuration files and hooks within your local project.
cd /path/to/your/repository
git lfs install
The git lfs install command sets up the necessary Git hooks that allow Git to communicate with the LFS client during operations like commit, checkout, and push.
Step 3: Tracking Large Files
You do not want to track every file with LFS, as this would be inefficient for standard source code. Instead, you specify which file types or specific files should be handled by LFS using the git lfs track command. This creates or updates a file named .gitattributes in your repository root.
# Track all .psd files
git lfs track "*.psd"
# Track all .mp4 files
git lfs track "*.mp4"
# Track a specific large file
git lfs track "assets/data/large_dataset.csv"
After running these commands, you will see a .gitattributes file. It is critical that you commit this file to your repository, otherwise, your teammates will not know which files are configured for LFS.
git add .gitattributes
git commit -m "Configure Git LFS for image and video assets"
Managing the Workflow
Once LFS is configured, the day-to-day workflow feels remarkably similar to standard Git usage. You add files, commit them, and push them to the remote server. However, there are nuances to how the data is transferred that you should understand.
Adding and Committing
When you add a file that matches your LFS patterns, Git LFS automatically intercepts the git add command. Instead of adding the binary file to the Git index, it creates the pointer file and stores the binary locally in a hidden .git/lfs directory.
# Add a large file
git add assets/textures/main_character.png
# Commit as usual
git commit -m "Add main character texture"
Pushing Changes
When you run git push, the Git LFS client scans your commits to identify the binary files associated with the pointers. It then uploads the actual binary data to the LFS storage server before the Git push completes. If the LFS upload fails, the Git push will also fail, ensuring that your remote repository never contains broken pointers that point to missing data.
Pulling and Cloning
When a teammate clones the repository, they will initially only download the pointer files. This makes the initial git clone extremely fast. To download the actual large assets, they must run:
git lfs pull
Alternatively, git checkout commands will automatically trigger the download of the necessary LFS files for the branch you are switching to. This "lazy loading" behavior is the core performance advantage of Git LFS.
Note: The "git lfs pull" vs "git pull" Distinction In older versions of Git LFS, you had to manually run
git lfs pullafter agit pull. Modern versions of Git LFS are much better at automatically fetching the necessary files during checkout. However, if you find that your files are missing or appear as text pointers in your file explorer, runninggit lfs pullis the standard way to synchronize your local working directory with the remote storage.
Best Practices and Industry Standards
To maintain a performant and organized repository, following standard practices is essential. Git LFS is a powerful tool, but misuse can lead to messy history and unexpected storage costs.
1. Track Binary Files Only
The most common mistake is tracking too many file types with LFS. Only track files that are truly large or binary in nature. Never track source code files like .js, .py, .cpp, or .html with LFS. These files benefit from Git's delta-compression and branching capabilities, which are lost if you move them into LFS.
2. Manage Your LFS Quota
Many hosting providers (like GitHub or GitLab) impose storage and bandwidth limits on Git LFS. If you are working on a project with massive assets, monitor your usage. Frequently committing large files that change often will consume your storage quota rapidly. If you have files that change daily and are very large, consider using a separate asset pipeline or an external storage bucket (like AWS S3) for those specific files, rather than Git LFS.
3. Use .gitattributes Consistently
Ensure that your .gitattributes file is maintained and consistent across all branches. If a new developer joins the team and pulls a branch where .gitattributes was not updated, they may inadvertently commit large files to the standard Git history, which is difficult to clean up later.
4. Optimize Large Repository Clones
If you have a massive project with thousands of LFS files, cloning the entire history can still be slow. You can use the --include flag to fetch only specific files or folders, though this is an advanced technique. For most teams, the standard git lfs pull is sufficient.
Warning: Removing Files from LFS Removing a file from LFS tracking is difficult because the file has already been committed to the history. If you decide to stop tracking a file type, you must update the
.gitattributesfile, but the old versions will remain in the LFS storage. To truly remove large files from your history, you would need to use tools likegit-filter-repoto rewrite the entire commit history of the repository, which is a destructive process that requires all team members to re-clone the repository.
Comparison of Git Tracking vs. Git LFS
| Feature | Standard Git | Git LFS |
|---|---|---|
| Storage Location | Inside .git directory |
Remote LFS server |
| Download Speed | Downloads everything | Downloads only required files |
| Diffing Ability | Full support for text files | No support for binary files |
| Repository Size | Grows with every binary commit | Remains small (pointer files) |
| Performance | Degrades as repo grows | Stable performance |
Common Pitfalls and Troubleshooting
Even with a well-configured setup, developers occasionally run into issues. Here are the most common scenarios and how to resolve them.
Missing LFS Objects
If you receive an error stating that an LFS object is missing, it usually means the file exists on the server, but your local client failed to fetch it. You can attempt to force a fetch of all objects for the current commit:
git lfs fetch --all
git lfs checkout
Large Files Committed to Git History by Mistake
If someone accidentally commits a large binary file without LFS, it is now part of the Git history. Simply adding the file to git lfs track afterward does not remove the file from the history. You must remove the file from the index and commit the change, but the file will persist in the .git database. To purge it, you must use a history-rewriting tool. The best approach is to prevent this via pre-commit hooks that check for file size before allowing a commit.
Server Connection Issues
If your push fails, verify your network connection and ensure your LFS server URL is configured correctly. You can check the current configuration with:
git config -l | grep lfs
If the URL is incorrect, you can update it using git config lfs.url <new-url>.
Step-by-Step: Migrating an Existing Repository to LFS
If you have an existing repository that has become bloated because you were tracking large files normally, you can migrate it to LFS. This process requires caution.
- Backup: Always create a full backup of the repository before performing a migration.
- Install BFG Repo-Cleaner: This tool is often faster and easier than
git filter-branchfor removing large files from history. - Remove Large Files: Use BFG to remove the large files from your history.
java -jar bfg.jar --strip-blobs-bigger-than 50M - Configure LFS: After the history is cleaned, run
git lfs installandgit lfs trackfor the file types you just removed. - Re-add Files: Add the files back into the repository. Because they are now tracked by LFS, they will be converted into pointer files.
- Force Push: Since you have rewritten history, you will need to perform a
git push --forceto update the remote repository. Note that this will affect everyone else on the team, who will need to re-clone the project.
Advanced Topic: File Locking
In many creative workflows, two people cannot work on the same binary file simultaneously. Unlike code, where you can merge, you cannot merge two different versions of a 3D model. Git LFS provides a "File Locking" feature to solve this.
By marking a file as "lockable," you ensure that only one person can edit it at a time. When a user wants to edit a file, they run:
git lfs lock path/to/file.psd
This sends a request to the server to lock the file. If someone else has already locked it, the server will reject the request. When the user is finished, they run:
git lfs unlock path/to/file.psd
This prevents the "lost update" problem where two developers spend hours working on the same asset only to have one person's changes overwritten by the other. This is an industry-standard practice for game studios and design agencies.
Summary and Key Takeaways
Managing large files in Git requires a disciplined approach to ensure your development environment remains fast and functional. Git LFS is the industry standard for handling this challenge, providing a clean separation between source code and heavy binary assets.
Here are the key takeaways from this lesson:
- Understand the Pointer System: Git LFS works by replacing large files with text pointers, keeping your core Git repository lightweight and performant.
- Selective Tracking: Only track large binary files with LFS. Keep your source code (text files) managed by standard Git to maintain the benefits of delta-compression and merging.
- Initialization is Mandatory: Every developer on the team must run
git lfs installto enable the necessary hooks. Failure to do so will result in files being committed as raw data rather than LFS pointers. - Use
.gitattributes: This file is the source of truth for your LFS configuration. It must be version-controlled and shared across the entire team to ensure consistency. - Leverage File Locking: For assets that cannot be merged (like binary design files), use the Git LFS file locking feature to prevent concurrent modifications and lost work.
- Monitor Quotas: Large files consume storage and bandwidth. Keep an eye on your hosting provider's limits to avoid unexpected service interruptions or additional costs.
- Handle History Carefully: Migrating a mature, bloated repository to LFS is possible but requires rewriting history. Always communicate clearly with your team before performing such operations, as it will force everyone to re-clone the repository.
By implementing these strategies, you can scale your development projects to include massive assets without sacrificing the speed and reliability of your version control system. Proper repository management is not just about tools; it is about establishing a workflow that supports collaboration, prevents data loss, and keeps the development process moving smoothly.
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons