Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
たぶんもう怖くないGit/maybe-not-afraid-of-git-anymore
marchin
February 10, 2022
Technology
2
1k
たぶんもう怖くないGit/maybe-not-afraid-of-git-anymore
marchin
February 10, 2022
Tweet
Share
More Decks by marchin
See All by marchin
1時間半で克服するJavaScriptの非同期処理/async_javascript_kokufuku
marchin1989
2
670
自動テストでモックするって、なにそれ?おいしいの?/what_is_mocking
marchin1989
0
380
モバイルアプリで機械学習入門/introduction-to-machine-learning-in-mobile-app
marchin1989
0
120
Other Decks in Technology
See All in Technology
Sysdig Secure/Falcoの活用術! ~Kubernetes基盤の脅威モデリングとランタイムセキュリティの強化~
owlinux1000
0
310
バッファープールが大きいMySQL v5.7でDROP DATABASEが詰まった原因と対策 / Causes and Remedies for DROP DATABASE Stuck in MySQL v5.7 with Large Buffer Pool
line_developers
PRO
4
850
Amazon SageMakerが存在しない世界線のAWS上で実現する機械学習基盤
sadynitro
0
120
GCCP Creator @ COSCUP 2022
line_developers_tw
PRO
0
1.4k
PMMやプロダクト関係者と協働するために役割を整理した話 / 20220810_pdmtipslt
rakus_dev
0
130
増田亨さんによる 「設計の考え方とやり方」勉強会オープニング
tsuyok
0
220
Simplify Cloud Native Security with Trivy
knqyf263
0
730
ReverseETLでユーザーに価値を届ける基盤を実現した話
hakky
0
360
20220803投資先CXO候補者向け 会社紹介資料_合同会社BLUEPRINT
hik
0
580
ソフトウェアアーキテクチャの基礎: Software Architecture in a Nutshell
snoozer05
31
9.1k
聊聊 Cgo 的二三事
david74chou
0
340
Settlement simulation testing to ensure correct settlement processing
applepine1125
2
1.5k
Featured
See All Featured
Fashionably flexible responsive web design (full day workshop)
malarkey
396
62k
Design by the Numbers
sachag
271
17k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
29
4.4k
Typedesign – Prime Four
hannesfritz
34
1.4k
Clear Off the Table
cherdarchuk
79
290k
Fontdeck: Realign not Redesign
paulrobertlloyd
73
4.1k
5 minutes of I Can Smell Your CMS
philhawksworth
196
18k
A Modern Web Designer's Workflow
chriscoyier
689
180k
Debugging Ruby Performance
tmm1
65
10k
Keith and Marios Guide to Fast Websites
keithpitt
404
21k
Unsuck your backbone
ammeep
659
55k
Why Our Code Smells
bkeepers
PRO
324
55k
Transcript
たぶんもう怖くないGit ~Git内部の仕組み~
自己紹介 - 名前: 阿部 真之 - 仕事: 主にAndroidエンジニア - 最近はサーバサイド
Kotlinの仕事も始めました - 趣味 - コーヒー、ビール、アニメ、ゲーム、読書、 etc… - Twitter: @marchin_1989
本日はqiitaで書いた記事の内容です
アジェンダ - バージョン管理システム(VCS)について - Gitオブジェクトとリファレンス - Gitオブジェクトとリファレンスの視点でgit操作をみる
ここで話さないこと - コミット間の差分の検出方法 - gitの設定
ここで話さないこと - コミット間の差分の検出方法 - gitの設定 => gitコマンドを打って、gitプログラムの処理が終わった後の、 gitのデータの最終結果にフォーカスする
そもそもバージョン管理システム(VCS) - 集中型 - CVS, Subversion, SVN - みんなで1つのリポジトリにネットワーク接続。 -
最新のソースコードを手元に持ってきて編集。 - 各自が中央のリポジトリに変更を直接反映する。 - 変更履歴を見たい場合は、中央リポジトリにアクセスする必要がある。 - 分散型(DVCS) - Git - 各自がリポジトリを持つ。 - リモートリポジトリにネットワーク接続不要 で、 各自のリポジトリで変更、履歴の確認ができる。 - 他の人に影響を与えず、各自のリポジトリで変更を反映できる。 - 例: 各自のローカルマシン内にリポジトリがある。 - リモートリポジトリに、後からまとめて変更を反映。 自分で送りたいものだけ共有できる。 集中型 分散型
Git - オープンソースの分散型バージョン管理システム。 - リーナス・トーバルズによって開発。 - Linuxカーネルの効率的な開発のために作られた。 - gitのソースコードもgitで管理されてる。 -
2005年12月21日リリース。
よくある疑問 - どうやってGitは履歴を管理してるんだ? - よくブランチみたいな複雑そうなものうまく動いてんな。 - ブランチ is 何?枝なの? -
HEAD is 何? - detached headみたいなログが出たけど、これなに?
よくある疑問 - どうやってGitは履歴を管理してるんだ? - よくブランチみたいな複雑そうなものうまく動いてんな。 - ブランチ is 何?枝なの? -
HEAD is 何? - detached headみたいなログが出たけど、これなに? => 毎日Git使ってるのに、よくわかってない。。。
git内部の仕組みを知っていると役立つこと - gitのコマンドを間違って実行してしまった時の対処 - 例 - 作業していたブランチを消してしまった。。 - revert、reset、rebaseなど使ったけど、期待した状態にならなかった。。
git内部の仕組みを知っていると役立つこと - gitのコマンドを間違って実行してしまった時の対処 - 例 - 作業していたブランチを消してしまった。。 - revert、reset、rebaseなど使ったけど、期待した状態にならなかった。。 =>
戻したいけど、毎回なんとなくググってやってる。 合ってるのかよくわからないけど、okにしてませんか?
git内部の仕組みを知っていると役立つこと - gitのコマンドを間違って実行してしまった時の対処 - 例 - 作業していたブランチを消してしまった。。 - revert、reset、rebaseなど使ったけど、期待した状態にならなかった。。 =>
戻したいけど、毎回なんとなくググってやってる。 合ってるのかよくわからないけど、okにしてませんか? => git内部の仕組みを知ることで、gitコマンドが何をやっているのか理解しやすい
gitのデータ保存の仕組み(いきなり結論) - gitは、オブジェクトモデルでデータを保存している。 - gitオブジェクトの集まりで、単なるDAG(有向非巡回グラフ)とポストイットで履歴を管理 している。 - DAG: Directed Acyclic
Graph(有向非巡回グラフ) - https://ja.wikipedia.org/wiki/%E6%9C%89%E5%90%91%E9%9D%9E%E5%B7%A1%E5%9B %9E%E3%82%B0%E3%83%A9%E3%83%95 - コミットは差分ではなく、スナップショット。 - gitの数あるコマンドは、DAGのオブジェクトの追加、ポストイットの移動を行ってい るだけ。
キーワード - Gitオブジェクト - blob, tree, commit, tag - リファレンス
- branch, HEAD, tag
キーワード - Gitオブジェクト - blob, tree, commit, tag - リファレンス
- branch, HEAD, tag => Gitオブジェクト と リファレンスの存在を強く意識すると理解しやすいです。
ここからの説明の順番 1. Gitオブジェクトとリファレンスの説明 2. DAG(非巡回有向グラフ)全体を眺めて、gitコマンドでDAGがどうなるかまずざっくり 説明 3. さらにより詳しく、gitコマンドによって、Gitオブジェクトが作成される様子や、リファレ ンスが作成されたり、移動する様子をみる
gitオブジェクト
gitオブジェクト(objects) - すべてzlibライブラリで可逆圧縮されたもの。 - SHA-1ハッシュによって識別子がついている。 - オブジェクトは4種類。 - 「.git/objects」ディレクトリを見ると確認できる。 -
イミュータブル。一度作られたらなかなか消えない。 ※アノテーションタグ hash: e732... hash: 4g35... hash: k35g... hash: 245f...
gitオブジェクト(objects) - すべてzlibライブラリで可逆圧縮されたもの。 - SHA-1ハッシュによって識別子がついている。 - オブジェクトは4種類。 - 「.git/objects」ディレクトリを見ると確認できる。 -
イミュータブル。一度作られたらなかなか消えない。 ※アノテーションタグ hash: e732... hash: 4g35... hash: k35g... hash: 245f... リファレンスにも「tag」があ る。ややこしいので、リファレ ンスの時に一緒に見る。
gitオブジェクトは「.git/objects」以下にある
blobオブジェクト - ファイルデータ(テキストデータ、画像etc)の中身そのもの。 - ファイル名などの情報は含まれていない。
blobオブジェクト - ファイルデータ(テキストデータ、画像etc)の中身そのもの。 - ファイル名などの情報は含まれていない。 gitオブジェクトは、可逆圧縮されているので、普通 にファイルを開いても意味がない。 gitのコマンドで、gitオブジェクトの内容を表示するも のがある。 git
cat-file -p <gitオブジェクトのハッシュ値 >
blobオブジェクト - ファイルデータ(テキストデータ、画像etc)の中身そのもの。 - ファイル名などの情報は含まれていない。 この出力が、blobオブジェクトの内容。 docker-compose.ymlの中身そのもの。 この出力に「docker-compose.yml」のファ イル名の情報はどこにもない。
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。 5r8d20 また、git cat-fileコマンドで、
内容を見てみる。
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。 5r8d20 これがtreeオブジェクトの内 容。
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。 この一行に注目してみる。 5r8d20
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。 gitオブジェクトのタイプ blobもしくは、tree。 5r8d20
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。 ディレクトリ内のgitオブジェクトのハッシュ 値。 5r8d20
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。 ファイル名やディレクトリ名。 5r8d20
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。 5r8d20 0be9e3 c5ac20
93db38 Procfile README.md app この3つを、図に当てはめてみると ...
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。 5r8d20 0be9e3 c5ac20
93db38 Procfile README.md app 参照先のgitオブジェクトと、タイプ、それぞ れのファイル名(ディレクトリ名)を保持して いることわかる。
treeオブジェクト - ディレクトリを表す。 - ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。 以降出てくる「矢印」は参 照の方向です。
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト 1aaa また、git cat-fileコマンドで、 内容を見てみる。
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト 1aaa これがcommitオブジェクトの 内容。
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト 1aaa トップレベルのtreeオブジェク トを指している。 5f8d20
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト 1aaa 親commitオブジェクトのハッ シュ値を指している。 e6c06e
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト 1aaa 親がないcommitは、initial commit。 親が2つあるのは?(問題) e6c06e
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト 1aaa 親がないcommitは、initial commit。 親が2つあるのは?(問題) e6c06e マージコミット(正解)
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト 1aaa コミットしたユーザの情報。
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト 1aaa コミットメッセージ
- 以下の情報を持つ。 - トップレベルのtreeオブジェクトへの参照 - コミットしたユーザの情報 - タイムスタンプ - コミットメッセージ
- 親コミットへの参照 commitオブジェクト 1aaa コミットから辿れば、 この時点の プロジェクトを再構成できる。
つまりこういう有向非巡回グラフ
つまりこういう有向非巡回グラフ 矢印は依存の方向。 サイクルがない、向きを持ったグ ラフ。
gitオブジェクトの作り方 - ハッシュ値 - ヘッダー: オブジェクトのタイプ (blob, tree, commit) +
スペース + コンテンツのサイズ + ヌルバイト - 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。 - gitオブジェクト - 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。
gitオブジェクトの作り方 - ハッシュ値 - ヘッダー: オブジェクトのタイプ (blob, tree, commit) +
スペース + コンテンツのサイズ + ヌルバイト - 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。 - gitオブジェクト - 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。 出典: git documentation https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E 3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88 ハッシュ値、gitオブジェ クトを作ってみる。
gitオブジェクトの作り方 - ハッシュ値 - ヘッダー: オブジェクトのタイプ (blob, tree, commit) +
スペース + コンテンツのサイズ + ヌルバイト - 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。 - gitオブジェクト - 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。 出典: git documentation https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E 3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88 コンテンツ
gitオブジェクトの作り方 - ハッシュ値 - ヘッダー: オブジェクトのタイプ (blob, tree, commit) +
スペース + コンテンツのサイズ + ヌルバイト - 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。 - gitオブジェクト - 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。 出典: git documentation https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E 3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88 コンテンツ ヘッダー
gitオブジェクトの作り方 - ハッシュ値 - ヘッダー: オブジェクトのタイプ (blob, tree, commit) +
スペース + コンテンツのサイズ + ヌルバイト - 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。 - gitオブジェクト - 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。 出典: git documentation https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E 3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88 ヘッダー コンテンツ ヘッダー+コンテンツ
gitオブジェクトの作り方 - ハッシュ値 - ヘッダー: オブジェクトのタイプ (blob, tree, commit) +
スペース + コンテンツのサイズ + ヌルバイト - 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。 - gitオブジェクト - 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。 出典: git documentation https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E 3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88 ヘッダー コンテンツ ヘッダー+コンテンツ ハッシュ値
gitオブジェクトの作り方 - ハッシュ値 - ヘッダー: オブジェクトのタイプ (blob, tree, commit) +
スペース + コンテンツのサイズ + ヌルバイト - 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。 - gitオブジェクト - 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。 出典: git documentation https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E 3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88 ヘッダー コンテンツ ヘッダー+コンテンツ ハッシュ値 gitオブジェクト
gitオブジェクトの作り方 - ハッシュ値 - ヘッダー: オブジェクトのタイプ (blob, tree, commit) +
スペース + コンテンツのサイズ + ヌルバイト - 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。 - gitオブジェクト - 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。 出典: git documentation https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E 3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88 ヘッダー コンテンツ ヘッダー+コンテンツ ハッシュ値 gitオブジェクト 【重要】 「ヘッダー」と「コンテンツ」が同じ内容なら、 同じgitオブジェクト、同じハッシュ値になる。
gitオブジェクト ※アノテーションタグ hash: e732... hash: 4g35... hash: k35g... hash: 245f...
以上、gitオブジェクトでした。 タグはリファレンスで説明します。
リファレンス
リファレンス(refs) - commitオブジェクトのポインタ - commitオブジェクトのハッシュ値を指している - 簡単に指し示す先を変更できる(ポストイットみたいに) - 注意: tagはできない
- 「.git/refs」や、「.git/HEAD」で確認できる。 - 3種類
これも 「.git」以下で確認できる。 リファレンス(refs)
これも 「.git」以下で確認できる。 リファレンス(refs) branch
これも 「.git」以下で確認できる。 リファレンス(refs) HEAD
これも 「.git」以下で確認できる。 リファレンス(refs) tag
branch - コミットを指す単純なポインタ。 - remoteのブランチも一緒。
branch - コミットを指す単純なポインタ。 - remoteのブランチも一緒。 「.git/refs/」以下にある。
branch - コミットを指す単純なポインタ。 - remoteのブランチも一緒。 ローカルのブランチ
branch - コミットを指す単純なポインタ。 - remoteのブランチも一緒。 remoteのブランチ
branch - コミットを指す単純なポインタ。 - remoteのブランチも一緒。 .git/refs/heads/main ブランチの 内容を見てみる。 ※gitオブジェクトと違い、リファレ ンスは圧縮されてない。
branch - コミットを指す単純なポインタ。 - remoteのブランチも一緒。 「main」ブランチは、commitの ハッシュを指している。 1aaa
HEAD - 現在チェックアウトしている、リファレンスを指す。 - つまり、リファレンスのリファレンス。
HEAD - 現在チェックアウトしている、リファレンスを指す。 - つまり、リファレンスのリファレンス。 HEADは、 「refs/heads/main」ファイ ルを指している。
HEAD - 現在チェックアウトしている、リファレンスを指す。 - つまり、リファレンスのリファレンス。 - 直接commitオブジェクトを参照する時もある。detached HEAD 。 リファレンスではなく、
commitオブ ジェクトを直接指せる。これが detached HEAD
tag - 軽量タグ - コミットを参照するリファレンス。 2種類あるが、まず1つ目。
tag - 軽量タグ - コミットを参照するリファレンス。 「.git/refs/tags」以下にある。 commitオブジェクトのハッシュ値を 指している。 これは軽量タグ。 aee092
tag - 軽量タグ - コミットを参照するリファレンス。 - アノテートタグ - tagオブジェクト(gitオブジェクト)へのリファレンス。 アノテートタグ(リファレンス)
tag - 軽量タグ - コミットを参照するリファレンス。 - アノテートタグ - tagオブジェクト(gitオブジェクト)へのリファレンス。 -
tagオブジェクトは、commitオブジェクトへの参照を持つ。 アノテートタグ(リファレンス) tagオブジェクト(gitオブジェクト)
tag - 軽量タグ - コミットを参照するリファレンス。 - アノテートタグ - tagオブジェクト(gitオブジェクト)へのリファレンス。 -
tagオブジェクトは、commitオブジェクトへの参照を持つ。 - 軽量タグは、コミットのハッシュしか指せないが、 tagオブジェクトでアノテート(注釈)情報を保持できる アノテートタグ(リファレンス) tagオブジェクト(gitオブジェクト)
tag - 軽量タグ - コミットを参照するリファレンス。 - アノテートタグ - tagオブジェクト(gitオブジェクト)へのリファレンス。 -
tagオブジェクトは、commitオブジェクトへの参照を持つ。 - 軽量タグは、コミットのハッシュしか指せないが、 tagオブジェクトでアノテート(注釈)情報を保持できる アノテートタグ(リファレンス) tagオブジェクト(gitオブジェクト) a3da72 tagオブジェクトの内容(コンテンツ) 1aaa21
Gitオブジェクトとリファレンス - ここまでで、Gitオブジェクトとリファレンスの説明をしました
Gitオブジェクトとリファレンス - ここまでで、Gitオブジェクトとリファレンスの説明をしました - DAG(非巡回有向グラフ)全体を眺めて、gitコマンドでDAGがどうなるかまずざっくり 説明します
Gitオブジェクトとリファレンス - ここまでで、Gitオブジェクトとリファレンスの説明をしました - DAG(非巡回有向グラフ)全体を眺めて、gitコマンドでDAGがどうなるかまずざっくり 説明します - 後で、さらにより詳しく、gitコマンドによって、Gitオブジェクトが作成される様子や、リ ファレンスが作成されたり、移動する様子をみる
左の図の状況を考える。 いくつかコミットされ、ブランチなど のリファレンスがある。
HEADの現在地
別のブランチにチェック アウトしてみる
None
HEADの指すブランチが 変わる。
HEADが指すコミットの状態(スナッ プショット)に、ワーキングディレクトリ が変更される。
ヘッドを元の場所に戻した。 次に、新しくコミットしてみる。
None
新しいcommit(Gitオブジェクト) ができた。 また、mainブランチ(リファレン ス)が移動する。
HEADは元からmainブランチを 参照していたので、間接的に新 しいcommitを指すことになる。
以上、gitの内部の仕組みでした - GitオブジェクトでDAGを作り、リファレンスで参照しているだけ。 - 意外とシンプル。
gitオブジェクトとリファレンスの視点でgit操作を見る - gitコマンドによって、Gitオブジェクトが作成される様子や、リファレンスが作成され たり、移動する様子をみる。 - 確認するgitコマンド - git init -
git add - git commit - git log - git branch - git checkout - git clone - git fetch - git merge(fast forward, non fast forward) - git rebase ※コンフリクトは起きない前提です
新しいまっさらなディレクトリ gitdemoを用意。 git initする。
git管理開始
.git/objects/ 以下を確認 してみる。
初期化した直後は、 info, packディレクトリのみ
hello1.txtを作成
中身を確認。 「hello1」1行の内容。
addしてステージングす る
再度、.git/objects/ 以下を確 認。
Gitオブジェクトができる。 注: ハッシュ値は先頭2文字 がディレクトリ名になる
git cat-fileコマンドでGitオブ ジェクトの中身を確認。 これがblobオブジェクト。 hello1.txtのコンテンツの中 身だけ持っている。
DAGでは、blobのみできてい る状態。
commitしてみる。
Gitオブジェクトが増 える
b321cd のハッシュの中身を確 認。 これはcommitオブジェクト。
DAGでは、このオブジェクト。
f97e7a のハッシュの中身を確認。 これはtreeオブジェクト。
DAGでは、このオブジェクト。
commitすると、この2つのGitオブ ジェクトできた。
リファレンスは、masterとHEAD ができる。 HEADはmasterを指していて、 masterはcommitオブジェクト (b321cd)を指している。
ディレクトリを作成。
dir配下に、hello2.txtと hello3.txt を作成。
ワーキングディレクトリ の状態。
両方ともaddする。
中身を確認。 2つblobができる。
DAGはここができてい る。
コミットしてみる。
この3つのGitオブジェク トができた。
Commitオブジェクトが作成される。 masterブランチが移動し、HEADはmasterを 指しているので、同じコミットを指す。
新しくできたCommitオブ ジェクトの中身を確認。
トップレベルのtreeを参照 している。
トップレベルのtreeの中身 を確認。
子に持つtreeとblobの参照と、 ファイル名(ディレクトリ名)を 持っている。
ここで注意。 ハッシュ値が同じ。実はこの2つは同じもの で、別のtreeから参照されている。 ※図(DAG)がわかりづらくなるので、あえて 分けて書いている。 変更がないgitオブジェクトは、commitの度に コピーされるわけではない。 新しいcommitでも、同じgitオブジェクトを参 照している。 =
ここで注意。 ハッシュ値が同じ。実はこの2つは同じもの で、別のtreeから参照されている。 ※図(DAG)がわかりづらくなるので、あえて 分けて書いている。 変更がないgitオブジェクトは、commitの度に コピーされるわけではない。 新しいcommitでも、同じgitオブジェクトを参 照している。 =
gitオブジェクトは、圧縮されて いるし、中身が同じ別の gitオ ブジェクトは作成されないの で、データ量の削減にもなって いる。
git logで確認してみる。 右下のDAGの図とHEAD、masterブ ランチが一致している。
ブランチを作成する。
またgit logを実行してみると、「 foo」ブ ランチが作成されていて、 3498e2の ハッシュ値のcommitを指している。
hello1.txtを修正してみる。
もう1行hello1が追加された。
addして、commitする。
Commitオブジェクトが作成される。 masterブランチが移動。(HEADも追 従)
前のhello1.txtのコンテンツの中身を 表していたblobとは別に、新しいblob ができる。
このblobが編集されるわけではない。 (Gitオブジェクトはイミュータブル) 前のhello1.txtのコンテンツの中身を 表していたblobとは別に、新しいblob ができる。
参照していた子のblobが変更されるので、 topレベルのtreeも別に、新しいtreeオブジェ クトができる。 参照する子供のgitオブジェクトが変わると、 芋づる式に新しいgitオブジェクトができる。
ただし、子のtreeである、77ffd0のtree以 下は変更がないため同じものが参照され る。
fooブランチにチェックアウトしてみる。
HEADがfooブランチを指すようにな る。
ワーキングディレクトリは前の Commitオ ブジェクトの状態になるので、 hello1.txt が1行になっている。
以降のgitコマンドはDAGだけみていきます
git clone リモートリポジトリがある状態。
git clone 自分のローカルマシン。リモートリポジトリを cloneす る。
git clone リモートリポジトリの gitオブジェクト、 リファレンスが取得された。
git clone HEADが指すブランチのコミットの状態に、 作業ツリーが展開される
git fetch xxx xxx yyy yyy zzz リモートリポジトリの方が、 commitオブジェクトが作成さ れていて、mainブランチが進
んでいる状態。 さらにdevelopブランチが作 成されている。 git fetchしてみる。
git fetch xxx xxx yyy yyy zzz git fetchを行うと、リモートリ ポジトリのgitオブジェクトやリ
ファレンスがローカルリポジ トリに反映される。 ただし、ローカルのbranchや HEADのリファレンスは動か ない。 zzz
git merge(fast-forward) HEADはmainを指している状 態。 「git merge develop」 を実行 してみる。
git merge(fast-forward) branchリファレンス(main)が移動 するだけ。 commitオブジェクトは作成され ない。
git merge(non-fast-forward) ローカルでcommitオブジェクトを作っ たとする。 リモートにはまだプッシュしておらず、 origin/mainは一つ前のコミットを指し ている。 この状態でfetchする。(まだ状況説明 が続く)
git merge(non-fast-forward) リモートリポジトリでコミットが作られて おり、origin/mainがそのコミットを参照 している状態になった。
git merge(non-fast-forward) リモートリポジトリの新しい変更 (commitオブジェクト) を、ローカルのbranchに取り込みたい。 gitオブジェクトはイミュータブルなため、我々にできる ことは、gitオブジェクトの追加か、 リファレンスの移動しかできない。 リファレンスの移動を試そうにも、つけて貼るしかでき ないため、両方の変更を取り込めない。
git merge(non-fast-forward) リモートリポジトリの新しい変更 (commitオブジェクト) を、ローカルのbranchに取り込みたい。 gitオブジェクトはイミュータブルなため、我々にできる ことは、gitオブジェクトの追加か、 リファレンスの移動しかできない。 リファレンスの移動を試そうにも、つけて貼るしかでき ないため、両方の変更を取り込めない。
新しいcommitオブジェクトを作 成するしかない。
git merge(non-fast-forward) 「git merge origin/master」を実行する。
git merge(non-fast-forward) 新しいcommitオブジェクトが作成される。 親commitを2つ参照しているという情報が 残る。 さらに、branch(main)が移動する。 これがnon-fast-forward merge。
git rebase HEADはmainにあり、mainはこの commitオブジェクトを指している。
git rebase origin/mainは下のcommitオブジェクトを指 している。
git rebase non fast forward mergeすれば変更は取り 込める。 しかし、ステッチ(縫い目)の様な状態になっ てしまうため、やりたくない。
git rebase 「git rebase origin/main」を実行する。
git rebase 新しいcommitオブジェクトができる。 branch(main)が移動する。 gitオブジェクトはイミュータブルなので、前 のcommitオブジェクトが修正されるわけで はない。
git rebase rebaseしても前のcommitオブジェクトは 残っている。branchを移動すれば、元の状 態に戻せる。 ※gitオブジェクトは、どこからも参照されな いと、適切なタイミングで、 gitのガベージコ レクションで消される。
まとめ - gitのデータストアはgitオブジェクトの集まり。 - 単なるDAGとポストイットで履歴を管理している。 - コミットは差分ではなく、スナップショット。 - commitオブジェクトから、tree, blobと参照を辿ることで、その時点の履歴を復元し
ている。 - gitの数あるコマンドは、DAGのオブジェクトの追加、ポストイットの移動を行ってい るだけ。
参考文献 - Git for Computer Scientists - https://eagain.net/articles/git-for-computer-scientists/ - Gitの内側
- 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 - 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 - コミットはスナップショットであり差分ではない - https://github.blog/jp/2021-01-06-commits-are-snapshots-not-diffs/
ご清聴ありがとうございました!