3
0

More than 1 year has passed since last update.

GitのPackfileの中身を見てみる

Last updated at Posted at 2021-12-30

前提

  • 公式サイトのBookにある説明を読んでいるものとしています
  • この記事は公式の説明pack-formatを参考に Gitのソースコードを読んで確認した内容を示しています
  • あえていうまでもありませんが、内容は保証しません

参考文献

  • 上記Bookとpack-format
  • Gitのソースコード

Packfileの記事に書かれていること

  • Packfileにはコミット・ツリー・Blobのいずれも含まれている
  • よく似たオブジェクトは差分化して保存されている

pack-formatの記事に書かれていること

  • pack-*.packには次のデータがある
    • char[4]: マジックナンバー"PACK"
    • uint32_t: バージョン
    • uint32_t: オブジェクト数
    • 差分化されていないデータ
      • $n$バイトのヘッダ: $3$-bitのタイプ+${(n-1)\times7 + 4}$-bitのサイズ
      • 圧縮データ
      • 差分化されたデータ
        • 上記と同じようなヘッダ。ただし、タイプはOBJ_REF_DELTA(7)または OBJ_OFS_DELTA(6)である。 このタイプは基準となっているデータを指し示す方法を表す。 - 圧縮された差分データ - .packのチェックサム
  • 差分データの中身は次の二つ
    • 0b1xxxxxxx ...: 基準となっているデータからコピーする
      • 0b11111111 offset1 offset2 offset3 offset4 size1 size2 size3
      • offsetsizeはリトルエンディアン
      • 直前の操作のあとに、直前のコピーで読み出した位置からoffsetバイト読み飛ばして sizeバイトコピーする - 0b0nnnnnnn ...: データを追加する - 続くnバイトの値を直前の操作の後に書く
  • pack-*.idxには次のデータがある(バージョン2の場合)
    • char[4]: マジックナンバー "\377tOc"
    • uint32_t: バージョン
    • uint32_t[256]: ハッシュ値の第1バイトがidx以下であるpack内のオブジェクトの数 (※公式サイトの図参照)
    • packに格納されたハッシュ値の配列。昇順にソートされている。
    • uint32_t[n_obj]: CRC32チェックサム(オブジェクトのハッシュ値昇順)
    • uint32_t[n_obj]: オフセット(.packのサイズが4GB以下の場合)
    • .packのサイズが2GB以上の場合、ここにオフセットの続きが入る)
    • .packのチェックサム
    • .idxのチェックサム
  • pack-*.revMIDXについては、現物を見たことがないので略

実際に見てみる

  • 適当に作ったGitリポジトリのオブジェクトを実際に読み取ってみる
    • この記事の内容を格納したリポジトリを作って試した。

git fast-import用のリポジトリデータ(タブがコピペでスペースに化けるので再現できませんが、本旨には関係ないので許してください)
% git fast-export --all
blob
mark :1
data 954
GitのPackfileの中身を見てみる

# 前提
* 公式サイトのBookにある説明を読んでいるものとしています
    - [10.2 Gitオブジェクト](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88)
    - [10.3 Gitの参照](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%81%AE%E5%8F%82%E7%85%A7)
    - [10.4 Packfile](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Packfile)
* この記事は公式の説明[pack-format](https://git-scm.com/docs/pack-format)を参考に
  Gitのソースコードを読んで確認した内容を示しています
* あえていうまでもありませんが、内容は保証しません

# Packfileの記事に書かれていること
* Packfileにはコミット・ツリー・Blobのいずれも含まれている。
* よく似たオブジェクトは差分化して保存されている

reset refs/heads/master
commit refs/heads/master
mark :2
author Takashi Sakai <sakai@localhost> 1640850064 +0900
committer Takashi Sakai <sakai@localhost> 1640850064 +0900
data 15
initial commit
M 100644 :1 a.txt

blob
mark :3
data 3127
GitのPackfileの中身を見てみる

# 前提
* 公式サイトのBookにある説明を読んでいるものとしています
    - [10.2 Gitオブジェクト](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88)
    - [10.3 Gitの参照](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%81%AE%E5%8F%82%E7%85%A7)
    - [10.4 Packfile](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Packfile)
* この記事は公式の説明[pack-format](https://git-scm.com/docs/pack-format)を参考に
  Gitのソースコードを読んで確認した内容を示しています
* あえていうまでもありませんが、内容は保証しません

# Packfileの記事に書かれていること
* Packfileにはコミット・ツリー・Blobのいずれも含まれている
* よく似たオブジェクトは差分化して保存されている

# pack-formatの記事に書かれていること
* ```pack-*.pack```には次のデータがある
    - ```char[4]```: マジックナンバー"PACK"
    - ```uint32_t```: バージョン
    - ```uint32_t```: オブジェクト数
    - 差分化されていないデータ
        - nバイトのヘッダ: 3-bitのタイプ+{(n-1)*7 + 4}-bitのサイズ
        - 圧縮データ
        - 差分化されたデータ
            - 上記と同じようなヘッダ。ただし、タイプは```OBJ_REF_DELTA```(7)または
              ```OBJ_OFS_DELTA```(6)である。
              このタイプは基準となっているデータを指し示す方法を表す。
                - 圧縮された差分データ
        - ```.pack```のチェックサム
* 差分データの中身は次の二つ
    - ```0b1xxxxxxx ...```: 基準となっているデータからコピーする
        - ```0b11111111 offset1 offset2 offset3 offset4 size1 size2 size3```
        - ```offset``````size```はリトルエンディアン
        - 直前の操作のあとに、直前のコピーで読み出した位置から```offset```バイト読み飛ばして
          ```size```バイトコピーする
        - ```0b0nnnnnnn ...```: データを追加する
            - 続くnバイトの値を直前の操作の後に書く
* ```pack-*.idx```には次のデータがある(バージョン2の場合)
    - ```char[4]```: マジックナンバー "\377tOc"
    - ```uint32_t```: バージョン
    - ```uint32_t[256]```: ハッシュ値の第1バイトがidx以下であるpack内のオブジェクトの数
      (※[公式サイトの図](https://git-scm.com/docs/pack-format#_original_version_1_pack_idx_files_have_the_following_format)参照)
    - ```uint32_t[n_obj]```: CRC32チェックサム(オブジェクトのハッシュ値昇順)
    - ```uint32_t[n_obj]```: オフセット(```.pack```のサイズが4GB以下の場合)
    - (```.pack```のサイズが2GB以上の場合、ここにオフセットの続きが入る)
    - ```.pack```のチェックサム
    - ```.idx```のチェックサム
* ```pack-*.rev``````MIDX```については、現物を見たことがないので略

commit refs/heads/master
mark :4
author Takashi Sakai <sakai@localhost> 1640851556 +0900
committer Takashi Sakai <sakai@localhost> 1640851556 +0900
data 14
add some text
from :2
M 100644 :3 a.txt

  • 対象のオブジェクトを決める。

    % git verify-pack -v .git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack
    bd662ac2ac6b1225be7772779424870aae89fdfb commit 224 154 12
    2e3d72440b11dbb1d4ec46ff75d7bc4a550cfdc5 commit 177 121 166
    44126f1961c05cf7c640a1da57128dac91007668 blob   3127 1355 287
    7721218b8e626bd5fb8510df87e5b3d070e548c9 tree   33 44 1642
    3431f58b1fdac86856bbf5a6dd9ed5186646d9fa tree   33 46 1686
    a4ebaf4caaf976aac558dcacfa4ec9d692cafaf3 blob   15 26 1732 1 44126f1961c05cf7c640a1da57128dac91007668
    non delta: 5 objects
    chain length = 1: 1 object
    .git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack: ok
    
  • 差分化されたものを例にしたいのでa4ebaf4caaf976aac558dcacfa4ec9d692cafaf3
    使う

  • 前節の説明より、.packの方をいきなり見ても、ほとんどの部分は圧縮データなのでデータの起点が特定できない(頭から順に読んでいけば読み出せるが面倒なのでやらない。)

インデックス編

  • まず.idxで目的のデータを探す

.idxの内容
  #          t  O  c  ver.      2
  0000000 ff 74 4f 63 00 00 00 02
  # 4バイト整数256個
  0000008 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  (snip)
  00000a8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00000b8 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01
  00000c8 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01
  00000d8 00 00 00 02 00 00 00 02 00 00 00 02 00 00 00 02
  (snip)
  0000108 00 00 00 02 00 00 00 02 00 00 00 02 00 00 00 02
  0000118 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 03
  (snip)
  00001d8 00 00 00 03 00 00 00 03 00 00 00 03 00 00 00 04
  00001e8 00 00 00 04 00 00 00 04 00 00 00 04 00 00 00 04
  (snip)
  0000288 00 00 00 04 00 00 00 04 00 00 00 04 00 00 00 04
  0000298 00 00 00 05 00 00 00 05 00 00 00 05 00 00 00 05
  (snip)
  00002e8 00 00 00 05 00 00 00 05 00 00 00 05 00 00 00 05
  00002f8 00 00 00 05 00 00 00 06 00 00 00 06 00 00 00 06
  0000308 00 00 00 06 00 00 00 06 00 00 00 06 00 00 00 06
  (snip)
  00003f8 00 00 00 06 00 00 00 06 00 00 00 06 00 00 00 06
  # オブジェクトハッシュ(6件、昇順、各20(0x14)バイト)
  0000408 2e 3d 72 44 0b 11 db b1 d4 ec 46 ff 75 d7 bc 4a
  0000418 55 0c fd c5
  000041c 34 31 f5 8b 1f da c8 68 56 bb f5 a6 dd 9e d5 18
  000042c 66 46 d9 fa
  0000430 44 12 6f 19 61 c0 5c f7 c6 40 a1 da 57 12 8d ac
  0000440 91 00 76 68
  0000444 77 21 21 8b 8e 62 6b d5 fb 85 10 df 87 e5 b3 d0
  0000454 70 e5 48 c9
  0000458 a4 eb af 4c aa f9 76 aa c5 58 dc ac fa 4e c9 d6
  0000468 92 ca fa f3
  000046c bd 66 2a c2 ac 6b 12 25 be 77 72 77 94 24 87 0a
  000047c ae 89 fd fb
  # CRC32(6件、同順、各4バイト)
  0000480 96 64 8d b1 5c b3 6c 97 a2 f3 5d 6e 08 fc a5 8a
  0000490 40 d2 d9 59 9d 23 99 de
  # オフセット(6件、同順、各4バイト)
  0000498 00 00 00 a6 00 00 06 96 00 00 01 1f 00 00 06 6a
  00004a8 00 00 06 c4 00 00 00 0c
  # packハッシュ
  00004b0 6b 39 32 9a ed 24 19 0a a8 35 8b 85 f1 0d 1c 0e
  00004c0 52 84 51 d7
  # idxハッシュ
  00004c4 9d 10 22 6c 21 e5 7a 12 55 d3 e4 b5 08 10 0f ff
  00004d4 db ed 77 cb

  • 今回探すのはa4ebaf4caaf976aac558dcacfa4ec9d692cafaf3なので 0xA4(164)-1番目の整数を見てみる。
    • 0xA3 × 4 = 0x28c。起点が0x8なので0x294を見る。
0000288 00 00 00 04 00 00 00 04 00 00 00 04 00 00 00 04
                                            ^^^^^^^^^^^  
0000298 00 00 00 05 00 00 00 05 00 00 00 05 00 00 00 05
(略)
00003f8 00 00 00 06 00 00 00 06 00 00 00 06 00 00 00 06
  • 00 00 00 04である。 よって、a4〜〜というハッシュのオブジェクトは4番目から始まるとわかる。
    • また、データの総数もわかる。最後(256番目)の整数が00 00 00 06なので 総数は6である。
  • この情報を参考に、a4ebaf4caaf976aac558dcacfa4ec9d692cafaf3を探す。

    1. 4バイト整数256個が終わるのは0000408。ここからハッシュ値の配列が続く。
    2. このリポジトリのハッシュ値はSHA-1なので20バイト(160 bits)。20×4 = 80(0x50)バイト読み飛ばす。
    3. 0000458をみるとちょうどそこにa4 eb af 4c ... fa f3がある。
      a4ebaf4caaf976aac558dcacfa4ec9d692cafaf3は5番目である。

        00003f8 00 00 00 06 00 00 00 06 00 00 00 06 00 00 00 06
        # オブジェクトハッシュ(6件、昇順、各20(0x14)バイト)
        #0
        0000408 2e 3d 72 44 0b 11 db b1 d4 ec 46 ff 75 d7 bc 4a
        0000418 55 0c fd c5
        000041c 34 31 f5 8b 1f da c8 68 56 bb f5 a6 dd 9e d5 18
        000042c 66 46 d9 fa
        0000430 44 12 6f 19 61 c0 5c f7 c6 40 a1 da 57 12 8d ac
        0000440 91 00 76 68
        0000444 77 21 21 8b 8e 62 6b d5 fb 85 10 df 87 e5 b3 d0
        0000454 70 e5 48 c9
        #4
        0000458 a4 eb af 4c aa f9 76 aa c5 58 dc ac fa 4e c9 d6
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        0000468 92 ca fa f3
                ^^^^^^^^^^^
        000046c bd 66 2a c2 ac 6b 12 25 be 77 72 77 94 24 87 0a
        000047c ae 89 fd fb
      
  • これでついに.pack内のアドレスが得られる。

    1. ハッシュ値の配列は0000408から始まって、20バイト×6個、120(0x78)バイトである。 よって、次のデータは0000480から始まる。
    2. 続くのはCRC32のチェックサムである。これは今回使わないので読み飛ばす。 4バイト×6個=24(0x18)バイト読み飛ばして、次は0000498から始まる。
    3. 次がまさに求めている.pack内のアドレスである。 この5番目を読みたいので4個=16(0x10)バイト飛ばす。00004A8
    4. 00004a8には00 00 06 c4と書いてある。アドレスゲットである。

      # CRC32(6件、同順、各4バイト)
      0000480 96 64 8d b1 5c b3 6c 97 a2 f3 5d 6e 08 fc a5 8a
      0000490 40 d2 d9 59 9d 23 99 de
      # オフセット(6件、同順、各4バイト)
      0000498 00 00 00 a6 00 00 06 96 00 00 01 1f 00 00 06 6a
              0           1           2           3
      00004a8 00 00 06 c4 00 00 00 0c
              4^^^^^^^^^^ 5
      
  • 同じ要領でこのオブジェクトの元のオブジェクト44126f1961c05cf7c640a1da57128dac91007668のアドレスも取得すると、0x11fである。

    • わかりやすさのため、まずこの0x11fを読んでみる。

普通オブジェクトの中身

  • 先ほどのインデックスのオフセットのリストを見てみると0x11fの次は0x66aから始まるとわかる。

      0000498 00 00 00 a6 00 00 06 96 00 00 01 1f 00 00 06 6a
      00004a8 00 00 06 c4 00 00 00 0c
    
    • (0x11fの次に小さい値は0x66aになっている。)
  • .packから0x11fから0x66a(1642)を取り出してみる

  % head -c 1642 .git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack | hexdump -s 0x11f
  000011f b7 c3 01 78 9c ad 56 6b 53 1a 57 18 fe 1c 7e c5
  000012f 8e 19 66 c4 0c 20 17 85 fa 0d 0c 3a e9 65 cc a4
  (snip)
  000064f 51 5f fa 8d 7f fd 67 94 4f 07 b6 83 d8 49 4b 85
  000065f 27 5b df d8 37 fc 05 5a c3 3b c7
  • b7 c3 01は「3127バイトのBlob」を表している
    • b7 (0b1_011_0111): タイプ=3(OBJ_BLOB), サイズ=0b...0111
    • c3 (0b1_1000011): サイズ=0b...1000011_0111
    • 01: サイズ = 0b0000001_1000011_0111 = 0xC37(3127)
  • 中身

    • この後はDeflate圧縮したデータが入っている。例えばpigzコマンドをインストールして、 これを使うことで伸長できる
      • 今回のリポジトリはこの記事のテキストの序盤を入れて作っているので、このような内容が出てくる。
    % dd bs=1 skip=0x122 count=0x548 \
      if=.git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack \
            2>/dev/null | pigz -dc
    GitのPackfileの中身を見てみる
    
    # 前提
    * 公式サイトのBookにある説明を読んでいるものとしています
    (snip)
    
    • looseオブジェクトにおけるblob (サイズ)\0のヘッダはついていない。 これは、type情報と重複するためと考えられる。

差分オブジェクトの読み出し

  • 先ほど取得したアドレス0x6c4をpackファイルから読み出してみる。

    % cat .git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack | hexdump -s 0x6c4
    00006c4 6f 8a 25 78 9c db 2e b1 8b 7d 43 1e 33 f3 e3 86
    00006d4 a6 c9 79 cc 9e 00 31 91 05 e7 6b 39 32 9a ed 24
    00006e4 19 0a a8 35 8b 85 f1 0d 1c 0e 52 84 51 d7
    00006f2
    
    • .packの末尾20(0x14)バイトは.pack自身のチェックサムなので今回はいらない。削る。
      0x6f2 - 0x14 = 0x6de = 1758

      % head -c 1758 .git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack | hexdump -s 0x6c4
      00006c4 6f 8a 25 78 9c db 2e b1 8b 7d 43 1e 33 f3 e3 86
      00006d4 a6 c9 79 cc 9e 00 31 91 05 e7
      00006de
      
    • たった26バイトで差分が表現されている。

  • こちらも最初はヘッダである。

  • 6f = タイプ6(OBJ_OFS_DELTA), サイズf(15)

    • 見ての通りこのデータのサイズは15バイトではない。
    • このサイズは伸長後のサイズなので、一致しない。
    • Deflate圧縮をかけているのだが、今回の差分は単純すぎるので、逆に容量が増してしまっている。
  • OBJ_OFS_DELTAの場合、元になっているオブジェクトとこのオブジェクト自身のアドレスの差が
    続いて書かれている。: 8a 25

  • この値は、offset encodingで符号化されている。
    ドキュメントにも説明されているが、たぶんソースを見たほうがわかりやすい。

    1. 8a: 最上位ビットが1である場合、この7ビットで値が表しきれないことを示す。
    2. まずは、このバイトで表すビットを取り出す。 0x8a & 0x7f = 0xa
    3. 表しきれないのだから、ここに1を足す。 0xa + 1 = 0xb
    4. 7ビット左シフトする。 0xb << 7 = 0x580
    5. 25: 最上位ビットが0である場合、これが最後であることを示す。
    6. 0x580 | 0x25 = 0x5a5
  • この値を今見ている項目のアドレスから引く。0x6c4 - 0x5a5 = 0x11f

    • 先ほどの0x11fのオブジェクトのデータがこの差分の元になっていることを確認できた。
  • 6f及び8a 25は「これが差分であること」と「元データは0x11fにあること」を示すのに
    使われているので、差分データ本体ではない。
    続く0x6c7〜0x6deが差分を表している。

  • 差分データ本体はDeflate圧縮されている。伸長し表示してみる。

  % dd bs=1 if=.git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack skip=0x6c7 count=0x17 2>/dev/null | pigz -dc | hexdump
  0000000 b7 18 ba 07 b0 6e 03 03 e3 80 82 93 6e 03 49
  000000f

差分データの中身

  • 差分データそのものの構造は前掲のドキュメントがわかりやすい

  • 今回の差分オブジェクトで表されている差分をdiffで表すと次の通りである。

この差分に対応するdiff
diff --git a/44126f1961c05cf7c640a1da57128dac91007668 b/a4ebaf4caaf976aac558dcacfa4ec9d692cafaf3
index 44126f1..a4ebaf4 100644
--- a/44126f1961c05cf7c640a1da57128dac91007668
+++ b/a4ebaf4caaf976aac558dcacfa4ec9d692cafaf3
@@ -1,48 +1,14 @@
 GitのPackfileの中身を見てみる

 # 前提
 * 公式サイトのBookにある説明を読んでいるものとしています
     - [10.2 Gitオブジェクト](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88)
     - [10.3 Gitの参照](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%81%AE%E5%8F%82%E7%85%A7)
     - [10.4 Packfile](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Packfile)
 * この記事は公式の説明[pack-format](https://git-scm.com/docs/pack-format)を参考に
   Gitのソースコードを読んで確認した内容を示しています
 * あえていうまでもありませんが、内容は保証しません

 # Packfileの記事に書かれていること
-* Packfileにはコミット・ツリー・Blobのいずれも含まれている
+* Packfileにはコミット・ツリー・Blobのいずれも含まれている。
 * よく似たオブジェクトは差分化して保存されている
-
-# pack-formatの記事に書かれていること
-* ```pack-*.pack```には次のデータがある
-    - ```char[4]```: マジックナンバー"PACK"
-    - ```uint32_t```: バージョン
-    - ```uint32_t```: オブジェクト数
-    - 差分化されていないデータ
-        - nバイトのヘッダ: 3-bitのタイプ+{(n-1)*7 + 4}-bitのサイズ
-        - 圧縮データ
-               - 差分化されたデータ
-                   - 上記と同じようなヘッダ。ただし、タイプは```OBJ_REF_DELTA```(7)または
-                     ```OBJ_OFS_DELTA```(6)である。
-                     このタイプは基準となっているデータを指し示す方法を表す。
-                               - 圧縮された差分データ
-               - ```.pack```のチェックサム
-* 差分データの中身は次の二つ
-    - ```0b1xxxxxxx ...```: 基準となっているデータからコピーする
-        - ```0b11111111 offset1 offset2 offset3 offset4 size1 size2 size3```
-        - ```offset```、```size```はリトルエンディアン
-        - 直前の操作のあとに、直前のコピーで読み出した位置から```offset```バイト読み飛ばして
-          ```size```バイトコピーする
-               - ```0b0nnnnnnn ...```: データを追加する
-                   - 続くnバイトの値を直前の操作の後に書く
-* ```pack-*.idx```には次のデータがある(バージョン2の場合)
-    - ```char[4]```: マジックナンバー "\377tOc"
-    - ```uint32_t```: バージョン
-    - ```uint32_t[256]```: ハッシュ値の第1バイトがidx以下であるpack内のオブジェクトの数
-      (※[公式サイトの図](https://git-scm.com/docs/pack-format#_original_version_1_pack_idx_files_have_the_following_format)参照)
-    - ```uint32_t[n_obj]```: CRC32チェックサム(オブジェクトのハッシュ値昇順)
-    - ```uint32_t[n_obj]```: オフセット(```.pack```のサイズが4GB以下の場合)
-    - (```.pack```のサイズが2GB以上の場合、ここにオフセットの続きが入る)
-    - ```.pack```のチェックサム
-    - ```.idx```のチェックサム
-* ```pack-*.rev```や```MIDX```については、現物を見たことがないので略
  • オリジナルバージョンと、「。」を消しいくらか文章を追加したバージョンの差分
  • 差分の効率や最新版の読み取り性能を高めるためなどの理由で、gitはこれを追加版と追加版に対するパッチとして表したオリジナル版、 という形で保持している。

0000000 b7 18 ba 07 b0 6e 03 03 e3 80 82 93 6e 03 49
  • 最初は差分適用前後のファイルサイズが書かれている。
    • 元サイズb7 18 (0b1_0110111 0b0_0011000) = 0b0011000_0110111 = 0xc37
    • 後サイズb0 07 (0b1_0110000 0b0_0000111) = 0b0000111_0110000 = 0x3b0
  • 後はコピー命令か追記命令が並ぶ。
  • 最初はb0 6e 03
    • b0 = 0b10110000
    • 最上位ビットが1のものはコピー命令。元データの一部をコピーする。
    • これを逆順に並べてみる。0000 110 (1)
    • 最初(LSB)がオフセット量最下位バイト、2番目がオフセット量の下から2番目のバイト、 ……の存在を示すので、オフセット量が0であることが読み取れる。
      • また、5番目(第4ビット)がサイズの最下位、6番目が下から2番目、7番目が下から3番目を示して、 コピーサイズが2バイトあることも読み取れる。
    • 続く指定を読み取る。
      • 6e 03 = コピーサイズ: 0x36e
 GitのPackfileの中身を見てみる
 (略)
 # Packfileの記事に書かれていること
 * Packfileにはコミット・ツリー・Blobのいずれも含まれている
  • 次は03 e3 80 82
    • 3バイト即値で加える。
    • e3 80 82 = UTF-8で"。"
-* Packfileにはコミット・ツリー・Blobのいずれも含まれている
+* Packfileにはコミット・ツリー・Blobのいずれも含まれている。
  • 次は93 6e 03 49
    • 93 = 0b1_001_0011
    • 6e 03: オフセット = 0x036e
    • 49: サイズ = 0x49
 * よく似たオブジェクトは差分化して保存されている
-
-(略)

おまけ

  • .packの末尾20バイトは.packのそれまでの部分のチェックサムが書かれている。チェックしてみる。
    • 今回の.packのサイズは0x6f2だから、チェックサムの始点は0x6de。
    • これはファイル名(pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack)とも一致する。
  % dd if=.git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack bs=1 skip=0x6de 2>/dev/null | hexdump
  0000000 6b 39 32 9a ed 24 19 0a a8 35 8b 85 f1 0d 1c 0e
  0000010 52 84 51 d7
  % dd if=.git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.pack bs=1 count=0x6de 2>/dev/null | shasum
  6b39329aed24190aa8358b85f10d1c0e528451d7  -
  • .idxの最後から40バイト目〜20バイト目にも同じことが書かれている。 今回の.idxのサイズは0x4d8だから、0x4b0+0x14を見れば良い
  % dd if=.git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.idx bs=1 skip=0x4b0 count=0x14 2>/dev/null | hexdump
  0000000 6b 39 32 9a ed 24 19 0a a8 35 8b 85 f1 0d 1c 0e
  0000010 52 84 51 d7
  • .idxの末尾20バイトは.idxのそれまでの部分のチェックサムが書かれている。 今回の.idxのサイズは0x4d8だから、チェックサムの始点は0x4c4。
  % dd if=.git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.idx bs=1 skip=0x4c4 2>/dev/null | hexdump
  0000000 9d 10 22 6c 21 e5 7a 12 55 d3 e4 b5 08 10 0f ff
  000010 db ed 77 cb
  % dd if=.git/objects/pack/pack-6b39329aed24190aa8358b85f10d1c0e528451d7.idx bs=1 count=0x4c4 2>/dev/null | shasum
  9d10226c21e57a1255d3e4b508100fffdbed77cb  -
3
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
3
0