Slide 1

Slide 1 text

gitのコミットの話 内部構造から見るgitのコミット @tkengo 2014-11-28

Slide 2

Slide 2 text

@tkengo Internal of git 最近gitの仕組みを興味本位で調べて遊んでたので 今日はその辺の話をしてみたいと思います

Slide 3

Slide 3 text

@tkengo Internal of git 内部動作と実装を学ぶことは、git がどうしてこんなに便利で有効な のかを根本的に理解するのに重要です。 ! 初期のgit(主として1.5以前)は、洗練されたVCS というよりもむし ろファイル・システムであることを(gitの特徴として)強調してお り、それ故に、ユーザー・インターフェイスは今よりも複雑なもので した。 ! 連想記憶ファイル・システム層は驚くほど素晴らしいので、この章の 最初にそれをカバーすることにします。 by Pro Git

Slide 4

Slide 4 text

@tkengo Internal of git つまりgitはVCSの皮を被った連想記憶ファイルシステム だったんだよ!

Slide 5

Slide 5 text

@tkengo Internal of git ΩΩΩ < な、なんだってー つまりgitはVCSの皮を被った連想記憶ファイルシステム だったんだよ!

Slide 6

Slide 6 text

@tkengo The body of git gitの本体はリポジトリのルートディレクトリにある.gitというディレクトリです。 gitの本体 .git/

Slide 7

Slide 7 text

@tkengo The body of git .gitディレクトリの中にはいくつかのファイルとディレクトリが存在しますが、今日の説明で関係があるのはHEAD objects/ refs/ の 3つです。 gitの本体 .git/ HEAD objects/ refs/ ...

Slide 8

Slide 8 text

@tkengo Objects in git gitにはblog tree commit reference head tagという6つの重要な要素があります。 blob tree commit R reference H head tag 愉快なgitの仲間たち

Slide 9

Slide 9 text

@tkengo blob tree R reference Objects in git 今日は主にblob tree commitについて詳しく見ていきます。reference headはちょっとだけ出てきます。tagは今回でてきません。 commit H head tag 今日の登場人物 -

Slide 10

Slide 10 text

@tkengo Objects in git gitはcommitしたその時点でのファイルツリーのスナップショットを持っており、スナップショットにはtreeとblobが含まれていま す。referenceはある特定のcommitを指しており、さらにheadは特定のreferenceを指しています。 R reference H head blob tree commit tree blob blob blob tree commit tree blob blob blob tree commit tree blob blob commit 省略... commit 省略... commit 省略... commit 省略... commit 省略... R reference 親 親 親 親 親 親 親

Slide 11

Slide 11 text

@tkengo Objects in git コミットはご存知の通りSHA1のハッシュ値を持っています。referenceは実は普段使っているブランチのことです。そしてHEADは今 の作業位置を示しています。 R feature-branch H HEAD blob tree 6d025cd tree blob blob blob tree 8d4a726 tree blob blob blob tree 5bd7d91 tree blob blob 96caf5f 省略... 31e5f34 省略... edd97e4 省略... fdf793c 省略... 1d09b9b 省略... R master 親 親 親 親 親 親 親

Slide 12

Slide 12 text

@tkengo Objects in git 実はスナップショットのtreeとblobにもそれぞれSHA1のハッシュ値がついています。 R feature-branch H HEAD 1e11458 358a4b5 6d025cd 100d7a6 8d4a726 5bd7d91 96caf5f 省略... 31e5f34 省略... edd97e4 省略... fdf793c 省略... 1d09b9b 省略... R master 親 親 親 親 親 親 親 ddf3ec9 3d4b084 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 2629bfc 2125db4 0926525 ddf3ec9 a214dca

Slide 13

Slide 13 text

@tkengo Objects in git そしてスナップショットには、そのスナップショットを一意に識別するrootツリーがあり、commitはそのrootツリーのSHA1ハッ シュ値を知っています。 H HEAD 1e11458 358a4b5 6d025cd 100d7a6 8d4a726 5bd7d91 96caf5f 省略... edd97e4 省略... fdf793c 省略... R master 親 親 親 親 親 ddf3ec9 3d4b084 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 2629bfc 2125db4 0926525 ddf3ec9 a214dca rootツリー

Slide 14

Slide 14 text

@tkengo The snapshot commitとtree、blobについてもう少し詳しく見てみます。この辺から少しファイルシステムっぽい話になってきます。 blob tree commit tree blob blob

Slide 15

Slide 15 text

@tkengo The snapshot treeはファイルシステム上のディレクトリに相当します。 blob tree commit tree blob blob

Slide 16

Slide 16 text

@tkengo The snapshot blobはファイルシステム上のファイルに相当します。 blob tree commit tree blob blob

Slide 17

Slide 17 text

@tkengo Detail of a blob blobは、ヘッダに対して実際のファイルの内容をくっつけたものを準備して、その内容をzlibで圧縮したものがblobの中身に、そして SHA1に通したものがblobのハッシュ値になります。 ddf3ec9 ヘッダ #include ! ! /* ϝΠϯؔ਺ */! int main() {! /* ग़ྗจࣈྻ */! char *str;! str = “Hello World!”;! ! printf(str);! return 0;! } blob 161/0 固定で “blob” スペース + ファイル ヌル 文字 zlib SHA1ハッシュ (161byte)

Slide 18

Slide 18 text

@tkengo Detail of a tree treeもblobと同じく、ヘッダにツリーのコンテンツをくっつけたものがtreeになります。treeのコンテンツは、そのtreeに含まれる tree及びblobの一覧をもっています。 9aeb4c2 ヘッダ 100644 tree 652e759… builtin! 100644 blob 7ba2ba7… main.c! 100644 blob e6c1c39… mem.c! ...! tree 177/0 固定で “tree” スペース + (177byte) ヌル 文字 treeのコンテンツ zlib SHA1ハッシュ オブジェクトのモード オブジェクトの種類 tree or blob SHA1ハッシュ値 ファイル名 or ディレクトリ名 ※実際はちょっと違うかも

Slide 19

Slide 19 text

@tkengo Detail of a commit commitも同じく、ヘッダにコンテンツをくっつけたものです。commitのコンテンツは、rootツリーのSHA1ハッシュ値、親commitの SHA1ハッシュ値、コミットした人の情報、そしてコミットメッセージを持っています。 ヘッダ tree 9aeb4c2...! parent 5d5de46...! author kengo ..! committer kengo ..! ! first commit commit 180/0 固定で “commit” スペース + ヌル 文字 commitのコンテンツ zlib SHA1ハッシュ rootツリーのSHA1ハッシュ値 user.name と user.email の値 コミットメッセージ 親commitのSHA1ハッシュ値 6d025cd (180byte)

Slide 20

Slide 20 text

@tkengo Summary of snapshot まとめると

Slide 21

Slide 21 text

@tkengo Summary of snapshot • commitはその時点のスナップショットのrootツリーを知っている blob tree commit tree blob blob ココ

Slide 22

Slide 22 text

@tkengo Summary of snapshot • commitはその時点のスナップショットのrootツリーを知っている • commitは自分の親のcommitも知っている blob tree commit tree blob tree commit tree 親 ココ

Slide 23

Slide 23 text

@tkengo Summary of snapshot • commitはその時点のスナップショットのrootツリーを知っている • commitは自分の親のcommitも知っている • treeはそのtreeに含まれているtree及びblobを全部知っている blob tree commit tree ココ

Slide 24

Slide 24 text

@tkengo Summary of snapshot • commitはその時点のスナップショットのrootツリーを知っている • commitは自分の親のcommitも知っている • treeはそのtreeに含まれているtree及びblobを全部知っている • blobはファイル内容を知っている ヘッダ #include ! ! /* ϝΠϯؔ਺ */! int main() {! /* ग़ྗจࣈྻ */! char *str;! str = “Hello World!”;! ! printf(str);! return 0;! } コンテンツ tree 161/0 blob

Slide 25

Slide 25 text

@tkengo Summary of snapshot • commitはその時点のスナップショットのrootツリーを知っている • commitは自分の親のcommitも知っている • treeはそのtreeに含まれているtree及びblobを全部知っている • blobはファイル内容を知っている てな感じです。

Slide 26

Slide 26 text

@tkengo Summary of snapshot このようにファイルやディレクトリにSHA1ハッシュをつけてblobやtreeとして管理する方法こそgitが連想記憶ファイルシステムと言 われる所以だと言えます。それを発展させ、スナップショットとしてcommitと紐付けることでVCSとしての機能を実現しています。 1e11458 358a4b5 100d7a6 ddf3ec9 3d4b084 358a4b5 という名前を持ったディレクトリ その実態は子ファイル及び子ディレクトリのSHA1ハッシュ値を並べたテキストデータを圧縮したもの 上に同じ 3d4b084 という名前を持ったファイル その実態はファイル内容そのものを圧縮したもの 上に同じ スナップショットはファイルシステムに似ています

Slide 27

Slide 27 text

@tkengo blob tree R reference Objects in git blobはファイル、treeはディレクトリ、commitはスナップショットを指し示すもの、referenceは特定のcommitを参照する、headは 特定のreferenceを参照する、でした。 commit H head tag 彼らのこと少しはわかりましたか? - ファイル ディレクトリ スナップショット へのポインタ commit へのポインタ reference へのポインタ

Slide 28

Slide 28 text

@tkengo Difference management スナップショットはわかったけど 差分ってどこに持ってるの?

Slide 29

Slide 29 text

@tkengo Difference management スナップショットはわかったけど 差分ってどこに持ってるの? gitはcommit間の差分は管理していません 実は

Slide 30

Slide 30 text

@tkengo Difference management gitは、commit間の差分を比較する場合には、差分を持っていないのでどこからか差分を計算する必要があります。 1e11458 358a4b5 6d025cd 100d7a6 ddf3ec9 3d4b084 8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084

Slide 31

Slide 31 text

@tkengo Difference management つまりcommitが指すスナップショット同士を比較して、gitが差分を計算してくれています。 1e11458 358a4b5 6d025cd 100d7a6 ddf3ec9 3d4b084 8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 git「あ、この ファイルの中身が 変わってるな」

Slide 32

Slide 32 text

@tkengo Save the disk space それにしても、このようにcommit毎にスナップショットを持っていたら大変なことにならないでしょうか。とても効率が悪そうな気 がします。 blob tree commit tree blob blob 親 blob tree commit tree blob blob 親 blob tree commit tree blob blob 親 blob tree commit tree blob blob 親 blob tree commit tree blob blob 親 blob tree commit tr blob b blob tree commit tree blob blob 親

Slide 33

Slide 33 text

@tkengo Save the disk space 確かに効率は悪いですが、それはディスク容量の部分です。差分計算やcheckoutなどはスナップショットで管理した方が圧倒的に早 いですし、ディスク容量もなるべく節約できるように工夫されています。 blob tree commit tree blob blob 親 blob tree commit tree blob blob 親 blob tree commit tree blob blob 親 blob tree commit tree blob blob 親 blob tree commit tree blob blob 親 blob tree commit tr blob b blob tree commit tree blob blob 親 ディスク容量は確かに非効率

Slide 34

Slide 34 text

@tkengo Save the disk space ディスク容量の節約について

Slide 35

Slide 35 text

@tkengo Save the disk space 最初の方に出てきたこの図をみて気付いた人がいたら素晴らしいですが、gitはcommit毎にスナップショットを保存するといっても、 commit毎に全てのファイルとディレクトリを保存しているわけではありません。 1e11458 358a4b5 6d025cd 100d7a6 8d4a726 5bd7d91 親 親 ddf3ec9 3d4b084 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 2629bfc 2125db4 0926525 ddf3ec9 a214dca

Slide 36

Slide 36 text

@tkengo Save the disk space この2つのcommitのスナップショットを見ていきます。 1e11458 358a4b5 6d025cd 100d7a6 ddf3ec9 3d4b084 8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 5bd7d91 2629bfc 2125db4 0926525 ddf3ec9 a214dca

Slide 37

Slide 37 text

@tkengo Save the disk space 2つのスナップショットを比べると、rootツリーと、その下にある1つのblobがそれぞれ別のSHA1ハッシュ値を持っています。 1e11458 358a4b5 6d025cd 100d7a6 ddf3ec9 3d4b084 8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 5bd7d91 2629bfc 2125db4 0926525 ddf3ec9 a214dca

Slide 38

Slide 38 text

@tkengo Save the disk space もう少し詳しく見ていきます。まずはblobの方です。たとえばblobは上記のようなファイル内容を持っており、ビックリマークを2つ 追加する変更が加えられているとします。すると 1e11458 のblobの内容が変わって新たに 2629bfc のblobが作られます。 1e11458 358a4b5 6d025cd 100d7a6 ddf3ec9 3d4b084 8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 5bd7d91 2629bfc 2125db4 0926525 ddf3ec9 a214dca blob 11/0Hello World blob 13/0Hello World!!

Slide 39

Slide 39 text

@tkengo Save the disk space すると、そのblobのSHA1ハッシュ値をしっている親のtreeも、子供のblobのハッシュ値を書き換えなければなりません。つまり新し く a25b132 のtreeが作られます。 1e11458 358a4b5 6d025cd 100d7a6 ddf3ec9 3d4b084 8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 5bd7d91 2629bfc 2125db4 0926525 ddf3ec9 a214dca tree 113/0! 100644 blob 1e11458..! 040000 tree 100d7a6.. tree 113/0! 100644 blob 2629bfc..! 040000 tree 100d7a6..

Slide 40

Slide 40 text

@tkengo Save the disk space しかし変更のない他の 100d7a6 ddf3ec9 3d4b084 はそのまま再利用されています。 1e11458 358a4b5 6d025cd 100d7a6 ddf3ec9 3d4b084 8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 5bd7d91 2629bfc 2125db4 0926525 ddf3ec9 a214dca

Slide 41

Slide 41 text

@tkengo Save the disk space このような表現の方が正しそうです。commitが違っても、容量節約のためにスナップショットは同じところを指している部分があり ます。 1e11458 358a4b5 6d025cd 8d4a726 2629bfc a25b132 5bd7d91 2629bfc 2125db4 0926525 ddf3ec9 a214dca 100d7a6 ddf3ec9 3d4b084

Slide 42

Slide 42 text

@tkengo Save the disk space この2つのcommitのスナップショットでも、同様のことが言えます。とあるblobの変化は最終的にはスナップショットの一番上のtree つまりrootツリーにまで変更が伝搬していくことになります。これが各スナップショット間でrootツリーが一意になる理由です。 1e11458 358a4b5 6d025cd 100d7a6 ddf3ec9 3d4b084 8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 5bd7d91 2629bfc 2125db4 0926525 ddf3ec9 a214dca

Slide 43

Slide 43 text

@tkengo Internal of git これでgitの内部の説明は終わりです。 ! もう少し実感してもらうために 最後に各オブジェクトがどんな風に 保存されているかを見てみます。

Slide 44

Slide 44 text

@tkengo The body of git 最初に.gitディレクトリの中のHEAD index objects/ refs/ の3つのファイル及びディレクトリの話をしたのを覚えているでしょうか。 HEAD objects/ refs/ ... .git/

Slide 45

Slide 45 text

@tkengo The body of git それぞれのgitオブジェクトはこのように.gitディレクトリ以下に格納されています。 HEAD objects/ refs/ ... blob tree commit R reference H head .git/

Slide 46

Slide 46 text

@tkengo The body of git HEADはただのテキストファイルです。catなどで中身を見てみると現在のheadが指しているreferenceが書いてあります。 HEAD objects/ refs/ ... .git/ ref: refs/heads/master

Slide 47

Slide 47 text

@tkengo The body of git objects/の下には、各オブジェクトのSHA1ハッシュ値の先頭2桁がディレクトリ名、残り38桁がファイル名になってファイルが格納 されています。ディレクトリを分ける理由は1ディレクトリ中に置けるファイル数の上限を回避するためかと思います。 HEAD objects/ refs/ ... .git/ 10/ 26/ 8d/ a2/ ... 0d7a6... 29bfc... 4a726... 5b132... d7644... ... 100d7a6 (blob) 2629bfc (blob) 8d4a726 (commit) a25b132 (tree) a2d7644 (blob)

Slide 48

Slide 48 text

@tkengo The body of git refs/の下にはさらにheads/ディレクトリがあり、その中にブランチ名をファイル名としたファイルがあります。中身はただのテキス トファイルで、そのブランチが指し示しているcommitのSHA1ハッシュ値が書き込まれています。 HEAD objects/ refs/ ... .git/ master feature-branch ... heads/

Slide 49

Slide 49 text

@tkengo The body of git もう一度HEADの中身を確認してみます。refs/heads/masterと書かれていましたね。これはつまりrefs/以下のディレクトリを指して たってことですね。 HEAD objects/ refs/ ... .git/ master feature-branch ... heads/ ref: refs/heads/master

Slide 50

Slide 50 text

@tkengo Internal of git gitのこと少しでもわかったでしょうか。

Slide 51

Slide 51 text

おわり