LoginSignup
0
0

More than 1 year has passed since last update.

Gitオブジェクトってなにもの?

Posted at

Gitってsvnと比べると、同じリモートリポジトリで比較しても、かなり速いですよね。
まだまだGitには知らないことも多くあるので、この際調べてみると、Gitってスナップショットで管理していることを知りました。svnのように差分を積み上げる必要がない分、Gitは速いんだと思います。

とすると、スナップショットで保持するのに、なぜGitリポジトリは肥大化しないのか?という疑問が沸いてきましたが、Gitオブジェクトの構造で工夫しているんだろうと分かったので、自分の整理のために記事にしました。

Gitオブジェクトの種類

  • 4種類のオブジェクトが存在する
  • SHA-1ハッシュによる識別
種類 内容
blobオブジェクト ファイルに相当
treeオブジェクト ディレクトリに相当
commitオブジェクト コミットのスナップショット
tagオブジェクト タグ。今回は対象外

blobオブジェクト

  • ファイルに相当するオブジェクトで、ファイルの中身がzlibで圧縮されている。ファイル名は管理外。
  • ファイルの中身にblob <ファイルサイズ>\0をつけたSHA-1ハッシュ値。
    つまり、 ファイルの中身が同じであれば、同じオブジェクトとなる。
  • git addしたときに生成する

試してみる

  • 以下4ファイルの構成で試してみます
フォルダ構成とファイルの中身
│  readme.txt      ファイルの中身:hogehoge
│  sample.txt      ファイルの中身:this is sample
├─subA
│     aaa.txt      ファイルの中身:Hello aaa
└─subB
      bbb.txt      ファイルの中身:hogehoge
  • git add .で4ファイルをaddした結果、.git/objectsは以下となりました
.git/objects以下の構成
.git/objects/48/f685c5179595c9edbcfb5ebcc1beafac716857
.git/objects/64/e3d0eb4f3c190bf547221f28a364c49b9df77e
.git/objects/6f/27b4832a80eaa3123c0d1c256c41cd1835b33d
  • ここで4ファイルのハッシュ値を計算してみます。
    blob <ファイルサイズ>\0 <ファイルの中身>をsha-1計算します
    • blobオブジェクトのハッシュと一致していることが確認できます
    • ファイルが異なっていても、中身が同じであればblobオブジェクトは共通なことが確認できます
ハッシュ計算結果
$ (echo -en 'blob 8\0';cat readme.txt) | shasum
48f685c5179595c9edbcfb5ebcc1beafac716857 *-

$ (echo -en 'blob 14\0';cat sample.txt) | shasum
64e3d0eb4f3c190bf547221f28a364c49b9df77e *-

$ (echo -en 'blob 9\0';cat subA/aaa.txt) | shasum
6f27b4832a80eaa3123c0d1c256c41cd1835b33d *-

$ (echo -en 'blob 8\0';cat subB/bbb.txt) | shasum
48f685c5179595c9edbcfb5ebcc1beafac716857 *-
  • 次にこのblobオブジェクトを確認します。git cat-file <オプション> <gitオブジェクト>
    -pオプションを使うと、中身が見れました。
blobオブジェクトの確認
$ git cat-file -p 48f68
hogehoge

$ git cat-file -p 64e3d
this is sample

$ git cat-file -p 6f27b
Hello aaa

treeオブジェクトとcommitオブジェクト

  • treeオブジェクト
    • git commitしたときに生成される
    • ディレクトリに相当
    • ディレクトリごとに生成される
  • commitオブジェクト
    • git commitしたときに生成される
    • 親のcommitを参照している
    • 作成者や日付、コミットメッセージが格納されている

試してみる

  • git commit -m "test commit"した結果、.git/objectsは4つ増えていました
.git/objects以下の構成(増えたもののみ)
.git/objects/06/4bee06635089f2c96c653c71c24e01fe78bbf7
.git/objects/27/7f73e22786e4adfab6ec27ca1aa1baf9187e8d
.git/objects/80/dcb15ab2823857535ee6123204c0aa167ce938
.git/objects/f5/abca5b5cc24cf60e3d72ac4efeb7cb723bcf7a
  • このうち3つめはcommitオブジェクトで、残りがtreeオブジェクトのようです
gitオブジェクトのタイプ
 $ git cat-file -t 064be
tree

$ git cat-file -t 277f7
tree

$ git cat-file -t 80dcb
commit

$ git cat-file -t f5abc
tree
  • treeオブジェクトの中身を見てみます
    treeオブジェクトが、ディレクトリごとに生成されていることが分かります
treeオブジェクトの中身
$ git cat-file -p 064be
100644 blob 48f685c5179595c9edbcfb5ebcc1beafac716857    readme.txt
100644 blob 64e3d0eb4f3c190bf547221f28a364c49b9df77e    sample.txt
040000 tree 277f73e22786e4adfab6ec27ca1aa1baf9187e8d    subA
040000 tree f5abca5b5cc24cf60e3d72ac4efeb7cb723bcf7a    subB

$ git cat-file -p 277f7
100644 blob 6f27b4832a80eaa3123c0d1c256c41cd1835b33d    aaa.txt

$ git cat-file -p f5abc
100644 blob 48f685c5179595c9edbcfb5ebcc1beafac716857    bbb.txt
  • commitオブジェクトの中身を見ます
    • ルートのtreeオブジェクトの存在が確認できます
    • コミットしたユーザー、タイムスタンプやコミットメッセージが確認できます
commitオブジェクトの中身
$ git cat-file -p 80dcb
tree 064bee06635089f2c96c653c71c24e01fe78bbf7
author hoge <hoge@hoge.com> 1660563865 +0900
committer hoge <hoge@hoge.com> 1660563865 +0900

test commit
  • 図で表すとこんな感じ
    commit_80dcb.png

もう少し試してみます

  • echo -n "hogehoge" > sample.txtでsample.txtの中身を、readme.txtと合わせました
    中身が同じなので、blobオブジェクトは作られないはず。実際にはこんなユースケースないと思いますが
  • echo -n "abcdefg" > abc.txtで新しいファイルを作ります
  • git add .しましたが、予想通りGitオブジェクトは1つしか増えていません
    変更のないファイルはオブジェクトが作られないため、リポジトリ容量の肥大化を防いでいます
.git/objects以下の構成
.git/objects/06/4bee06635089f2c96c653c71c24e01fe78bbf7
.git/objects/27/7f73e22786e4adfab6ec27ca1aa1baf9187e8d
.git/objects/48/f685c5179595c9edbcfb5ebcc1beafac716857
.git/objects/64/e3d0eb4f3c190bf547221f28a364c49b9df77e
.git/objects/6f/27b4832a80eaa3123c0d1c256c41cd1835b33d
.git/objects/80/dcb15ab2823857535ee6123204c0aa167ce938
.git/objects/dd/a6eb4b7b86af8aea969498003937b7c8aa46be  これが増えました
.git/objects/f5/abca5b5cc24cf60e3d72ac4efeb7cb723bcf7a
  • 増えたGitオブジェクトを確認します
増えたGitオブジェクト
$ git cat-file -t dda6e
blob

$ git cat-file -p dda6e
abcdefg
  • git commit -m "2nd commit"したら、2つ増えました
.git/objects以下の構成
.git/objects/06/4bee06635089f2c96c653c71c24e01fe78bbf7
.git/objects/27/7f73e22786e4adfab6ec27ca1aa1baf9187e8d
.git/objects/48/f685c5179595c9edbcfb5ebcc1beafac716857
.git/objects/64/e3d0eb4f3c190bf547221f28a364c49b9df77e
.git/objects/6f/27b4832a80eaa3123c0d1c256c41cd1835b33d
.git/objects/72/d0fc47ed60e0f2abdfef26c9d48c2a0b56e3b0  これが増えた
.git/objects/80/dcb15ab2823857535ee6123204c0aa167ce938
.git/objects/a0/4d10c2f7ebd3490d126d88e5979b0c4913b865  これも増えた
.git/objects/dd/a6eb4b7b86af8aea969498003937b7c8aa46be
.git/objects/f5/abca5b5cc24cf60e3d72ac4efeb7cb723bcf7a
  • 増えたGitオブジェクトを確認します
    • commitしたので、増えた1つは当然commitオブジェクトです
    • もう1つはルートディレクトリにファイルが増えたので、treeオブジェクトです
増えたGitオブジェクト
$ git cat-file -t 72d0f
commit

$ git cat-file -p 72d0f
tree a04d10c2f7ebd3490d126d88e5979b0c4913b865
parent 80dcb15ab2823857535ee6123204c0aa167ce938
author hoge <hoge@hoge.com> 1660572580 +0900
committer hoge <hoge@hoge.com> 1660572580 +0900

2nd commit

$ git cat-file -t a04d1
tree

$ git cat-file -p a04d1
100644 blob dda6eb4b7b86af8aea969498003937b7c8aa46be    abc.txt
100644 blob 48f685c5179595c9edbcfb5ebcc1beafac716857    readme.txt
100644 blob 48f685c5179595c9edbcfb5ebcc1beafac716857    sample.txt
040000 tree 277f73e22786e4adfab6ec27ca1aa1baf9187e8d    subA
040000 tree f5abca5b5cc24cf60e3d72ac4efeb7cb723bcf7a    subB
  • 図で表すとこんな感じ

    • 赤の2つが増えて、黄色のハッシュが変わっています。
      commit_72d0f.png
  • こんなこともできます

HEADを使ってcommitオブジェクトの確認
$ git cat-file -p HEAD
tree a04d10c2f7ebd3490d126d88e5979b0c4913b865
parent 80dcb15ab2823857535ee6123204c0aa167ce938
author hoge <hoge@hoge.com> 1660572580 +0900
committer hoge <hoge@hoge.com> 1660572580 +0900

2nd commit

$ git cat-file -p HEAD~
tree 064bee06635089f2c96c653c71c24e01fe78bbf7
author hoge <hoge@hoge.com> 1660563865 +0900
committer korena hoge <hoge@hoge.com> 1660563865 +0900

test commit
HEADのtreeを確認
$ git cat-file -p HEAD^{tree}
100644 blob dda6eb4b7b86af8aea969498003937b7c8aa46be    abc.txt
100644 blob 48f685c5179595c9edbcfb5ebcc1beafac716857    readme.txt
100644 blob 48f685c5179595c9edbcfb5ebcc1beafac716857    sample.txt
040000 tree 277f73e22786e4adfab6ec27ca1aa1baf9187e8d    subA
040000 tree f5abca5b5cc24cf60e3d72ac4efeb7cb723bcf7a    subB

$ git ls-tree -r HEAD
100644 blob dda6eb4b7b86af8aea969498003937b7c8aa46be    abc.txt
100644 blob 48f685c5179595c9edbcfb5ebcc1beafac716857    readme.txt
100644 blob 48f685c5179595c9edbcfb5ebcc1beafac716857    sample.txt
100644 blob 6f27b4832a80eaa3123c0d1c256c41cd1835b33d    subA/aaa.txt
100644 blob 48f685c5179595c9edbcfb5ebcc1beafac716857    subB/bbb.txt

まとめ

こんな感じになるのかなぁと思ってます。間違っていたらご指摘ください

  • Gitはスナップショットで管理しているため、チェンジセット管理のsvnよりも速い
  • 変更がないファイルのblobオブジェクトは作られないため、リポジトリ容量の肥大化を防いでいる
  • blobファイルは、zlibで圧縮されているので、ここでも容量削減に貢献している
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0