git clone

git clone is the command that takes a repository living somewhere else — on GitHub, on a teammate’s machine, anywhere reachable by a URL — and creates a complete, independent copy of it on your own computer. It doesn’t just grab the current files; it copies the entire commit history, every branch, and every tag, so the result is a fully functional Git repository in its own right, not a snapshot. For almost everyone, git clone is the very first Git command they ever run against a project, and understanding exactly what it downloads and configures will save you a lot of confusion later when you start pushing, pulling, and branching.

Overview: What git clone Actually Does

Under the hood, git clone performs several distinct steps, and it is worth knowing them individually because each one corresponds to something you will interact with directly later on.

First, Git creates a new directory — named after the repository unless you tell it otherwise — and initializes it as a Git repository. This is essentially the same as running git init inside that folder: it creates the .git subdirectory that holds Git’s object database, configuration, and refs.

Second, Git contacts the remote (over HTTPS or SSH) and negotiates which objects it needs. Git’s fundamental unit of storage is the object: a blob holds raw file contents, a tree is a snapshot of a directory listing that points to blobs and other trees, and a commit points to exactly one tree (the project’s state at that point) plus its parent commit(s), an author, a timestamp, and a message. Every object is addressed by the SHA-1 hash of its content, so two repositories that contain the identical commit will always compute the identical hash for it. A branch is nothing more than a small text file under refs/heads/ holding one of these hashes — a lightweight, movable pointer, not a copy of anything. When you clone, Git downloads every object reachable from every branch and tag on the remote, transferred efficiently as a compressed packfile, and writes them into your local object database.

Third, Git records the remote’s URL under the name origin (visible later with git remote -v) and creates a full set of remote-tracking branchesrefs/remotes/origin/main, refs/remotes/origin/feature-x, and so on, one for every branch that existed on the remote at clone time. These are read-only bookmarks: they tell your local Git “this is where origin’s main branch was, the last time I talked to it.”

Fourth, Git looks at which branch the remote considers its default (communicated by the remote’s HEAD, almost always main), creates a real local branch with that name pointing at the same commit, and checks it out. “Checking out” means Git populates the index (its staging area, a binary file listing exactly what is committed) and your working directory with the files from that commit’s tree. At the end of a clone, your local HEAD points to your new local main branch, that branch points to the same commit as origin/main, the index matches that commit exactly, and your working tree matches the index — everything is in sync. Because objects are content-addressed, the blobs and commits you now have are byte-for-byte identical to the ones on the server; a clone never “translates” anything, it only copies the object graph.

Syntax

The general form of the command is:

git clone "<repository-url>" "<directory>"

The directory argument is optional; if you omit it, Git names the new folder after the repository (stripping a trailing .git from the URL). The most useful options are:

Option What it does
-o <name>, --origin <name> Names the remote something other than the default origin.
-b <name>, --branch <name> Checks out <name> instead of the remote’s default branch.
--depth <n> Creates a shallow clone containing only the last n commits of history, not the full history.
--single-branch Fetches only the one branch being checked out, instead of every branch on the remote.
--recurse-submodules Also clones and initializes any submodules the repository references.
--bare Creates a bare repository with no working tree — used for servers and mirrors, not for day-to-day editing.
-n, --no-checkout Fetches all objects and refs but skips populating the working tree.

Examples

Example 1: A basic clone over HTTPS

The simplest and most common case: cloning a public repository by its HTTPS URL.

git clone https://github.com/octocat/Hello-World.git

Output:

Cloning into 'Hello-World'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

Git created a new Hello-World directory, downloaded every object reachable from the repository’s branches and tags, and checked out the default branch. You now have a full local repository, complete with history, sitting inside Hello-World/.

Example 2: Cloning over SSH into a custom folder name

If you push often, an SSH remote avoids retyping credentials on every push. You can also choose your own local folder name by passing a second argument.

git clone git@github.com:octocat/Hello-World.git my-hello-world
cd my-hello-world
git remote -v

Output:

Cloning into 'my-hello-world'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
origin  git@github.com:octocat/Hello-World.git (fetch)
origin  git@github.com:octocat/Hello-World.git (push)

The repository was checked out into my-hello-world instead of Hello-World, and git remote -v confirms that origin is configured with the SSH URL for both fetching and pushing.

Example 3: A shallow clone for CI, then recovering full history

CI systems and quick one-off checkouts often don’t need years of history. --depth limits how much gets downloaded, dramatically speeding up the clone on large repositories.

git clone --depth 1 --branch main https://github.com/octocat/Hello-World.git
cd Hello-World
git log --oneline
git fetch --unshallow
git log --oneline

Output:

Cloning into 'Hello-World'...
remote: Enumerating objects: 3, done.
Receiving objects: 100% (3/3), done.
7fd1a60 Merge pull request #6 from Spaceghost/patch-1
remote: Enumerating objects: 12, done.
Unpacking objects: 100% (12/12), done.
7fd1a60 Merge pull request #6 from Spaceghost/patch-1
762941318 Fix all the bugs
df65be2 first commit

The first git log shows only one commit, because --depth 1 told Git to fetch just the tip of history. git fetch --unshallow then goes back to the remote and retrieves the missing older commits, converting the repository into a normal, fully-historied clone — the second git log shows the earlier commits that were previously invisible.

How git clone Works Step by Step

  1. Git creates the target directory and initializes an empty .git repository inside it, exactly as git init would.
  2. Git contacts the remote and asks which refs (branches and tags) it has and what commit each one currently points to.
  3. Git downloads a single compressed packfile containing every object reachable from those refs (unless you limited it with --depth or --single-branch), then verifies and unpacks it into .git/objects.
  4. Git writes one remote-tracking ref per branch on the remote, under refs/remotes/origin/.
  5. Git creates a local branch matching the remote’s default branch, pointing it at the same commit, and points HEAD at that local branch.
  6. Git checks out that commit’s tree: it writes matching entries into the index and writes the actual file contents into your working directory, giving you an editable copy of the project.

Common Mistakes

Mistake 1: Running git clone again to “update” an existing clone

New users sometimes treat git clone as a general-purpose “download the latest files” command and re-run it inside a folder they already cloned.

cd my-hello-world
git clone https://github.com/octocat/Hello-World.git

This doesn’t update anything — it creates a brand-new nested Hello-World folder inside my-hello-world, cloning the whole history again from scratch. git clone is only for the initial copy; to bring an existing local repository up to date, use git fetch or git pull instead.

cd my-hello-world
git pull origin main

Mistake 2: Expecting a username and password to work over HTTPS

GitHub removed support for plain password authentication over HTTPS in 2021, so a bare password prompt at push time always fails now.

git push origin main
Username for 'https://github.com': myusername
Password for 'https://myusername@github.com':
remote: Support for password authentication was removed on August 13, 2021.
fatal: Authentication failed for 'https://github.com/myusername/my-hello-world.git/'

The fix is to authenticate with a Personal Access Token (used in place of a password) or, more conveniently for frequent pushes, switch the remote to SSH once an SSH key is registered with your GitHub account.

git remote set-url origin git@github.com:myusername/my-hello-world.git
git push origin main

Mistake 3: Forgetting submodules exist

If a repository references other repositories as submodules, a plain clone leaves those submodule directories present but empty.

git clone https://github.com/octocat/repo-with-submodules.git

The submodule folders will look empty until they’re explicitly initialized. Either clone with --recurse-submodules up front, or initialize them afterward.

git clone --recurse-submodules https://github.com/octocat/repo-with-submodules.git

Best Practices

  • Use an SSH remote if you push frequently, so you authenticate once with a key instead of a token on every operation.
  • Use --depth for CI jobs or throwaway checkouts of large repositories, where full history isn’t needed and speed matters.
  • Run git remote -v right after cloning to confirm you’re pointed at the repository you intended — especially important when cloning a fork.
  • Never embed a token directly in a clone URL like https://ghp_xxxxxxxxxxxx@github.com/... — it can end up stored in plaintext in .git/config and in your shell history.
  • Clone with --recurse-submodules whenever a project’s documentation mentions submodules, to avoid missing files later.
  • Consider the GitHub CLI’s gh repo clone <owner>/<repo> for convenience — it reuses your existing gh authentication automatically.

Practice Exercises

  1. Pick any public GitHub repository and clone it over HTTPS into the default folder name. Then run git log --oneline -5 and git branch -a and note which branches are local and which are remote-tracking.
  2. Clone the same repository a second time, into a folder called shallow-copy, using --depth 1. Compare its git log output to the full clone from exercise 1, then run git fetch --unshallow inside it and check the log again.
  3. If you have an SSH key registered with GitHub, clone a repository using its git@github.com:... form, then confirm with git remote -v that the URL shown is the SSH form and not HTTPS.

Summary

  • git clone copies an entire repository — every reachable commit, branch, and tag — not just the current files.
  • It creates .git, downloads objects into the local object database, sets up an origin remote, creates remote-tracking branches for every branch on the source, and checks out a local branch matching the remote’s default.
  • Objects are content-addressed by SHA-1 hash, so a cloned commit is identical, hash for hash, to the original on the remote.
  • Use --branch, --depth, and --recurse-submodules to control exactly what gets cloned.
  • git clone is for the initial copy only; use git fetch or git pull to update a repository you already have.
  • Prefer SSH keys or a Personal Access Token over HTTPS passwords, since GitHub no longer accepts password authentication.