git clone

TL;DR: The git clone command downloads an existing Git repository from a remote host (like GitHub) to your local machine. Under the hood, it automates five separate steps: creating a directory, running git init, configuring a remote alias (origin), fetching all objects/refs, and checking out the default branch into your workspace.


What Does git clone Actually Do?

To a beginner, git clone is simply the command you run to copy code from GitHub. However, under the hood, git clone is not a single tool. It is an orchestrator that chains together several low-level commands.

If you did not have git clone, you could achieve the exact same result by running this sequence of commands manually:

  1. mkdir project: Creates a directory for the project.
  2. cd project: Enters the directory.
  3. git init: Initializes the empty database (detailed in the /git-init tutorial).
  4. git remote add origin <url>: Configures the remote address pointer in the config file (detailed in the /git-remote tutorial).
  5. git fetch origin: Queries the remote, downloads the database objects, and builds remote tracking pointers (detailed in the /git-fetch tutorial).
  6. git checkout main: Checks out the files from the database into your working directory (detailed in the /git-branch tutorial).
                                  +---------------------------------------------+
                                  |         Remote Repository (GitHub)          |
                                  +---------------------------------------------+
                                                         |
                                                         | git clone (downloads complete history)
                                                         v
+-------------------------------------------------------------------------------------------------------+
|                                           Local Computer                                              |
|                                                                                                       |
|  1. mkdir project_dir               2. git init                 3. git remote add origin <url>        |
|  [Creates new folder]               [Creates .git skeleton]     [Writes to .git/config]               |
|                                                                                                       |
|  4. git fetch                       5. git checkout                                                   |
|  [Downloads database packfiles &]   [Unpacks files from objects/ database into working directory]     |
|  [writes refs/remotes/origin/ref]                                                                     |
+-------------------------------------------------------------------------------------------------------+

When you clone, you are not merely copy-pasting the current snapshot of files. Instead, you download the entire historical commit graph and every version of every file that has ever been committed. This ensures your local copy is a fully functional peer in the distributed network, capable of editing history, committing offline, and branching without consulting a server.


What Happens Inside .git/ (Hands-on exploration)

Let’s watch what happens when we clone a repository and examine the newly created .git folder structure.

Step 1: Clone the Repository

We will run git clone targeting a public repository. We can specify a custom directory name at the end of the command:

$ git clone https://github.com/octocat/hello-world.git local-project
Cloning into 'local-project'...
remote: Enumerating objects: 13, done.
remote: Counting objects: 100% (13/13), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 13 (delta 2), reused 10 (delta 2), pack-reused 0
Receiving objects: 100% (13/13), done.
Resolving deltas: 100% (2/2), done.

Let’s navigate inside the new directory:

$ cd local-project
$ ls -la
total 16
drwxr-xr-x 3 admin admin 4096 Jul  7 10:24 .
drwxr-xr-x 4 admin admin 4096 Jul  7 10:24 ..
drwxr-xr-x 8 admin admin 4096 Jul  7 10:24 .git
-rw-r--r-- 1 admin admin   14 Jul  7 10:24 README.md

The files have been unpacked into our working directory.

Step 2: Inspect the Configuration

Let’s open the local configuration file inside .git/config (managed by /git-config):

$ cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[remote "origin"]
	url = https://github.com/octocat/hello-world.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master

Git has automatically:

  1. Written the [remote "origin"] section pointing to the URL we cloned.
  2. Configured the default branch upstream connection in the [branch "master"] block (linking local master to remote master on origin).

Step 3: Inspect References and HEAD

Let’s list the reference files under .git/refs/:

$ find .git/refs/
.git/refs/
.git/refs/tags
.git/refs/heads
.git/refs/heads/master
.git/refs/remotes
.git/refs/remotes/origin
.git/refs/remotes/origin/HEAD
.git/refs/remotes/origin/master

Let’s check the contents of .git/refs/heads/master and .git/refs/remotes/origin/master:

$ cat .git/refs/heads/master
7fd305217cef615c1de71f747d3d7af4e2e28a5b

$ cat .git/refs/remotes/origin/master
7fd305217cef615c1de71f747d3d7af4e2e28a5b

They both contain the identical hash 7fd3052.... This points to the head commit of the repository.

Let’s check .git/HEAD:

$ cat .git/HEAD
ref: refs/heads/master

This confirms our local checked-out branch is tracking master.

Step 4: Examine the Index and Object Database

Let’s look at the index staging area using git ls-files:

$ git ls-files --stage
100644 980a0d5f19a64b4bc30a87d741088641e01490a7 0	README.md

The file README.md is staged with hash 980a0d5....

Let’s inspect where the objects are stored in the database. During cloning, Git download objects in bulk inside a packed binary format (a packfile) to optimize download speed:

$ find .git/objects/ -type f
.git/objects/pack/pack-9fd4407886401083416fd1d7f6c38a209fae4a78.idx
.git/objects/pack/pack-9fd4407886401083416fd1d7f6c38a209fae4a78.pack

Instead of loose objects (individual hash folders), they are stored inside the .pack file, and cataloged in the .idx index file. Git can search and extract commit data from this packfile directly.

Let’s verify this using git cat-file:

$ git cat-file -t 7fd305217cef615c1de71f747d3d7af4e2e28a5b
commit

$ git cat-file -p 7fd305217cef615c1de71f747d3d7af4e2e28a5b
tree 5110d7a04874c7db0f7690623a60a77c7f3f18e1
parent 553c2077f0edc3d5dc5d17262f6aa4b2e6e346b4
author The Octocat <support@github.com> 1295981078 -0800
committer The Octocat <support@github.com> 1295981078 -0800

Merge pull request #6 from spaceghost/patch-1

This proves that even though the objects are inside a compressed pack file, Git’s object database resolves them correctly.


Detailed Command Options & Advanced Usage

1. Shallow Clones: git clone --depth=<n>

When working with legacy codebases with hundreds of thousands of historical commits, downloading the complete history can take hours.

If you only need to run a deployment script, test a build, or make a quick fix, you can run a shallow clone:

git clone --depth=1 https://github.com/angular/angular.git

This instructs Git to:

  • Download only the single latest commit of the default branch.
  • Truncate the history graph.
  • Write the boundary hash into .git/shallow.
  • Substantially reduce download sizes (often from gigabytes to megabytes).
Full History:  C1 ---> C2 ---> C3 ---> C4 ---> C5 ---> C6 (HEAD)
Shallow Clone (--depth=1):                              C6 (HEAD, boundary defined in .git/shallow)

2. Cloning Specific Branches

If a repository contains multiple branches but you are only interested in working on a specific development branch, you can restrict the clone:

git clone --branch dev --single-branch https://github.com/user/project.git
  • --branch dev tells Git to automatically check out the dev branch instead of the default branch (usually main).
  • --single-branch tells Git to download history only for that branch, ignoring commits on all other branches. This reduces download times and keeps the local database clean.

3. Cloning Repositories with Submodules

Many modern projects reference external codebases using Git submodules. A standard git clone will download the parent repository, but leave the submodule folders empty.

To clone the repository and automatically download and initialize all submodules in one command, use the --recursive flag:

git clone --recursive https://github.com/user/parent-project.git

If you forget to use this flag during clone, you must initialize them manually afterward:

git submodule update --init --recursive

Real-World Scenario: Developer Onboarding Flow

You are joining a new engineering team. The main application is hosted on GitHub, contains submodules, and has a large history.

Here is the professional workflow for setting up your environment:

Step 1: Clone recursively

Clone the repository using the --recursive flag to ensure all dependency repositories are pulled down automatically. We will also rename the target directory to app-development:

$ git clone --recursive https://github.com/organization/enterprise-app.git app-development
Cloning into 'app-development'...
...
Submodule 'modules/payments' (git@github.com:organization/payments.git) registered...
Cloning into 'modules/payments'...

Step 2: Set Developer Details

Ensure your commits are signed with your correct email:

$ cd app-development
$ git config user.name "Alex Dev"
$ git config user.email "alex@organization.com"

Step 3: Check Out and Track the Development Branch

The default branch is main, but your team does active work on the dev branch. Check it out:

$ git checkout dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.
Switched to a new branch 'dev'

Because Git downloaded the remote-tracking branches during the clone, it immediately knows what commit dev points to, creates a local dev branch tracking origin/dev, and updates your workspace.


Common Pitfalls and Mistakes

1. Nested Repositories (Cloning inside a Git repo)

  • The Problem: You run git clone while already inside a directory that is a Git repository.
  • The Cause: You forgot to verify your current working directory. The new repository is created as a folder inside the parent repository, which Git views as an untracked directory or a nested repository (which can cause commits to be ignored).
  • The Fix: Delete the newly cloned folder, navigate outside the parent Git repository, and run the clone command again.

2. Copy-Pasting Instead of Cloning

  • The Problem: Copying a project folder from a colleague’s computer via USB drive and realizing you cannot run git push or git pull.
  • The Cause: Copying only the working directory files without copying the hidden .git folder, or copying a .git folder that contains your colleague’s username and credentials in .git/config.
  • The Fix: Clone the repository cleanly from the server to create a proper connection under your own credentials.

3. Shallow Clone Failure During Rebase or Push

  • The Problem: You performed a shallow clone (--depth=1), made some edits, and when trying to run git rebase or push to a remote, Git throws errors.
  • The Cause: Some Git workflows (like complex merges or rebases) require walking the history graph to find common ancestors. Because your history was truncated, Git does not have the ancestor commits.
  • The Fix: You can “unshallow” your repository, downloading the rest of the history:
    git fetch --unshallow

Command Quick Reference

CommandResultUse Case
git clone <url>Clones the repository into a directory named after the project.Standard use
git clone <url> <dir>Clones the repository into a custom named directory.Custom folder naming
git clone -b <branch> <url>Clones the repository and checks out the specified branch.Target specific branch
git clone --depth <n> <url>Downloads only the last n commits (shallow clone).Quick builds / slow connections
git clone --recursive <url>Clones the parent and automatically initializes all submodules.Projects with dependencies
git clone --bare <url>Clones only the .git folder contents directly into the folder (no working files).Server setups / backups

FAQ (People Also Ask)

How do I clone a repository to a specific folder?

By default, Git creates a folder named after the repository (e.g. cloning project.git creates a directory named project). To clone into a specific folder, append the path to the end of the command:

git clone https://github.com/user/project.git custom-folder-name

To clone directly into your current directory without creating a subdirectory, use a dot (.):

git clone https://github.com/user/project.git .

(Note: The target folder must be empty for this to succeed).

What is the difference between HTTPS and SSH cloning?

  • HTTPS cloning (URL formats like https://github.com/...) is simple, firewall-friendly, and requires no prior setup. However, it often requires you to input authentication credentials (like tokens or usernames) when pushing.
  • SSH cloning (URL formats like git@github.com:...) uses cryptographic SSH key pairs. It requires you to generate a key pair and register the public key on GitHub. Once set up, it allows you to sync and push securely without typing credentials.

When should I use a shallow clone?

You should use a shallow clone (--depth=1) in CI/CD build environments, automation scripts, or when you are simply downloading a massive project to review code or compile a single release. Avoid shallow clones if you plan to do long-term development, perform complex rebases, or merge divergent branches, as these require full history graphs.

What is a bare clone (git clone --bare)?

A bare clone contains only the database and metadata files (the contents of the .git folder) in the root directory. It does not check out a working directory (there are no editable source code files). Bare repositories are used on servers (like GitHub) to store code and handle push/pull operations, or for local backups, since developers do not write code directly on them.


Test Your Knowledge

Loading quiz…