Working with very large repos
A full clone downloads every object from every commit. Shallow clone (--depth 1) truncates history for fast CI; partial clone (--filter=blob:none) fetches blobs on demand; sparse-checkout populates only part of a monorepo working tree. Each trades a different cost.
git clone on the company monorepo has been running for eleven minutes. It is pulling fifteen years of history and the entire working tree when all your CI job needs is the latest commit, and all you need on your laptop is the one service you own. A plain clone copies every object from every commit — every old version of every file — whether you will ever look at it or not.
By the end of this lesson you will be able to pick the right cheap-clone strategy — shallow, partial, or sparse — for a CI runner, a monorepo developer, or a bandwidth-constrained checkout, and explain what each one does and does not download.
After this lesson you can choose and run the correct clone for a large repository — --depth, --filter, or sparse-checkout — and explain the history, object, and working-tree tradeoff each one makes.
A full clone downloads every object in the entire history — that is the cost you are fighting. Git stores a project as commits pointing at trees pointing at blobs (file contents). A default git clone transfers all of them: every blob of every file as it existed at every commit, plus all the trees and commits that tie them together. For a long-lived repo with large or churny files, the history dwarfs the current checkout — you might download gigabytes to end up with a 200 MB working tree. The three techniques below each cut a different slice of that transfer.
Shallow clone truncates history: download recent commits, not all of them. --depth N asks the server for only the last N commits on the branch, throwing away everything older.
# Just the tip commit — typical for CI
git clone --depth 1 https://example.com/big/repo.git
# A handful of recent commits, single branch only
git clone --depth 50 --single-branch --branch main https://example.com/big/repo.git--single-branch (implied by --depth) avoids fetching every other branch’s history too. This is the standard CI clone: a build only needs the current tree and maybe a few commits for a changelog, never the full past. The catch is that history operations are crippled — more on that below.
Partial clone keeps full history but defers the heavy file contents. --filter=blob:none downloads all the commits and trees immediately, but no blobs — file contents are fetched lazily from the server the first time you actually need them (a checkout, a diff, a git log -p).
# All commits/trees now; blobs fetched on demand
git clone --filter=blob:none https://example.com/big/repo.git
# Skip only large blobs, keep small ones local
git clone --filter=blob:limit=1m https://example.com/big/repo.gitUnlike shallow, you keep the complete commit graph, so git log, git blame, and git bisect work — they just trigger an on-demand fetch when they touch old file content. The cost: it needs a server that supports the partial-clone protocol (GitHub, GitLab, Gitea, recent Git servers do), and it is slower offline because missing blobs require the network.
Sparse-checkout shrinks the working tree: populate only the directories you care about. Partial and shallow shrink the download; sparse-checkout shrinks what lands on disk. In a monorepo you can clone metadata only, then check out a few paths:
git clone --filter=blob:none --no-checkout https://example.com/monorepo.git
cd monorepo
git sparse-checkout init --cone
git sparse-checkout set services/payments libs/shared
git checkout mainIn cone mode you list directories and Git populates just those (plus repo-root files); the other thousand services never appear in your tree but the repo still knows about them. Combined with --filter=blob:none, you download blobs only for the paths you actually populate — the sweet spot for working in one corner of a giant monorepo.
▸Edge cases
Shallow clones break history-dependent operations. With --depth 1 there is no merge base before the tip, so git log stops at one commit, git blame cannot reach older authors, and git merge or git rebase against an older branch may fail or behave oddly. CI that needs a real diff against the base branch must deepen first — git fetch --deepen=50 or git fetch --unshallow to restore full history. Treat a shallow clone as a throwaway, not a place you do history work.
One 8 GB monorepo, three people, three clones.
CI runner builds the payments service on every push. It never needs history, never needs other services. It runs git clone --depth 1 --single-branch --branch "$CI_COMMIT_BRANCH" $REPO — a few hundred megabytes, seconds not minutes. When a step needs to diff against main, it adds git fetch --deepen=100 only then.
A payments developer lives in services/payments all day but occasionally runs git blame to find why a line exists. They want full history but not 8 GB of every other team’s blobs. They clone --filter=blob:none --no-checkout, then sparse-checkout set services/payments libs/shared and checkout main. Disk holds two directories; git log and git blame work, fetching the rare old blob on demand.
An analyst on hotel Wi-Fi just needs to read the latest config files once. They clone --depth 1 --filter=blob:limit=100k so even the tip downloads only small files, and let the few large ones stream in if they open them. Same repo, three transfers ranging from megabytes to gigabytes — chosen, not accidental.
▸Why this works
None of this helps with large binary files that change often — a 50 MB design asset committed weekly bloats history no matter how you clone, because every version is a full new blob. That is what Git LFS (Large File Storage) is for: tracked patterns (git lfs track "*.psd") are replaced in the repo by tiny text pointers, while the real bytes live on a separate LFS server and are fetched only for the versions you check out. Partial clone defers blobs you already have in history; LFS keeps the giant binaries out of the Git object store in the first place. They compose: many large monorepos use LFS and partial/sparse clones together.
A developer needs full history (for git blame and bisect) but does not want to download every old version of every file up front. Which option fits best?
A default clone copies every object from every commit, which is wasteful on large repos. Shallow clone (--depth N, usually --depth 1 --single-branch) truncates history for fast, throwaway CI checkouts — but breaks blame, bisect, and base-branch diffs until you --deepen or --unshallow. Partial clone (--filter=blob:none) keeps the full commit graph and fetches file contents lazily, needing a server that supports it. Sparse-checkout (git sparse-checkout set <dirs>, cone mode) populates only chosen directories of a monorepo, and pairs naturally with partial clone. For frequently-changing large binaries, Git LFS keeps the bytes out of history entirely. Next you will see how to embed an entire other repository inside yours — with submodules and subtrees.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.