commits in git

Eca2f293c36947e2a8944b39e57d105c?s=47 けんご
November 28, 2014

commits in git

Eca2f293c36947e2a8944b39e57d105c?s=128

けんご

November 28, 2014
Tweet

Transcript

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

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

  3. @tkengo Internal of git 内部動作と実装を学ぶことは、git がどうしてこんなに便利で有効な のかを根本的に理解するのに重要です。 ! 初期のgit(主として1.5以前)は、洗練されたVCS というよりもむし

    ろファイル・システムであることを(gitの特徴として)強調してお り、それ故に、ユーザー・インターフェイスは今よりも複雑なもので した。 ! 連想記憶ファイル・システム層は驚くほど素晴らしいので、この章の 最初にそれをカバーすることにします。 by Pro Git
  4. @tkengo Internal of git つまりgitはVCSの皮を被った連想記憶ファイルシステム だったんだよ!

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

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

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

    gitの本体 .git/ HEAD objects/ refs/ ...
  8. @tkengo Objects in git gitにはblog tree commit reference head tagという6つの重要な要素があります。

    blob tree commit R reference H head tag 愉快なgitの仲間たち
  9. @tkengo blob tree R reference Objects in git 今日は主にblob tree

    commitについて詳しく見ていきます。reference headはちょっとだけ出てきます。tagは今回でてきません。 commit H head tag 今日の登場人物 -
  10. @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 親 親 親 親 親 親 親
  11. @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 親 親 親 親 親 親 親
  12. @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
  13. @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ツリー
  14. @tkengo The snapshot commitとtree、blobについてもう少し詳しく見てみます。この辺から少しファイルシステムっぽい話になってきます。 blob tree commit tree blob blob

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

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

  17. @tkengo Detail of a blob blobは、ヘッダに対して実際のファイルの内容をくっつけたものを準備して、その内容をzlibで圧縮したものがblobの中身に、そして SHA1に通したものがblobのハッシュ値になります。 ddf3ec9 ヘッダ #include

    <stdio.h>! ! /* ϝΠϯؔ਺ */! int main() {! /* ग़ྗจࣈྻ */! char *str;! str = “Hello World!”;! ! printf(str);! return 0;! } blob 161/0 固定で “blob” スペース + ファイル ヌル 文字 zlib SHA1ハッシュ (161byte)
  18. @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 ディレクトリ名 ※実際はちょっと違うかも
  19. @tkengo Detail of a commit commitも同じく、ヘッダにコンテンツをくっつけたものです。commitのコンテンツは、rootツリーのSHA1ハッシュ値、親commitの SHA1ハッシュ値、コミットした人の情報、そしてコミットメッセージを持っています。 ヘッダ tree 9aeb4c2...!

    parent 5d5de46...! author kengo <kengo@t.com> ..! committer kengo <kengo@t.com> ..! ! first commit commit 180/0 固定で “commit” スペース + ヌル 文字 commitのコンテンツ zlib SHA1ハッシュ rootツリーのSHA1ハッシュ値 user.name と user.email の値 コミットメッセージ 親commitのSHA1ハッシュ値 6d025cd (180byte)
  20. @tkengo Summary of snapshot まとめると

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

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

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

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

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

    • blobはファイル内容を知っている てな感じです。
  26. @tkengo Summary of snapshot このようにファイルやディレクトリにSHA1ハッシュをつけてblobやtreeとして管理する方法こそgitが連想記憶ファイルシステムと言 われる所以だと言えます。それを発展させ、スナップショットとしてcommitと紐付けることでVCSとしての機能を実現しています。 1e11458 358a4b5 100d7a6 ddf3ec9

    3d4b084 358a4b5 という名前を持ったディレクトリ その実態は子ファイル及び子ディレクトリのSHA1ハッシュ値を並べたテキストデータを圧縮したもの 上に同じ 3d4b084 という名前を持ったファイル その実態はファイル内容そのものを圧縮したもの 上に同じ スナップショットはファイルシステムに似ています
  27. @tkengo blob tree R reference Objects in git blobはファイル、treeはディレクトリ、commitはスナップショットを指し示すもの、referenceは特定のcommitを参照する、headは 特定のreferenceを参照する、でした。

    commit H head tag 彼らのこと少しはわかりましたか? - ファイル ディレクトリ スナップショット へのポインタ commit へのポインタ reference へのポインタ
  28. @tkengo Difference management スナップショットはわかったけど 差分ってどこに持ってるの?

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

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

    8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084
  31. @tkengo Difference management つまりcommitが指すスナップショット同士を比較して、gitが差分を計算してくれています。 1e11458 358a4b5 6d025cd 100d7a6 ddf3ec9 3d4b084

    8d4a726 2629bfc a25b132 100d7a6 ddf3ec9 3d4b084 git「あ、この ファイルの中身が 変わってるな」
  32. @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 親
  33. @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 親 ディスク容量は確かに非効率
  34. @tkengo Save the disk space ディスク容量の節約について

  35. @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
  36. @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
  37. @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
  38. @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!!
  39. @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..
  40. @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
  41. @tkengo Save the disk space このような表現の方が正しそうです。commitが違っても、容量節約のためにスナップショットは同じところを指している部分があり ます。 1e11458 358a4b5 6d025cd

    8d4a726 2629bfc a25b132 5bd7d91 2629bfc 2125db4 0926525 ddf3ec9 a214dca 100d7a6 ddf3ec9 3d4b084
  42. @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
  43. @tkengo Internal of git これでgitの内部の説明は終わりです。 ! もう少し実感してもらうために 最後に各オブジェクトがどんな風に 保存されているかを見てみます。

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

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

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

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

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

    ... .git/ master feature-branch ... heads/ ref: refs/heads/master
  50. @tkengo Internal of git gitのこと少しでもわかったでしょうか。

  51. おわり