Git for large files at any scale
Introducing Crab: one Git history for code, models, datasets, and media, with the heavy bytes stored directly in your own cloud object store.
Change the scale. Keep the same Git workflow.
Model one new version of a large repository. The illustration separates its logical size from the chunk data that changed.
Git already gives teams the best shared vocabulary for software: add, commit, branch, push, clone.
The problem is no longer Git’s interface. It is the weight behind it.
A modern commit may need to name source code, a 40 GB checkpoint, a changing dataset, and thousands of binary assets. Moving the heavy files into a second catalog lightens Git, but splits the state the team must reproduce.
Today we are launching Crab around one premise:
Keep one Git history. Put the heavy bytes directly in your object store. Move only the content a person or job actually needs.
Crab stores compact pointers in Git. Content-defined chunks, immutable packed objects, and reconstruction metadata live in S3, Google Cloud Storage, Azure Blob Storage, or an S3-compatible store.
No Crab data server sits between your team and the bucket.
INTERACTIVE REPOSITORY WORKBENCH
One commit. Two data paths.
Select a stage to see what Git records, what Crab prepares or stores, and which state becomes true.
crab track '*.safetensors'The rule decides which path Crab manages. It does not create a second repository.
GIT LANE
Commit graph
- README.md → bytes
- train.py → bytes
- encoder.safetensors → not committed yet
CRAB LANE
Tracking rule
- No chunks prepared
- Object storage unchanged
STATE NOW TRUE
Working tree knows the tracking rule.
The file became infrastructure
“Large file” used to mean an occasional installer or video. Now binary content is often the product:
- ML: checkpoints, embeddings, tokenized corpora, evaluation sets
- Games: textures, audio, meshes, maps, engine caches
- Data: Parquet, Arrow, databases, scientific arrays
- Media: frames, simulation output, layered source assets
- Agents: more branches, worktrees, experiments, and short-lived states
Git still provides the right coordination unit. A commit is reviewable. A branch is collaborative. A checkout selects an exact historical state.
The physical representation is where the economics break down.
A small edit can create a large object
Imagine four 8 GB checkpoints. Each new version changes only 0.5 GB of encoded bytes.
Ordinary Git still sees four complete blobs. A content-addressed data plane can keep the shared regions once and add only the new identities.
Four checkpoints: full objects or unique chunks
Scroll horizontally to explore the full diagram →
That example is illustrative, not a savings guarantee. Recompressed or encrypted data may change broadly even when the logical edit is small.
One history, two data paths
Crab extends Git at two existing boundaries:
- A long-running
filter=crabprocess turns selected files into backed pointers during add. git-remote-crabimplements the remote-helper protocol for acrab://remote.
The commit remains the join:
commit 8fc2
├── src/train.rs ───────────── ordinary Git blob
└── models/encoder.bin ─────── Crab pointer: f41a… · 40 GB
│
└── recipe → shards → xorb ranges
│
└── your bucketGit can inspect names and history without downloading model bytes. The object store can hold immutable content without understanding branches or worktrees.
SYSTEM TRACE 01
One history, two physical data paths
Classify the path
Crab reads Git's own attributes to decide whether a file stays ordinary or uses the large-file representation.
| Layer | Owns | Deliberately does not own |
|---|---|---|
| Git commit + tree | names, history, branches | large-file placement |
| Crab pointer | full-file identity + size | chunk location |
| Recipe + shards | ordered reconstruction terms | branch visibility |
| Xorbs | immutable packed chunks | file names or refs |
| Ref journal | which complete commit is visible | hydration policy |
The user sees one repository. The implementation gets two lanes with different scaling behavior.
Reuse starts with identity
Whole-file storage can reuse content only when the complete file is identical. Fixed-size chunks improve the unit, but an insertion near the front shifts every later boundary.
Content-defined chunking can resynchronize after an edit. Distant, unchanged regions retain their earlier identities.
CONTENT-DEFINED CUTTING MAT
Change the edit. Watch boundaries recover.
A fixed-size replacement disturbs one region
The byte length stays stable, so the next content boundary can recover quickly.
Crab hashes chunks with BLAKE3 and packs new chunk data into immutable xorbs. Shards locate each chunk range. A file recipe preserves reconstruction order.
That creates reuse across several dimensions:
- Versions: a checkpoint can retain regions from its parent.
- Branches: experiments can share their common base.
- Paths: a copy or rename does not imply another physical copy.
- Contributors: identity does not depend on the uploader.
- Time: a new version can reuse content introduced months earlier.
The invariant is intentionally modest: identical chunks have identical identities, so Crab does not need to store proven content twice in one repository data plane.
Add prepares. Push publishes.
A pointer is useful only when its client can later publish a complete reconstruction recipe. That makes add a transaction, not a text substitution.
Add creates a backed pointer
crab add streams the file, chooses stable chunk boundaries, hashes content, flushes local staging, writes the pointer, and then updates Git’s index.
COMMAND TRACE 02
crab add chooses and prepares the representation
Resolve Git attributes
Each path is classified with the same .gitattributes rules that Git uses for its filter process.
Three decisions remain independent:
- Git and
.gitignoredecide whether a path enters history. .gitattributesdecides whether Git stores bytes or a Crab pointer.- Hydration policy decides whether this worktree materializes the bytes.
Push makes immutable data durable first
A branch must never point to a missing chunk. Crab therefore prepares every immutable dependency before it changes the destination ref.
TRANSACTION TRACE 03
Push makes dependencies durable, then moves the ref
Discover the complete push closure
The helper walks reachable Git objects and parses Crab pointers before opening any large-file metadata path.
The expected-old comparison is the authority: move the ref only if it still names the commit this push planned against.
Before publication, an interruption can leave safe immutable orphans. After publication, the visible state is complete.
Clone history. Hydrate bytes.
Clone should not mean “download every large file anybody ever committed.”
A source-only CI job may need no checkpoints. An evaluation job may need one model and one test set. An artist may need the current map but not every cinematic.
Crab clone is lazy by default:
# Fetch Git history and compact pointers.
crab clone crab://team-bucket/vision-search
# Materialize only this job's working set.
crab hydrate 'models/encoder.safetensors'
# Return clean managed files to pointer form.
crab dehydrate 'models/*.safetensors'READ TRACE 05
A pointer becomes byte-identical worktree content
Git supplies the pointer
The Git graph stays compact and reveals the content identity without embedding the large file in the pack.
Hydration is a proof-oriented read path:
- Read the pointer identity and logical size.
- Resolve every ordered recipe term.
- Coalesce the required xorb ranges.
- Read from verified cache or canonical origin.
- Reconstruct chunks in recipe order.
- Verify the complete output.
- Materialize only after verification succeeds.
The result is byte-identical, or hydration returns an error. A partial output does not quietly become the worktree file.
Scale has five axes
Repository size is only one kind of pressure.
| Axis | What hurts | Crab’s boundary |
|---|---|---|
| One huge file | memory + transfer | bounded streaming + range reads |
| Deep history | repeated versions | chunk identity + lazy hydration |
| Many files | metadata + request fan-out | packed xorbs + shards |
| Many writers | conflicting publication | per-ref lock + expected-old ref |
| Many repositories | service + database overhead | repository prefixes in object storage |
One huge file
Hashing, chunking, upload, and reconstruction stream through bounded buffers. The whole file does not need to fit in memory.
Deep history
The full-file hash changes with every version, preserving exact identity. Stable chunk regions can still be reused physically.
Many files
Xorbs avoid creating one tiny object-store object per chunk. Shards preserve lookup and ordering without using path names as the physical layout.
Many writers
Different refs can prepare in parallel. Writers to the same ref reconcile at the expected-old transaction; uploading bytes never grants permission to replace history.
Many readers
Verified caches can accelerate reads, but the bucket remains authoritative. A cache miss changes latency, not correctness.
Failure is boring on purpose
Machines stop mid-plan. Credentials expire. Another writer moves the branch. A response disappears after a successful commit.
Crab assigns each failure to a boundary with a visible result and a safe next action.
FAILURE TRACE 04
Every interruption has an explicit visible result
Uploads are not publication
Crab may write any number of immutable objects while the old branch tip remains the only reachable state.
| Failure point | What readers see | Safe response |
|---|---|---|
| Local staging fails | no backed pointer added | fix local access; add again |
| Immutable upload stops | old ref | restore access; retry |
| Closure verification fails | old ref | repair missing dependencies |
| Expected-old check loses | winning ref | fetch, reconcile, push a new plan |
| Response is lost after commit | new ref may be visible | read the canonical ref first |
| Derived index update fails | committed state remains valid | rebuild from durable records |
The visibility rule is compact enough to keep in your head:
before REF TXN → old tip stays visible → retry is safe
at REF TXN → expected-old decides → one winner
after REF TXN → new tip is complete → repair moves forwardGarbage collection follows reachability
Deleting a path does not prove its chunks are unused. The same content may still belong to another version, branch, file, workflow artifact, or recovery point.
Crab GC starts from retained roots, walks the complete Git and file-data closure, compares the mark set with repository inventory, and then applies a grace window.
Reachability and age decide each object outcome
Scroll horizontally to explore the full diagram →
An object is eligible only when both statements are true:
- No retained root reaches it.
- It is older than the protection window.
The grace window protects concurrent or interrupted writers. Repository scope protects other teams that share the bucket.
Choose Crab for the right problem
Crab is not the answer to every large object.
| Approach | Strong fit | Trade-off |
|---|---|---|
| Ordinary Git | source + small files | binary versions inflate packs and clones |
| Hosted Git LFS | familiar pointer workflow | whole-file storage + separate service path |
| Artifact registry | published build outputs | not a branch-history or worktree model |
| Dataset system | lineage, metrics, domain catalog | code and data may live in separate histories |
| Crab | mixed repos + related versions + LFS compatibility | requires Crab clients + object-store access |
Use ordinary Git when it already works. Crab leaves unmatched paths alone.
Use an artifact registry when the content is a released product, not a file edited beside source. Use a domain data system when its query, lineage, or governance model is the primary interface.
Use Crab when four conditions line up:
- Git commits and branches should remain the coordination interface.
- Large versions share meaningful byte regions.
- Worktrees should materialize selected content.
- Durable bytes should live directly in cloud storage you control.
First push in five minutes
Crab ships as one CLI. The installation also provides Git’s remote helper and filter process.
brew install crabbuild/tap/crab
# Or use the checksum-verifying installer.
curl -fsSL https://crab.build/install.sh | bashCreate a repository backed by your bucket:
mkdir vision-search && cd vision-search
crab configure s3://team-bucket/vision-search
crab track '*.safetensors'
crab add .
git commit -m "Add the first model"
crab push origin mainThen verify the ref and its data closure separately:
git ls-remote origin refs/heads/main
crab fsck --json
crab doctor- 1Configure a dedicated repository prefix and review the generated .gitattributes rules.
- 2Add one representative large file and inspect the pointer staged in Git.
- 3Push, then verify the ref and remote closure from the producing machine.
- 4Clone lazily in a clean environment.
- 5Hydrate one file and compare its full identity with the source.
- 6Interrupt a test push before publication and confirm the old ref remains visible.
The premise, tested
“Any scale” does not mean physics stops applying.
A five-terabyte version still contains five terabytes of logical content. A cold object still has restore latency. Re-encoding can erase reuse. Object stores still charge for storage, requests, and transfer.
The goal is more precise: do not force every scale dimension onto every operation.
- Git owns names, commits, trees, branches, and review
- Pointers bind paths to exact large-file identities
- Recipes and shards bind identities to ordered ranges
- Xorbs carry immutable packed content
- Hydration turns selected pointers into verified files
- The ref moves only after every dependency is durable
A large history should not require a large checkout. A large file should not require whole-file memory. A failed upload should not expose a broken ref.
Crab is our attempt to keep those statements true while preserving the workflow developers already understand.
Start with the installation guide, create a first repository, or inspect the complete CLI documentation.
Migrating from Git LFS? Read the migration guide. Working with more data than local disk? Continue with lazy clone and hydration and the local mount workflow.
The first question is not “How large is the repository?”
It is: Which bytes does this operation actually need?
Crab lets Git answer which state you mean—and lets the data plane answer the rest.