Speed Up Git: Essential Performance Optimization Techniques
Speed up Git by reducing clone cost, using Git LFS wisely, pruning stale refs, ignoring generated files, and applying sparse checkout.
Speed Up Git: Essential Performance Optimization Techniques
Slow Git usually has a concrete cause: too much history, too many untracked files, large binary objects, expensive filesystem scans, or network latency. Before you blame Git itself, check which operation is slow and what it is trying to read or download.
Git performance optimization works best when you match the fix to the symptom. A slow CI clone needs a different answer than a slow git status in a huge working tree.
Understanding the Causes of Slow Git Performance
Start with the common causes:
- A long history with many files and refs.
- Large binary files committed directly to Git.
- Build output, dependency folders, or logs sitting unignored in the working tree.
- Many stale remote-tracking branches and tags.
- Slow network links to the remote.
- Old Git versions missing newer maintenance and sparse checkout improvements.
Run the slow command with intent. If git clone is slow, look at history size and network transfer. If git status is slow, look at working tree size, ignored files, and filesystem behavior. If git fetch is slow, look at remote refs, tags, and changed objects.
Reduce Clone and Fetch Cost
For CI, deployment jobs, and read-only inspection, you often do not need the full history.
Use a shallow clone:
git clone --depth <number> <repository_url>
For example, to clone only the last 10 commits:
git clone --depth 10 https://github.com/example/repo.git
For CI jobs that only build the current commit, --depth 1 is often enough. For developer work, a full clone is usually less surprising because commands such as deep git log, old tag checkout, and some rebases need more history.
If you already have a shallow clone and need more history, deepen it:
git fetch --deepen=50 origin
Or convert it to a full clone:
git fetch --unshallow origin
For partial data needs, newer Git versions also support partial clone filters such as --filter=blob:none, but only use them when your Git host and workflow support them well:
git clone --filter=blob:none https://github.com/example/large-repo.git
Keep Large Files Out of Normal Git History
Large binaries are one of the fastest ways to make Git feel slow. Images, videos, archives, design files, and model files often do not compress or diff well.
Use Git LFS for large assets that truly belong in the repository:
git lfs install
git lfs track "*.psd"
git lfs track "assets/*.mp4"
git add .gitattributes
git commit -m "Track large assets with Git LFS"
Git LFS affects future commits after the tracking rules are in place. If someone already committed large files to normal Git history, removing them from the current tree is not enough. You may need a coordinated history rewrite with a tool such as git lfs migrate import or git filter-repo.
For generated build artifacts, the better answer is usually not LFS. Do not commit them. Add them to .gitignore instead:
node_modules/
dist/
coverage/
*.log
Clean Up Local References and Objects
Stale remote-tracking branches add clutter and can slow commands that list or inspect refs. Prune them during fetch:
git fetch --prune
Delete local branches that are already merged and no longer needed:
git branch --merged
git branch -d old-feature-branch
Let Git run maintenance:
git maintenance run
On older workflows, git gc is still useful:
git gc
Avoid aggressive cleanup commands unless you know why you need them. For example, expiring reflogs can make it harder to recover from a bad reset.
Make git status Cheaper
git status has to inspect the working tree. If your project directory contains thousands of generated or dependency files, status can become noisy and slow.
Use .gitignore for files Git should not consider. If a file is already tracked, .gitignore will not stop Git from tracking it; you must remove it from the index first:
git rm --cached path/to/generated-file
For very large repositories where you only need part of the tree, sparse checkout can help:
git sparse-checkout init --cone
git sparse-checkout set services/api docs
That keeps only selected paths in your working tree. It is useful in monorepos, but your team should document the expected sparse paths so developers do not miss files they need.
Separate Fetch from Integration
git pull fetches and then integrates changes with merge or rebase, depending on configuration. When a repository is large or a branch has diverged, it is often clearer to fetch first:
git fetch origin
git log --oneline HEAD..origin/main
git merge origin/main
This does not make the network transfer smaller by itself. It gives you control before changing your working branch.
Practical Takeaway
Use shallow clones for short-lived jobs, Git LFS for large assets, .gitignore for generated files, pruning for stale refs, and sparse checkout for large trees where you only need a subset. Keep Git current, but do not use integrity tools such as git fsck as a performance fix unless you suspect repository corruption.
When Git feels slow, write down the exact command and where the time goes: network transfer, object processing, or working tree scan. That one detail usually points to the right optimization.