To clone a specific branch: git clone -b branch-name repo-url. That checks out the branch you asked for and still downloads every other branch in the repository. If you wanted the branch and not the rest, you need —single-branch, and if you did not want the history either, you need —depth.
Those three options are independent and control different things. Most people learn the first, assume it does what the other two do, and then wonder why cloning a repository with a large history is slow when they only needed one branch.
Table of contents
- The four forms
- What —single-branch actually changes
- Shallow clones and their limits
- The CI case
- Sparse checkout for large repositories
- Choosing quickly
- How this fits the rest of the stack
- FAQ
The four forms
Each downloads a different amount:
# Everything. All branches, all history. Checks out the default branch.
git clone https://example.com/repo.git
# All branches, all history. Checks out the one you named.
git clone -b feature-x https://example.com/repo.git
# One branch, all of its history.
git clone --single-branch -b feature-x https://example.com/repo.git
# One branch, most recent commit only.
git clone --depth 1 -b feature-x https://example.com/repo.git
The second is the one people reach for, and it saves nothing on transfer — it only changes which branch is checked out afterwards.
Note that —depth implies —single-branch, so the fourth form does not need both. Writing both is harmless and makes the intent obvious.
The saving between the first and the last can be dramatic. A repository with years of history and dozens of branches can go from hundreds of megabytes to a few.
What —single-branch actually changes
It changes the refspec stored in your git configuration. A normal clone configures git to fetch every branch; —single-branch configures it to fetch one.
The practical consequence appears later, not at clone time. Running git fetch in a single-branch clone fetches only that branch, so other branches never appear even after fetching. This surprises people who expect fetch to eventually pull everything.
To add another branch afterwards, add it to the refspec:
git remote set-branches --add origin other-branch
git fetch origin other-branch
Or to convert back to a normal clone:
git remote set-branches origin '*'
git fetch origin
Neither is difficult, and knowing the mechanism explains the behaviour: it is configuration, not a permanent property of the clone.
Shallow clones and their limits
—depth 1 downloads only the most recent commit. That makes it the fastest possible clone and it removes capabilities that depend on history:
- git log shows one commit. There is no history to show.
- git blame is useless — every line appears to have been introduced by that commit.
- git bisect cannot work without a range.
- git describe finds no tags unless you also pass —tags.
- Merging and rebasing may fail because there is no common ancestor to compute against.
This is entirely fine for a CI job that builds and discards the checkout, and unsuitable for a working clone you will develop in.
If you need history after a shallow clone, deepen it:
git fetch --deepen 50 # 50 more commits
git fetch --unshallow # everything
So a shallow clone is not a dead end. It is a starting point you can extend, which makes it a safer default than it first appears.
The CI case
This is where the options earn their keep, because CI clones a repository on every run and throws it away.
A sensible CI checkout:
git clone --depth 1 --single-branch -b "$BRANCH" "$REPO_URL" .
On a repository with substantial history this can cut checkout time from minutes to seconds, multiplied by every build.
Two cases where you need more than depth 1:
- Comparing against a base branch, which needs both branches and enough history for a common ancestor. Fetch the base explicitly with a modest depth rather than unshallowing the whole thing.
- Versioning from tags with git describe, which needs —tags and enough depth to reach the most recent tag.
Most CI platforms expose this as a fetch-depth setting. Setting it to 1 is usually correct, and knowing why the pipeline breaks when a step needs history is the useful part.
Sparse checkout for large repositories
The options above control how much history and how many branches. For a monorepo the problem is different — you want one commit of one branch but only part of the tree.
git clone --filter=blob:none --sparse https://example.com/monorepo.git
cd monorepo
git sparse-checkout set services/api packages/shared
Two mechanisms combine here. —filter=blob:none is a partial clone: git downloads the commit and tree structure but no file contents, fetching blobs on demand as you touch files. —sparse limits the working directory to the paths you name.
For a large monorepo this is the difference between a workable checkout and one that takes ten minutes. It requires a server that supports partial clone, which the major hosts do.
The cost is that operations touching unfetched files trigger network requests, so a command that would be local in a full clone can suddenly be slow. That trade is worth it when the repository is genuinely large and not otherwise.
Choosing quickly
A short decision procedure:
- Working on the code? Plain clone. Disk is cheap and having the full history available is worth more than the download time you save once.
- CI build?
--depth 1 --single-branch. Add depth only when a step genuinely needs history. - One-off inspection of a branch?
--depth 1 --single-branch, and delete it afterwards. - Monorepo where you need one directory? Partial clone with sparse checkout.
The main thing to avoid is optimising a clone you will keep. Developers regularly shallow-clone their working repository, then hit the missing-history limits weeks later during a bisect or a rebase, and have to unshallow anyway. The one-time saving was not worth it.
How this fits the rest of the stack
Checkout options are the least glamorous part of a build and one of the easiest wins, because the cost is paid on every single run. The same principle applies further down the pipeline: what you download, build, and deploy should be as small as the job genuinely needs. Builds on RunxBuild covers how a repository becomes a live route with the build log attached, and deploying from GitHub covers the connection itself. If you are working out what the resulting services and databases cost, the RunxBuild hosting calculator breaks it into line items.
Useful related references:
- Cloud Computing Engineer: A More Specific Flavor of Cloud Engineering
- Deployment Server: The Thing That Decides Whether Your App Reaches Production or Lives in a Branch
- How to Rename a File in Linux: mv, rename, and git mv
- Services on RunxBuild
FAQ
How do I clone only one branch in git?
git clone —single-branch -b branch-name repo-url. Using -b alone checks out the branch you named but still downloads every other branch, which saves nothing on transfer.
What does git clone —depth 1 do?
Downloads only the most recent commit, making it the fastest clone. It also implies —single-branch. History-dependent commands like log, blame, and bisect will not work until you deepen the clone.
Can I get more history after a shallow clone?
Yes. git fetch —deepen 50 adds fifty more commits, and git fetch —unshallow retrieves the complete history. A shallow clone is a starting point rather than a permanent state.
Why does git fetch not get other branches after —single-branch?
The option changes the fetch refspec in your git config to one branch. Add another with git remote set-branches —add origin name, or restore normal behaviour with git remote set-branches origin ’*’.
What is the best checkout for CI?
git clone —depth 1 —single-branch for most jobs. Increase the depth only when a step needs history — comparing against a base branch, or deriving a version from tags with git describe.