SlideShare a Scribd company logo
実践 Git
低レベルに知る Git
本⽇のアジェンダ
⾃⼰紹介
スライドのスコープについて
リポジトリ

オブジェクトデータベース

インデックス
リファレンス

まとめ
<⾃⼰紹介>
新⽥ 洋平

2012年3⽉ 福岡オフィス⼊社
アプリケーションエンジニア
福岡に来て昨⽇でまだ⼆ヶ⽉
仲良くしてね
ソーシャルメディアとか
blog
@youhei
fb.com/youhei

git.io/youhei
Git 使⽤歴
前職までは Subversion 中⼼
前職の2つのプロジェクトで実験的に導⼊

初⼼者2名でそれほどノウハウも溜まらず

ちゃんと使い出したのは gumi に⼊ってから
</⾃⼰紹介>
みなさん Git 使ってますか?
Git ってなんかわかりにくく
      ないですか?
⾃分はわかりにくいなあと
  感じていました
はてブ, Twitter に溢れる
  初⼼者向け Tips
各地で開催される
  Git 勉強会
Git のわかりにくさを解消す
   るための様々なツール
GUI
sourcetreeapp.com
git-tower.com
CLI
git-flow
$   git   flow   init
$   git   flow   feature start branch_name
$   git   flow   feature end branch_name
$   git   flow   hotfix
$   git   flow   release
legit - Git Workflow for Humans
$   git   switch <branch>
$   git   sync
$   git   publish
$   git   unpublish
$   git   harvest
$   git   sprout
$   git   graft
$   git   branches
便利ツールは諸刃の剣
これら⾼機能なツールは Git を使いやすくしてくれますが、「本質
 的な理解」をせずとも使えてしまう諸刃の剣でもあります。
「本質的な理解」に近付くには
⾼レベルの抽象化されたコマンドやオプションを使
いこなせても Git がわかった感じがしない
対症療法的な解決になりがち

ググってコマンド打ってみる、とか

それは Git の内部を理解していないから
しかしコマンドやオプションは全て理解するには数
がとても多い
データに焦点を当てると覚えることが少なそう
と、いうことでデータ構造
の観点から Git を調べてみ
     ました
Git を実践的に使う上で⽋かせない
       ⾼レベルな機能
 分散リポジトリ

 add --patch
 merge, pull --rebase

 stash, reflog

 rebase -i による歴史の改変
これらはこのスライドのスコープ外
       です
 ⼀旦お忘れください

 (実践 Git と⾔っておきながら...)
リポジトリ
  .git
git の基本構成要素
.git は working tree 以外の情報全てをもつ




Git をボトムアップから理解するより引⽤
リポジトリの作成
$ mkdir sandbox && cd sandbox
$ git init
$ tree .git
.git
├── HEAD # 現在チェックアウトしてるブランチ
├── config
├── description
├── hooks
│    └─各種 hook スクリプト
├── info
│    └── exclude
├── objects # オブジェクトデータベース
│    ├── info
│    └── pack
└── refs # リファレンス(ブランチ, タグなど)
     └── heads
         └── tags
オブジェクトデータベース
    .git/objects
.git/objects の特徴

変更(デルタ)ではなくスナップショット
各々のコミットに対するツリー構造とデ
ータをまるごと保管する
オブジェクトの種類
オブジェクトデータベース(.git/objects)に格納されるオブジェクト


  blob

  tree
  commit
  tag (今回省略します)
blob object
データそのものを指す
  オブジェクト
オブジェクト ID は SHA-1 ハッシュ
$ echo 'Hello, World!' > greeting
$ git hash-object greeting
8ab686eafeb1f44702738c8b0f24f2567c36da6d
blob はファイル名に関係なく
           中⾝が同じならば同じ ID
$ echo 'Hello, World!' > greeting2
$ git hash-object greeting greeting2
8ab686eafeb1f44702738c8b0f24f2567c36da6d
8ab686eafeb1f44702738c8b0f24f2567c36da6d
オブジェクトはオブジェクトデータベースに
     ファイルで管理される
       先頭2桁のディレクトリ名/残り38桁のファイル名
$ git add greeting
$ tree .git
.git # (⾊々省略)
├── index # インデックス
└── objects
    └─ 8a
      └── b686eafeb1f44702738c8b0f24f2567c36da6d
$ git cat-file -t 8ab6
blob

$ git cat-file blob 8ab6
Hello, World!
オブジェクトは読み込み権限で作成される
     ⼀度作成されたオブジェクトは上書きされないことを⽰唆している
$ stat -c %A .git/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6d
-r--r--r--
同じ内容の blob を追加してもオブジェクトは置換されない
          更新時刻は変わらないので置換されていないことがわかる
 $ stat -c %y .git/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6d
 2012-04-23 09:43:12.000000000 +0900
 $ git add greeting2
 $ stat -c %y .git/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6d
 2012-04-23 09:43:12.000000000 +0900
削除してもオブジェクトデータベースから
        消えない
$ git rm --cached greeting greeting2
rm 'greeting'
rm 'greeting2'
$ git cat-file blob 8ab6
Hello, World!
blob object についてわかったこと

オブジェクトは SHA-1 ハッシュ ID で⼀意になる
blob はデータの中⾝のみで⼀意になる

オブジェクトデータベースは追記のみで更新しない

削除コマンドを実⾏しても実体は残る
いわゆるイミュータブル(不変)なオブジェクト
tree object
blob にファイル構造を
 与えるオブジェクト
tree はコミット時に作られる
$ git add greeting
$ git commit -m 'added my greeting.'
[master (root-commit) 77d0330] added my greeting.
 1 file changed, 1 insertion(+)
   create mode 100644 greeting
$ git cat-file commit HEAD # 最新コミットの中⾝をみる
tree 2da064c4206cb1e94a20a99c2cd2e19f3d193b74
author Youhei Nitta <me@youhei.jp> 1335212302 +0900
committer Youhei Nitta <me@youhei.jp> 1335212302 +0900
added my greeting.
$ git cat-file -t 2da0
tree
tree は blob や tree を保持している
$ git ls-tree 2da0
100644 blob 8ab686eafeb1f44702738c8b0f24f2567c36da6d   greeting
$ mkdir subdir && mv greeting2

$ git add subdir
$ git commit -m 'added sub directory.'
$ git ls-tree HEAD
040000 tree 18e6d2abde0f316ff62e0c8093ca8f569910bf7d   subdir
100644 blob 8ab686eafeb1f44702738c8b0f24f2567c36da6d   greeting
$ git ls-tree 18e6
100644 blob 8ab686eafeb1f44702738c8b0f24f2567c36da6d   greeting2
tree object についてわかったこと

構造や名前をもたない blob にファイルシステムとし
ての構造を与える

blob や tree を保管している

tree はコミットに保持される
commit object
ある時点でのスナップショットを
    ⽰すオブジェクト
tree のオブジェクト ID を持つ
$ git cat-file commit HEAD
tree 680e36390174676a7343ec570f9f22d0632f4444
parent 77d03308354257e850041fd2d10448fc3a2c4c8b
author Youhei Nitta <me@youhei.jp> 1335213434 +0900
committer Youhei Nitta <me@youhei.jp> 1335213434 +0900
added sub directory.
$ git ls-tree commit 680e
040000 tree 18e6d2abde0f316ff62e0c8093ca8f569910bf7d     subdir
100644 blob 8ab686eafeb1f44702738c8b0f24f2567c36da6d     greeting
親コミットのオブジェクト ID を持つ(最初のコミットを除く)
$ git cat-file commit HEAD
tree 680e36390174676a7343ec570f9f22d0632f4444
parent 77d03308354257e850041fd2d10448fc3a2c4c8b
author Youhei Nitta <me@youhei.jp> 1335213434 +0900
committer Youhei Nitta <me@youhei.jp> 1335213434 +0900
added sub directory.

$ git cat-file -t 77d0
commit
$ git cat-file commit 77d0
tree 2da064c4206cb1e94a20a99c2cd2e19f3d193b74
author Youhei Nitta <me@youhei.jp> 1335212302 +0900
committer Youhei Nitta <me@youhei.jp> 1335212302 +0900

added my greeting.
マージコミットは⼆つの親コミットを持つ
                          (少し⾼レベルに戻ります)
$   git branch fukuoka
$   git checkout fukuoka
$   echo 'Hello, Fukuoka!' > greeting2
$   git add greeting2
$   git commit -m 'added another greeting.'
$   git checkout master
$   git merge fukuoka -m 'merged fukuoka branch.'
$ git cat-file commit HEAD
tree 2fb79db8e8b4ab0f704a056754b434ff2a5435b9
parent ae3bc73b86e8ea701496122f4ea7ec5e29cab979
parent 5d8fc0a8aef3930dac4347bdfda950cec83bde8c
author Youhei Nitta <me@youhei.jp> 1335215713 +0900
committer Youhei Nitta <me@youhei.jp> 1335215713 +0900
merged fukuoka branch.
commit object についてわかったこと
必ず tree を持っている

つまりコミット時点でのツリーの
完全な状態(スナップショット)を持っている

parent を持っている
マージコミットは parent を⼆つ持っている

つまり parent を辿れば「履歴」を構成できる
Git の実体(エンティティ)は
 基本的にはこれだけです
    とてもシンプルです
残りの登場⼈物はふたつ

インデックス
リファレンス
インデックス
 .git/index
.git/index

次のコミットの準備をする場所
バイナリファイルにパス、パーミッシ
ョン、blob 値をもつ
次の HEAD になる commit を⽣成するた
めのエリア
.git/index は tree の鋳型




   Git をボトムアップから理解するより引⽤
リファレンス
  .git/refs
リファレンスとは

覚えにくいオブジェクト ID の代わりに使う別名

ローカルブランチ, リモートブランチ, タグ, HEAD は
全てリファレンス
リファレンスの種類

references
symbolic references
remote references (今回省略します)
branch は references
$ cat .git/refs/heads/master
41219f49d28b936482be893a6ca3b5a77443c78a

$ git cat-file commit master
tree 2fb79db8e8b4ab0f704a056754b434ff2a5435b9
parent ae3bc73b86e8ea701496122f4ea7ec5e29cab979
parent 5d8fc0a8aef3930dac4347bdfda950cec83bde8c
author Youhei Nitta <me@youhei.jp> 1335215713 +0900
committer Youhei Nitta <me@youhei.jp> 1335215713 +0900
Merge branch 'fukuoka'

$ git cat-file commit 41219f49d28b936482be893a6ca3b5a77443c78a
tree 2fb79db8e8b4ab0f704a056754b434ff2a5435b9
parent ae3bc73b86e8ea701496122f4ea7ec5e29cab979
parent 5d8fc0a8aef3930dac4347bdfda950cec83bde8c
author Youhei Nitta <me@youhei.jp> 1335215713 +0900
committer Youhei Nitta <me@youhei.jp> 1335215713 +0900

Merge branch 'fukuoka'
HEAD は symbolic references
                     他の references へのポインタで表現されている
$ cat .git/refs/HEAD
ref: refs/heads/master

$ git symbolic-ref HEAD
refs/heads/master

$ git cat-file commit HEAD
tree 2fb79db8e8b4ab0f704a056754b434ff2a5435b9
parent ae3bc73b86e8ea701496122f4ea7ec5e29cab979
parent 5d8fc0a8aef3930dac4347bdfda950cec83bde8c
author Youhei Nitta <me@youhei.jp> 1335215713 +0900
committer Youhei Nitta <me@youhei.jp> 1335215713 +0900
Merge branch 'fukuoka'
tag も references
$ git tag v0.0.1

$ cat .git/refs/tags/v0.0.1
41219f49d28b936482be893a6ca3b5a77443c78a

$ git cat-file commit v0.0.1
tree 2fb79db8e8b4ab0f704a056754b434ff2a5435b9
parent ae3bc73b86e8ea701496122f4ea7ec5e29cab979
parent 5d8fc0a8aef3930dac4347bdfda950cec83bde8c
author Youhei Nitta <me@youhei.jp> 1335215713 +0900
committer Youhei Nitta <me@youhei.jp> 1335215713 +0900
Merge branch 'fukuoka'

$ git cat-file commit 41219f49d28b936482be893a6ca3b5a77443c78a
tree 2fb79db8e8b4ab0f704a056754b434ff2a5435b9
parent ae3bc73b86e8ea701496122f4ea7ec5e29cab979
parent 5d8fc0a8aef3930dac4347bdfda950cec83bde8c
author Youhei Nitta <me@youhei.jp> 1335215713 +0900
committer Youhei Nitta <me@youhei.jp> 1335215713 +0900

Merge branch 'fukuoka'
tag と branch の違い
実はほとんど同じ

branch は commit に合わせて変動する
逆に tag は変動しない branch といえる
以上が、Git を構成する要素の(ほぼ)全てです
リポジトリ

オブジェクトデータベース
インデックス

リファレンス
ここまでの低レベルな観点だけで、
        下記の⾼レベルな視点に
      ⾃分なりの回答ができるはずです
いくら分散しても⽭盾が発⽣しない理由
ブランチの切り替えが⾼速な理由
reflog で過去のどの時点にも戻れる理由
差分はどこから取り出しいつ計算するか推測
SHA-1 ハッシュ値が衝突時に Git はどう振る舞うか推測
おまけ
       SHA-1 ハッシュ値の衝突について⼩噺

SHA-1 の衝突を⾒るにはどうしたらいいのか、ひとつの例をごらんに⼊
れましょう。
地球上の⼈類 65 億⼈が全員プログラムを書いていたとします。そして
その全員が、Linux カーネルのこれまでの開発履歴 (100 万の Git オブジ
ェクト) と同等のコードを⼀秒で書き上げ、⾺⿅でかい単⼀の Git リポジ
トリにプッシュしていくとします。これを五年間続けたとして、SHA-1
オブジェクトの衝突がひとつでも発⽣する可能性がやっと 50% になり
ます。
それよりも「あなたの所属する開発チームの全メンバーが、同じ夜にそ
れぞれまったく無関係の事件で全員オオカミに殺されてしまう」可能性
のほうがよっぽど⾼いことでしょう。

             ProGit 6.1 リビジョンの選択 より
まとめ
git のデータ構造はとてもシンプル
低レベルな視点を持つと⾼レベルな機能
・問題は⾃然とわかるようになる
参考資料(掘り下げたい⼈向け)
Git をボトムアップから理解する
ProGit 9. Git の内側
Wrangling Git
⼊⾨ Git 2章 git の基本概念
Git によるバージョン管理 13章 Git リポジトリの中⾝を⾒る
gitcore-tutorial(7)
git(1): LOW-LEVEL COMMANDS
We are hiring!!
では低レベルな技術者を募集しています!
質疑応答

More Related Content

実践 Git - 低レベルに知る Git