I don't know how exactly GitHub do it, but here is a possible way. It requires some knowledge of the way git stores its data.
The short answer is that the repos can share the
objects database but each have their own references.
We can even simulate it locally for a proof-of-concept.
In the directory of a bare repo (or in the
.git/ subdir if it is not bare) there are three things that are the minimum for a repo to work:
objects/ subdirectory, which stores all the objects (commits, trees, blobs ...). They are stored either individually as files with names equal to the hash of the object or in
refs/ subdirectory, which stores simple files like
refs/heads/master whose contents is the hash of the object it references.
HEAD file, which says what is the current commit. Its value is either a raw hash (which corresponds to a detached head i.e we are not on any named branch) or a textual link to a ref where the actual hash can be found (for example
ref: refs/heads/master - that would mean we are on branch
Let's suppose someone creates his original (not forking) repo
orig at Github.
To simulate, locally we do
$ git init --bare github_orig
We imagine that the above happens at the Github servers. Now there is an empty github rpository. Then we imagine that from our own PC we clone the github repo:
$ git clone github_orig local_orig
Of course in real life instead of
github_orig we will use
https://github.... Now we have cloned the github repo in
$ cd local_orig/
$ echo zzz > file
$ git add file
$ git commit -m initial
$ git push
$ cd ..
object dir will contain our pushed commit object, one blob object for
file and one tree object. The
refs/heads/master file will contain the commit hash.
Now let's image what could be happening when someone hits the
We will create a git repo but by hand:
$ mkdir github_fork
$ cd github_fork/
$ cp ../github_orig/HEAD .
$ cp -r ../github_orig/refs .
$ ln -s ../github_orig/objects
$ cd ..
Notice that we copy
refs but we make a symbolic link for
objects. As we can see making a fork is very cheap. Even if we have tens of branches each of them is simply a file in the
refs/heads directory which contains a simple hexadecimal hash (40 bytes). For
objects we only link to the original objects directory - we do not copy anything!
Now we simulate that the user making the fork, clones the forked repo locally:
$ git clone github_fork local_fork
$ cd local_fork
$ # ls
We can see that we have successfully cloned although the repo that we clone from does not have its own
objects but links to that of the original repo.
Now the forking user may make branches, commits and then push them to
github_fork. Objects will be pushed in the
objects directory which is the same for
HEAD will be modified and will no longer match the ones at the
So the bottom line is that all repos that belong to the same forking tree share a common objects pool while each repo contains its own references. Anyone who pushes commits to his own forked repo modifies his own references but puts the objects in a shared pool.
Of course to be really usable some more things must be taken care of - most importantly the git garbage collector must not be invoked unless the repo where it is invoked in has the knowledge of all references - not just it's own. Otherwise it could discard objects in the shared pool which are not reachable from its references but could be reachable from other repos' refs.