Version Control with Git ch4 Basic Git Concepts (2/2)

Git勉強メモ

出典: 


Git Concepts at Work

  • 実際にGitリポジトリを作って内部表現を詳細に見てみる

Inside the .git Diretory

  • .gitディレクトリにはいろいろ入ってる
git init
find .
.
./.git
./.git/HEAD
./.git/branches
./.git/config
./.git/description
./.git/hooks
./.git/hooks/fsmonitor-watchman.sample
./.git/hooks/update.sample
./.git/hooks/pre-applypatch.sample
./.git/hooks/pre-push.sample
./.git/hooks/pre-receive.sample
./.git/hooks/applypatch-msg.sample
./.git/hooks/pre-commit.sample
./.git/hooks/prepare-commit-msg.sample
./.git/hooks/commit-msg.sample
./.git/hooks/post-update.sample
./.git/hooks/pre-rebase.sample
./.git/objects
./.git/objects/pack
./.git/objects/info
./.git/info
./.git/info/exclude
./.git/refs
./.git/refs/tags
./.git/refs/heads
  • 中身はGitのバージョン依存

      • .git/hooks/*.sample.sampleサフィックスは新しいバージョンでは無くなっていたりするらしい
      • git version 2.17.1ではまだあるみたい
  • オブジェクトストアははじめ空っぽ
find .git/objects/
.git/objects/
.git/objects/pack
.git/objects/info
  • オブジェクト追加してみる
echo "hello world" > hello.txt
git add hello.txt
  • 【補】addするだけでオブジェクトストアに格納される
  • 完全に同じファイル内容ならばSHA1値も同じ
find .git/objects
.git/objects
.git/objects/3b
.git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad
.git/objects/pack
.git/objects/info
  • これ 3b18e512dba79e4c8300dd08aeb37f8e728b8dad

    • 書籍と一致

Objects, Hashes, and Blobs

  • SHA1ハッシュ(160bits)の40桁16進数表記の名前でオブジェクトを保存

    • 効率のために頭2桁=先頭1バイトでディレクトリを切っている

      • 同一のディレクトリにファイルを大量に置きすぎると遅くなるファイルシステムもある
    • 256通りの一様分布パーティショニング
  • オブジェクトの読み込み: git cat-fileサブコマンド
git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
hello world
  • 40桁全部手入力するのは酷
  • 前方一致で特定できればそれをルックアップしてくれる
git rev-parse 3b18
3b18e512dba79e4c8300dd08aeb37f8e728b8dad
  • 3文字ではダメ
git rev-parse 3b1
fatal: ambiguous argument '3b1': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
~/learn/git/sandbox $ 

How Do We Know a SHA1 Hash Is Unique?

  • ぶつかる可能性は0ではないが、実用上心配する必要はない
  • SHA1は暗号学的ハッシュ関数

    • 故意に衝突をおこす方法は見つかっていない
  • 偶然おこることは?

    • 2^80個くらいオブジェクトを作れば衝突確率は有意になってくる
  • Bruce Schneierを読め

Files and Trees

  • blobがオブジェクトストアのなかに安置されていることはわかった
  • ファイル名はどこ?
  • treeで追跡している
  • git add,git rm, git mvではまだtreeオブジェクトはできない

    • .git/indexが更新される

      • パス名と、blobとの紐付けを追跡
git ls-files -s
100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0	hello.txt
  • tree書き出し
git write-tree
68aba62e560c0ebc3396e8ae9335232cd93a3f60
  • オブジェクトが増えていることを確認する
find .git/objects
  .git/objects
  .git/objects/3b
  .git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad
  .git/objects/pack
  .git/objects/info
+ .git/objects/68
+ .git/objects/68/aba62e560c0ebc3396e8ae9335232cd93a3f60
  • 68aba6...がtree
  • blobと同じオブジェクトなので、同じ低レベルコマンドで中身を見れる
git cat-file -p 68aba6
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad	hello.txt
  • わかりやすい

    • ファイル属性(8進数)
    • 参照しているオブジェクトの種類
    • 参照しているオブジェクトのSHA1
    • blobに紐付いているファイル名

A Note on Git’s Use of SHA1

  • 再現性

    • 同じtreeをいくつも再生成するような無駄なことはしない
git write-tree
68aba62e560c0ebc3396e8ae9335232cd93a3f60
git write-tree
68aba62e560c0ebc3396e8ae9335232cd93a3f60
git write-tree
68aba62e560c0ebc3396e8ae9335232cd93a3f60
  • ハッシュ関数は純粋関数

    • 誰がいつどこでどのように生成しても同じ値
  • 衝突はせず、オブジェクトと1対1とみなせる

    • 同じハッシュ値のオブジェクトがあれば、それは同じ内容のオブジェクト
    • 同じハッシュ値のオブジェクトがなければ、同じ内容のオブジェクトはない
  • commitは、それ以下の全データ構造の状態を一意に特定する

    • commitは親commitとtreeを参照する
    • 再帰的

Tree Hierarchies

  • サブディレクトリを切ってみる
mkdir subdir
cp hello.txt subdir/
git add subdir/hello.txt 
git write-tree
  • サブディレクトリが追加されたので、トップレベルのtreeのSHA1値は変わる
492413269336d21fac079d4a4672e55d5d2147ac
  • トップレベルのtreeにはサブディレクトリに対応するtreeが追加される
git cat-file -p 492413269
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad	hello.txt
040000 tree 68aba62e560c0ebc3396e8ae9335232cd93a3f60	subdir
  • 見覚えのあるSHA1値68aba6...

    • サブディレクトリsubdirは、さっきまでのトップレベルのtreeと同じ構造なので、同じSHA1値になる
  • オブジェクト一覧
find .git/objects
  .git/objects
+ .git/objects/49
+ .git/objects/49/2413269336d21fac079d4a4672e55d5d2147ac
  .git/objects/3b
  .git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad
  .git/objects/pack
  .git/objects/info
  .git/objects/68
  .git/objects/68/aba62e560c0ebc3396e8ae9335232cd93a3f60
  • ユニークなオブジェクトは3つ

    • “hello world”なるblob 3b18e5
    • “hello world”なる内容のファイルhello.txtを指すtree 68aba6
    • hello.txtとtree 68aba6を指すtree 492413

Commits

object (低レベル)コマンド
blob git add
tree git write-tree
commit git commit-tree
  • 低レベルコマンドによるcommitの生成
echo -n "Commit a file that says hello\n" \
| git commit-tree 4924132
  • commitが指すtreeを指定し、コミットメッセージを流し込む
ef7baf4adb968812217b4e81b58577ca51bedb71
  • 完全に同一のコミットにはならないので、SHA1値は毎回変わる

    • author
    • committer
    • 時刻
  • 指しているtreeは完全に同一

    • commitオブジェクトとtreeオブジェクトとが分離されている理由
    • 同一のtreeを複数の異なるcommitが指したりする

      • 【補】merge時に--no-ffでわざとmerge commitを刻んだりするやつ
  • commitオブジェクトの確認
git cat-file -p ef7baf4a
tree 492413269336d21fac079d4a4672e55d5d2147ac
author Daiki Horiyama <d19921207@gmail.com> 1572018647 +0900
committer Daiki Horiyama <d19921207@gmail.com> 1572018647 +0900

Commit a file that says hello\n
  • 実生活では上記低レベルコマンドは使わずにgit commitする
  • commitはオブジェクトだが、treeとは完全に異なる構造

    • tree: 木
    • commit: 一般には木ではないグラフ

      • 複数の親commitを持つこともできる

Tags

  • 2種類ある

    • lightweight
    • annotated
  • lightweight

    • 単にcommitを参照する
    • リポジトリプライベート

      • オブジェクトストアには永続化されない
  • annotated

    • オブジェクトストアに永続化される
  • commitに名前をつけるうえでは同じように扱える
  • が、基本的にはGitコマンドはannotated tagにしかはたらかない
git tag -m "Tag version 1.0" V1.0 ef7baf
git rev-parse V1.0
  • tag objectができている
b347bc62dcc80376c4a926d9dba5ebe11e013510
git cat-file -p b347bc
object ef7baf4adb968812217b4e81b58577ca51bedb71
type commit
tag V1.0
tagger Daiki Horiyama <xxxxxxxxx@gmail.com> 1572019505 +0900

Tag version 1.0
  • 格納される情報

    • ログメッセージ(-mオプションで指定したもの)
    • tagger
    • GPG署名(今回は指定していない)
    • 参照するcommitのSHA1値(ef7baf)
  • タグはcommitを指し、commitはtreeを指すので、tree以下の全ファイルに適用される

    • CVSとは異なるところ

      • CVSでは個々のファイルにタグ付けを行う

英語

  • chancy

    • 不確か

      • 手入力すると間違うよ、という文脈
  • bank on

    • 頼りにする
  • ensconce

    • 安置する
  • encompass

    • 包含する