How to clone git repo without using "git clone"

The most common way to clone git repository is to use "git clone REPO_URL".

git clone https://github.com/bessarabov/Moment.git

(the repo specified here does not matter, this text is not about this repo, it is about git itself).

But it also possible to clone a repo without running "git clone". So, how can it be done?

Git has a great idea. Git is a distributed revision control system. There can be a server that stores a git repo and there can be a git repo that is situated localy on your computer. That repos can interact with each other but they both are just the git repos. Your own local git repo has a thing called "working copy" — it is all the files in your project. You can connect your local git repo to the repo on a server. That server repo is called "remote" it is it usually has the name "origin". Usually a local git repo has exactly one remote, but it also possible to connect several remotes to a local git repo, or to have a local git repo without remotes. You are working with you own repo. You don't need internet when you are working with it. Every operation is performed on your device. There are only several commands that makes interactions with remote, the most used ones are: "git push", "git fetch", "git pull" and "git clone". And you can do everything you need without "git clone" (you can also omit using "git pull", but this is not covered in this text).

When you run "git clone https://github.com/bessarabov/Moment.git" several things happen:

So, lets do the same things manually.

1. Create directory and enter it

mkdir Moment
cd Moment

2. Create empty git repo

$ git init .
Initialized empty Git repository in /Users/bessarabov/Moment/.git/

The dot here means "current directory". Now we are in the directory "Moment", so the command "git" has created git repo here.

Running git status shows that this is a empty git repo:

$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

3. Add remote

The empty git repo that we have creates in the previous step has no remotes. We can check it with the command:

$ git remote -v

It does not output anything, it means that there is no remotes.

To add remote we must execute command:

$ git remote add origin https://github.com/bessarabov/Moment.git

This command adds remote with the name "origin" (this is the standard name, most of the time the remote is called "origin"). We can check that the remote is set with the command:

bessarabov@air:~/Moment$ git remote -v
origin  https://github.com/bessarabov/Moment.git (fetch)
origin  https://github.com/bessarabov/Moment.git (push)

You can see that there are is the same remote for fetch (get data) and push (send data), git can be set to get data from one remote and to send data to the other remote, but such setup is extremely rare. Usually the fetch & push remote are the same.

The command "git remote add" just changed file with your local git repo config, here is the fragment of the file ".git/config" containing information about remote:

[remote "origin"]
        url = https://github.com/bessarabov/Moment.git
        fetch = +refs/heads/*:refs/remotes/origin/*

4. Fetch everything from remote

Now we have our own git repo and we have a remote connected to it. Our own git repo is empty. We can copy everything from the remote repo to our repo with just one command:

$ git fetch
remote: Enumerating objects: 83, done.
remote: Counting objects: 100% (83/83), done.
remote: Compressing objects: 100% (40/40), done.
remote: Total 583 (delta 26), reused 62 (delta 14), pack-reused 500
Receiving objects: 100% (583/583), 81.16 KiB | 573.00 KiB/s, done.
Resolving deltas: 100% (272/272), done.
From https://github.com/bessarabov/Moment
 * [new branch]      master            -> origin/master
 * [new tag]         1.0.0             -> 1.0.0
 * [new tag]         1.0.1             -> 1.0.1
 * [new tag]         1.1.0             -> 1.1.0
...

Command "git fetch" by default is working with remote with the name "origin". If we want to get data from some other remote, say "my_fork" we need to run "git fetch my_fork".

So, after "git fetch" our git repo has everything that is in the repo "origin".

But if we check the files we have in our local git repo, there is only a .git/ directory:

$ ls -1a
.
..
.git

Our git repo has everything that is in the remote repo, but all the files are hidden somewhere in the .git/ directory. To see the files we need to switch working directory to some state of the git history.

5. Switch working directory to the state

To see the files in the working directory we need to switch to some git commit. We will switch to the latest commit in de default branch.

First of all we need to find out what is the default branch. Usually it is called "master", but sometimes it has some other name. To find it out, we need to run "git remote show origin". This command interacts with remote, so it will not work without internet.

$ git remote show origin
* remote origin
  Fetch URL: https://github.com/bessarabov/Moment.git
  Push  URL: https://github.com/bessarabov/Moment.git
  HEAD branch: master
  Remote branches:
    master            tracked

The line starting from "HEAD branch:" is what we are looking for. Here is is written "HEAD branch: master", so this repo has the default branch called "master" (this is the name for the default branch in the most repos, but there are some rare exceptions).

Now we know the name of the default branch and we can switch our working copy to it:

$ git checkout -b master origin/master
Branch 'master' set up to track remote branch 'master' from 'origin'.
Already on 'master'

With this command we create a new branch in our local git repo that with the name "master" and we make it point to the same commit as the branch "master" in there remote "origin" is pointing.

And after that we have a file structure in our repo:

$ ls -1a
.
..
.git
.github
Changes
Dockerfile
README.md
build
check
dist.ini
lib
t
xt

Five steps and we have the same result as if we used "git clone" command.

Why do you need to know how to clone git repo without "git clone"? Well, if you need to clone a git repo the best way to do it is to run "git clone". But knowing how it can be done without "git clone" improves your understanding of how git works and it makes you more powerfull git user.

Ivan Bessarabov
ivan@bessarabov.ru

30 august 2019