HOWTO · Git
Difference Between Git Checkout and Git Clone
Differentiate between the git checkout and git clone commands.
On this page
git clone copies a remote repository into a new local repository. git checkout works inside a repository you already have: historically, it switches branches or restores files. SVN users may recognize svn checkout as the operation for obtaining a working copy, but Git uses git clone for that task.
The two commands belong to different stages of work, so they are not interchangeable alternatives. Cloning answers the question “How do I get this project onto my machine?” Checking out answers “Which branch, commit, or file version should my existing working tree show right now?” If you do not yet have a repository, there is nothing to check out; if you already have one, cloning again only creates a second, separate copy.
At a Glance
| Aspect | git clone |
git checkout |
|---|---|---|
| Starting point | A remote repository URL or path | A repository that already exists locally |
| Main result | A new local repository with its own .git directory |
A changed working tree and moved HEAD inside the same repository |
| Network use | Downloads objects and history from the remote | None for local branches, commits, and files |
| Typical frequency | Once per copy you need | Many times a day while you work |
| Can it create a repository? | Yes, that is its purpose | No, it fails outside a repository |
| Related modern commands | None; cloning has no split replacement | git switch for branches, git restore for files |
How git clone Creates a Local Repository
Use git clone with a repository URL. Git creates a local repository and checks out its default branch; cloning is useful whenever you need another local copy, not only the first time you work on a project.
This standalone example creates a local source repository, clones it, and creates a feature branch. The command sequence prints a confirmation after switching to the new branch.
set -eu
workdir=$(mktemp -d)
trap 'rm -rf "$workdir"' EXIT
git init -q "$workdir/source"
git -C "$workdir/source" config user.name "Example User"
git -C "$workdir/source" config user.email "example@example.com"
printf 'Project notes\n' > "$workdir/source/README.md"
git -C "$workdir/source" add README.md
git -C "$workdir/source" commit -qm "Add project notes"
git clone -q "$workdir/source" "$workdir/local"
git -C "$workdir/local" switch -q -c feature
printf 'Created a local clone and switched to feature.\n'
Created a local clone and switched to feature.
For a hosted repository, use its copied HTTPS or SSH URL in git clone <repository-url>. The destination directory defaults to a name based on the repository URL; add a directory name after the URL to choose another location. The URL is the key distinction: this command creates a repository from a remote source.
A clone is more than a download of the current files. Git copies the commit history, creates remote-tracking branches for the remote branches, and registers the source as the remote named origin, so a later git fetch or git pull knows where updates come from. It then performs an internal checkout of the default branch to fill the working tree. That last step is the only point where the two commands meet: cloning finishes by checking out a branch, but checking out can never produce a clone.
Clone Options That Change What You Get
Three options cover most real-world choices. Use --depth 1 for a shallow clone when you only need the newest snapshot, for example in continuous integration: the clone downloads just the specified number of commits instead of the full history, which saves time and disk space but leaves older commits unavailable locally. Use --branch <name> to check out a branch other than the default after cloning, and combine it with --single-branch when you do not need the other branches at all. Be aware that --depth is ignored for a plain local-path clone; pass a file:// URL instead when you test shallow cloning against a local source, as the second example below does.
What git checkout Does
git checkout has historically handled both branch operations and file restoration. These are distinct tasks, which is why newer Git versions provide the clearer git switch and git restore commands. Their availability depends on the installed Git version; use the corresponding checkout form when either newer command is unavailable.
To create and enter a branch, use git switch -c <branch-name>; the historical form is git checkout -b <branch-name>. To move to an existing branch, use git switch <branch-name> or git checkout <branch-name>. Replace the placeholder with a branch that exists in your repository; the default branch name varies by project.
To discard unstaged edits to a tracked file, git restore <file> restores its content from the index. It leaves staged changes intact, but discards the file’s unstaged edits. The older equivalent is git checkout -- <file> and has the same behavior. Check git status and save any work you may need before restoring.
The double dash in the file form is not decoration. It separates the branch-or-commit argument from file paths, so Git never has to guess whether a name means a branch or a file. When a branch and a file share the same name, git checkout <name> is ambiguous and can switch branches when you meant to restore a file; git checkout -- <name> always means the file. This ambiguity is one reason the split commands are safer for everyday use.
Checking Out a Commit: Detached HEAD
Checking out a branch moves HEAD to that branch, so new commits extend it. Checking out a specific commit hash or tag instead puts you in a detached HEAD state: the working tree shows that older snapshot, and git branch --show-current prints nothing. Inspecting an old release or reproducing a bug report is a safe use, because you change nothing by looking. Committing while detached is risky, because the new commits belong to no branch and become hard to find once you switch away; create a branch first with git switch -c <branch-name> if you intend to keep the work. Nothing about this state involves cloning: you are still inside the same repository, looking at history it already contains.
See the Difference in One Repository
The next standalone example builds a three-commit source repository and then contrasts the commands directly. It compares a shallow clone with a full clone, restores an edited file with the checkout file form, and finally checks out an older commit to show the detached state. The printed commit counts and file contents are the verification: the shallow clone holds one commit, the full clone holds all three, the restore removes the appended draft line, and the detached checkout lands on the second commit.
set -eu
workdir=$(mktemp -d)
trap 'rm -rf "$workdir"' EXIT
git init -q -b main "$workdir/source"
git -C "$workdir/source" config user.name "Example User"
git -C "$workdir/source" config user.email "example@example.com"
printf 'one\n' > "$workdir/source/notes.txt"
git -C "$workdir/source" add notes.txt
git -C "$workdir/source" commit -qm "First"
printf 'two\n' >> "$workdir/source/notes.txt"
git -C "$workdir/source" commit -qam "Second"
printf 'three\n' >> "$workdir/source/notes.txt"
git -C "$workdir/source" commit -qam "Third"
git clone -q --depth 1 "file://$workdir/source" "$workdir/shallow"
printf 'Shallow commits: %s\n' "$(git -C "$workdir/shallow" rev-list --count HEAD)"
git clone -q "$workdir/source" "$workdir/full"
printf 'Full commits: %s\n' "$(git -C "$workdir/full" rev-list --count HEAD)"
printf 'draft\n' >> "$workdir/full/notes.txt"
git -C "$workdir/full" checkout -- notes.txt
printf 'After restore: %s\n' "$(tail -n 1 "$workdir/full/notes.txt")"
git -C "$workdir/full" checkout -q HEAD~1
printf 'Detached commit subject: %s\n' "$(git -C "$workdir/full" log -1 --format=%s)"
if [ -z "$(git -C "$workdir/full" branch --show-current)" ]; then
printf 'HEAD state: detached\n'
fi
Shallow commits: 1
Full commits: 3
After restore: three
Detached commit subject: Second
HEAD state: detached
Notice the order of operations in this example, because it mirrors real work. Every checkout runs inside a repository that a clone (or git init) created first. Reversing that order is impossible: running git checkout in an empty directory fails, because there is no branch, commit, or tracked file for it to select.
Choose the Right Command
- Have a remote repository URL and need a local repository? Use
git clone <repository-url>. - Need to create or switch to a branch in an existing repository? Use
git switchor the correspondinggit checkoutform. - Need to discard uncommitted edits to a tracked file? Use
git restore <file>, or the oldergit checkout -- <file>form. - Need only the latest snapshot of a large repository, for example for a build? Use
git clone --depth 1 <repository-url>rather than cloning everything and checking out the newest commit. - Need to inspect how the code looked at an old tag or commit? Use
git checkout <tag-or-commit>inside your existing clone, and return withgit switch <branch-name>; do not clone the repository again. - Starting work on a teammate’s branch that exists only on the remote? Fetch inside your existing clone and check the branch out; a fresh clone is only needed when you want a completely separate copy.
Two mistakes cause most confusion. The first is running git checkout with a repository URL and expecting a download: checkout takes branch, commit, and path arguments, never a URL, so the command fails instead of fetching anything. The second is cloning again whenever a different branch is needed, which wastes time and disk space and leaves you with several copies that drift apart; one clone normally serves every branch in the project. A related misconception is that checkout downloads the branch it selects. It does not contact the network at all: it can only select branches and commits your repository already knows about, which is why a git fetch may be needed first for brand-new remote branches.
In short, git clone obtains a repository from a remote URL. git checkout changes the selected branch or, in its older file form, restores a file within an existing repository; it does not create a local repository from a remote URL.