たぶんもう怖くないGit~Git内部の仕組み~
View Slide
自己紹介- 名前: 阿部 真之- 仕事: 主に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
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オブジェクトへの参照を持っている。5r8d200be9e3 c5ac20 93db38Procfile README.md appこの3つを、図に当てはめてみると ...
treeオブジェクト- ディレクトリを表す。- ファイル名、blobオブジェクト or 別のtreeオブジェクトへの参照を持っている。5r8d200be9e3 c5ac20 93db38Procfile 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は、initialcommit。親が2つあるのは?(問題)e6c06e
- 以下の情報を持つ。- トップレベルのtreeオブジェクトへの参照- コミットしたユーザの情報- タイムスタンプ- コミットメッセージ- 親コミットへの参照commitオブジェクト1aaa親がないcommitは、initialcommit。親が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 documentationhttps://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オブジェクトを作ってみる。
gitオブジェクトの作り方- ハッシュ値- ヘッダー: オブジェクトのタイプ (blob, tree, commit) + スペース + コンテンツのサイズ + ヌルバイト- 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。- gitオブジェクト- 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。出典: git documentationhttps://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オブジェクトの作り方- ハッシュ値- ヘッダー: オブジェクトのタイプ (blob, tree, commit) + スペース + コンテンツのサイズ + ヌルバイト- 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。- gitオブジェクト- 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。出典: git documentationhttps://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オブジェクトの作り方- ハッシュ値- ヘッダー: オブジェクトのタイプ (blob, tree, commit) + スペース + コンテンツのサイズ + ヌルバイト- 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。- gitオブジェクト- 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。出典: git documentationhttps://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オブジェクトの作り方- ハッシュ値- ヘッダー: オブジェクトのタイプ (blob, tree, commit) + スペース + コンテンツのサイズ + ヌルバイト- 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。- gitオブジェクト- 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。出典: git documentationhttps://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オブジェクトの作り方- ハッシュ値- ヘッダー: オブジェクトのタイプ (blob, tree, commit) + スペース + コンテンツのサイズ + ヌルバイト- 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。- gitオブジェクト- 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。出典: git documentationhttps://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オブジェクト
gitオブジェクトの作り方- ハッシュ値- ヘッダー: オブジェクトのタイプ (blob, tree, commit) + スペース + コンテンツのサイズ + ヌルバイト- 「ヘッダー」と「コンテンツ」を連結、 sha1 hashをとったもの。- gitオブジェクト- 「ヘッダー」と「コンテンツ」を連結、 zlibで可逆圧縮したもの。出典: git documentationhttps://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オブジェクト【重要】「ヘッダー」と「コンテンツ」が同じ内容なら、同じ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オブジェクト)a3da72tagオブジェクトの内容(コンテンツ)1aaa21
Gitオブジェクトとリファレンス- ここまでで、Gitオブジェクトとリファレンスの説明をしました
Gitオブジェクトとリファレンス- ここまでで、Gitオブジェクトとリファレンスの説明をしました- DAG(非巡回有向グラフ)全体を眺めて、gitコマンドでDAGがどうなるかまずざっくり説明します
Gitオブジェクトとリファレンス- ここまでで、Gitオブジェクトとリファレンスの説明をしました- DAG(非巡回有向グラフ)全体を眺めて、gitコマンドでDAGがどうなるかまずざっくり説明します- 後で、さらにより詳しく、gitコマンドによって、Gitオブジェクトが作成される様子や、リファレンスが作成されたり、移動する様子をみる
左の図の状況を考える。いくつかコミットされ、ブランチなどのリファレンスがある。
HEADの現在地
別のブランチにチェックアウトしてみる
HEADの指すブランチが変わる。
HEADが指すコミットの状態(スナップショット)に、ワーキングディレクトリが変更される。
ヘッドを元の場所に戻した。次に、新しくコミットしてみる。
新しい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オブジェクト。
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 cloneHEADが指すブランチのコミットの状態に、作業ツリーが展開される
git fetchxxxxxxyyyyyyzzzリモートリポジトリの方が、commitオブジェクトが作成されていて、mainブランチが進んでいる状態。さらにdevelopブランチが作成されている。git fetchしてみる。
git fetchxxxxxxyyyyyyzzzgit 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 rebaseHEADはmainにあり、mainはこのcommitオブジェクトを指している。
git rebaseorigin/mainは下のcommitオブジェクトを指している。
git rebasenon fast forward mergeすれば変更は取り込める。しかし、ステッチ(縫い目)の様な状態になってしまうため、やりたくない。
git rebase「git rebase origin/main」を実行する。
git rebase新しいcommitオブジェクトができる。branch(main)が移動する。gitオブジェクトはイミュータブルなので、前のcommitオブジェクトが修正されるわけではない。
git rebaserebaseしても前の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/
ご清聴ありがとうございました!