By admin , 29 April 2026

The cost of full clones

A full clone of a long-lived repository can be hundreds of megabytes. On every CI job. Multiplied by every push. The bandwidth and time costs are real, especially in monorepos with five years of history.

Shallow clone basics

git clone --depth=1 https://example.com/repo.git

This fetches only the tip commit and its tree. The local repo is "shallow" - it has no parent commits.

Shallow with multiple branches

git clone --depth=1 --no-single-branch https://example.com/repo.git

Default --depth implies --single-branch. Override if your job needs to look at main as well as the PR head.

Partial clones (Git 2.22+)

Where shallow truncates history, partial clone defers blob downloads:

git clone --filter=blob:none https://example.com/repo.git
git clone --filter=tree:0 https://example.com/repo.git

--filter=blob:none fetches commits and trees but no file contents. --filter=tree:0 fetches only commits. Files are pulled lazily when accessed.

Combining shallow and partial

git clone --depth=1 --filter=blob:none https://example.com/repo.git

Smallest possible clone for a single-commit checkout. The repo is fully functional; missing data is fetched on demand.

GitHub Actions example

- uses: actions/checkout@v4
  with:
    fetch-depth: 1   # default

- name: Get full history when needed
  run: git fetch --unshallow

Some tools - git describe, conventional-changelog, blame-driven test selection - need full history. Unshallow only the jobs that need it.

Limitations of shallow

  • You cannot push from a shallow clone to a different repo without unshallowing first.
  • Some operations (rebase across the missing range, full bisect) are impossible.
  • Forks and complex submodule setups can interact poorly with shallow.

Measuring

time git clone --depth=1 https://example.com/repo.git
time git clone https://example.com/repo.git

On a Linux kernel-sized repo, the difference is minutes versus seconds.

Cache the result

Even shallow clones repeat work. Many CI systems support a persistent .git cache. After clone, run git fetch to bring it up to date - usually a tiny operation:

if [ -d .git ]; then
  git fetch --depth=1
  git reset --hard FETCH_HEAD
else
  git clone --depth=1 ...
fi

Gauge before optimising

If your repo is small (under 50 MB), shallow saves seconds and complicates debugging. Reserve aggressive shallow strategies for large repos and short-lived runners. For everything else, the default is fine.