GitHub が正確にどのように行っているかはわかりませんが、可能な方法は次のとおりです。git がデータを保存する方法についてある程度の知識が必要です。
簡単に言うと、リポジトリはデータベースを共有objects
できますが、それぞれに独自の参照があります。
概念実証のためにローカルでシミュレートすることもできます。
ベアレポのディレクトリ (ベア.git/
でない場合はサブディレクトリ) には、レポが機能するための最低限のものが 3 つあります。
objects/
すべてのオブジェクト (コミット、ツリー、ブロブなど) を格納するサブディレクトリ。それらは、オブジェクトのハッシュと同じ名前のファイルとして個別に保存されるか、ファイルに保存され.pack
ます。
- 内容が参照するオブジェクトのハッシュである
refs/
ような単純なファイルを格納するサブディレクトリ。refs/heads/master
HEAD
現在のコミットが何であるかを示すファイル。その値は、生のハッシュ (デタッチされたヘッドに対応する、つまり、名前付きブランチにいない) または実際のハッシュが見つかる ref へのテキスト リンク (たとえばref: refs/heads/master
、ブランチにいることを意味しますmaster
)のいずれかです。
誰かがオリジナルの (フォークではない) リポジトリorig
を Github に作成したとします。
シミュレートするために、ローカルで行います
$ git init --bare github_orig
上記がGithubサーバーで発生すると想像します。これで、空の github リポジトリができました。次に、自分の PC から github リポジトリのクローンを作成するとします。
$ git clone github_orig local_orig
もちろん、実生活では代わりにgithub_orig
を使用しますhttps://github...
。これで、github リポジトリのクローンが作成されましたlocal_orig
。
$ cd local_orig/
$ echo zzz > file
$ git add file
$ git commit -m initial
$ git push
$ cd ..
このdirgithub_orig
の後、プッシュされたコミット オブジェクト、1 つの blob オブジェクト、および 1 つのツリー オブジェクトが含まれます。ファイルにはコミット ハッシュが含まれます。object
file
refs/heads/master
では、誰かがFork
ボタンを押したときに何が起こるかを想像してみましょう。git リポジトリを作成しますが、手動で行います。
$ mkdir github_fork
$ cd github_fork/
$ cp ../github_orig/HEAD .
$ cp -r ../github_orig/refs .
$ ln -s ../github_orig/objects
$ cd ..
andをコピー していますが、 のシンボリック リンクを作成していることに注意してください。ご覧のとおり、フォークの作成は非常に安価です。数十のブランチがある場合でも、それぞれは単純な 16 進数のハッシュ (40 バイト) を含むディレクトリ内の単なるファイルです。元のオブジェクト ディレクトリにリンクするだけなので、何もコピーしません。HEAD
refs
objects
refs/heads
objects
ここで、ユーザーがフォークを作成し、フォークされたリポジトリをローカルで複製することをシミュレートします。
$ git clone github_fork local_fork
$ cd local_fork
$ # ls
.git/ file
objects
クローン元のリポジトリには独自のリポジトリはありませんが、元のリポジトリのリポジトリにリンクされていますが、クローンが正常に作成されたことがわかります。
これで、forking ユーザーはブランチを作成し、コミットしてから、それらを にプッシュできますgithub_fork
。オブジェクトは、 !objects
と同じディレクトリにプッシュされます。github_orig
ただしrefs
、 とHEAD
は変更され、 のものとは一致しなくなりgithub_orig
ます。
つまり、同じフォーク ツリーに属するすべてのリポジトリは共通のオブジェクト プールを共有し、各リポジトリには独自の参照が含まれているということです。自分のフォークされたリポジトリにコミットをプッシュする人は誰でも、自分の参照を変更しますが、オブジェクトを共有プールに入れます。
もちろん、実際に使用できるようにするには、さらにいくつかのことに注意する必要があります。最も重要なことは、git ガベージ コレクターは、それが呼び出されるリポジトリがすべての参照を認識していない限り、呼び出されてはならないということです。そうしないと、その参照からは到達できないが、他のリポジトリの参照からは到達できる共有プール内のオブジェクトが破棄される可能性があります。