Skip to main content

Git LFS

Git Large File Storage (LFS) lets you store large binary files outside your repository while keeping them versioned alongside your code. Instead of bloating your repository with multi-megabyte assets, LFS replaces them with lightweight pointer files and stores the actual content in dedicated blob storage.

Why Use Git LFS

Repositories that contain large binary files — images, videos, datasets, trained models, design files, compiled binaries — suffer from slow clones, bloated disk usage, and degraded performance. Every clone downloads the full history of every binary, even if you only need the latest version. Git LFS solves this by:
  • Keeping repositories fast: Only pointer files (a few bytes each) are stored in the repository. The actual file content is fetched on demand.
  • Versioning large files: LFS objects are immutable and content-addressed. Every version of a file is preserved and retrievable.
  • Efficient transfers: Only the LFS objects you check out are downloaded, not the entire history of every large file.

How LFS Works on JJHub

JJHub stores LFS objects in dedicated blob storage. Uploads and downloads use signed URLs and go directly to storage — they are not proxied through the API server. This keeps transfers fast and avoids bottlenecking the API. The flow:
When someone clones or pulls, the reverse happens: JJHub provides signed download URLs and the client fetches objects directly from storage.

Setup

Prerequisites

You need Git LFS installed locally. Since jj uses git as its storage backend, standard Git LFS tooling works with JJHub repositories. Install Git LFS:
Initialize Git LFS in your environment:
This only needs to be run once per user account (not once per repository). It configures your global Git hooks to intercept LFS pointer files during push and pull operations.

Repository Setup

Inside your repository, tell Git LFS which file patterns to track:
Each git lfs track command adds an entry to the .gitattributes file in your repository root. You can also edit .gitattributes directly:
Important: The .gitattributes file must be committed to your repository. This is how collaborators and CI know which files are managed by LFS.

Push and Pull Workflow

Once LFS tracking is configured, your normal workflow does not change. Git LFS hooks handle everything transparently.

Pushing LFS Objects

During the push:
  1. jj exports refs to the git backend
  2. Git detects that scene.psd matches an LFS pattern
  3. Git LFS uploads the file content to JJHub (via signed URLs)
  4. Only the pointer file is pushed to the repository

Pulling LFS Objects

Git LFS downloads the actual file content for any pointer files in your working copy. Files not in your current checkout are not downloaded.

Checking LFS Status

To see which files are tracked by LFS and their status:

Common File Patterns

Here are recommended LFS tracking patterns for common use cases: Tip: Only track files that are genuinely large or binary. Text files, even large ones, are better served by regular git storage since they benefit from delta compression and line-level diffing.

API: Signed URL Endpoints

JJHub provides LFS endpoints for programmatic access. These follow the Git LFS Batch API specification and are used automatically by the Git LFS client.

Request Upload URL

Request a signed URL for uploading an LFS object. Request:
Response:
The upload_url is a signed URL with a short expiry. Upload the file content directly to this URL with a PUT request.

Confirm Upload

After uploading, confirm the upload so JJHub records the object metadata. Request:
Response:

Download URL

When pulling LFS objects, the Git LFS client requests download URLs through the standard LFS Batch API. JJHub returns signed URLs for direct download.

Storage and Retention

  • LFS objects are retained as long as they are referenced by a repository. Deleting a repository removes its LFS objects.
  • LFS objects are content-addressed (identified by SHA-256 hash). Identical files uploaded to different repositories share storage.
  • Storage limits per repository and organization are TBD and will be documented before general availability.

Troubleshooting

”LFS objects not found” on clone

This usually means LFS was not installed or initialized locally:

Large files committed without LFS

If you committed a large file before setting up LFS tracking, the file is stored directly in the repository. To migrate it to LFS:
Note that history rewriting changes commit SHAs. In jj, Change IDs remain stable through rewrites.

Push rejected due to missing LFS objects

If a push fails because the server cannot find referenced LFS objects, push the LFS objects first: