gitはどうやって動いている?
エンジニアとして活動してはや数年、今になってなんか急に気になったから調べてみた備忘録。
誤情報等あればコメントでご指摘ください✊
キーワードとして大事なのはとりあえず以下っぽいということがわかった。
- Gitは差分管理方法としてまるごと前後の2ファイルを保持して差分を取得しているわけではなく、スナップショットという仕組みを使ってファイルの差分を保持している。
- 各コミットを識別するためのID算出にハッシュを使って重複を回避(コミットID)している。
- オブジェクト として必要な情報を保持している。
それぞれ一つずつ見ていこう。
① スナップショットという仕組みについて
スナップショットには2種類存在する。
- 2ファイルをまるごと保持する方法
- ある地点でのファイルをまるごと保持していく方法。バックアップと異なるのはバックアップは定期的に行われるのに対し、スナップショットは保存するタイミングを自分で決められる。
- 2ファイルの差分を管理していく方法
- 一つのファイルに対してある瞬間と今の瞬間を比較してその変更・関連情報を保持するという仕組みである。
ファイルをまるごと保存するわけではないので、保持するデータ量が少なくて済む。
- 一つのファイルに対してある瞬間と今の瞬間を比較してその変更・関連情報を保持するという仕組みである。
Gitは前者「2ファイルをまるごと保持する方法」である。
一見効率が悪そうに見えるが、Gitはファイルまるごとのデータとは別にどのファイルに変更があったのかという、ディレクトリ構成のようなデータも一緒に情報として保持している(後述の「treeオブジェクト」で管理するファイルのハッシュ情報)。これにより、変更のあったファイルだけを簡単に見つけ出している。
このGit特有のスナップショット技術こそがロジックの核といえる。
② ハッシュによるコミットID算出
ご存知の通り、Gitの最大の利点の一つとして、ある地点までのロールバックが可能であるということは皆さんご存知だろう。
以下のコマンドなどである地点まで戻すということができる。
git reset <コミットID>
当たり前だが、差分の各地点を指定するには識別するための一意なIDが必要である。
このコミットIDに関して、お気づきかも知れないが、ハッシュによって割り振られており、SHA-1のアルゴリズムで決定されている。
パターン数
コミットIDは40文字かなり、1~9(10),a~z(26)で構成されるため、
パターン数は
36^40
= 1.7868991024601705453143247728944e+62
178那由多6899阿僧祇1024恒河沙6017極545載3143正3247澗7289溝4400穣 パターンある
SHA-1アルゴリズム
入力として与えられた情報から特定の規則に従いハッシュ値を生成するアルゴリズムである。
Gitでは以下の情報を入力値として与えて、コミットIDを生成している。
- ディレクトリ構成のハッシュ値
- 直前のコミットID
- Author名
余談:ハッシュなんだから重複の怖くない?
実際起こることがあるっぽい。ブランチもコミットした人も全く同じだとやばいみたいで `git reset` ができなってしまうみたい。超稀だが
起きてしまうと、すでにあったコミットが優先されるらしい。これ起きたらどう回避するんだろう。
この問題回避もあり、最新のGitではSHA-256(256ビット)が採用されている
③ オブジェクトとしての情報管理
オブジェクトとして差分の管理に必要な情報をそれぞれ管理している。
ディレクトリ階層的には以下で見れる。
.git
└── objects # Gitが監視・管理するファイルやディレクトリの情報
└── 00
└── ...
オブジェクトの種類は3種類でそれぞれの役割は以下の通り
- blobオブジェクト(ファイルの圧縮・共有版とスナップショットの管理。詳しくは後述)
- treeオブジェクト(ディレクトリ構成とスナップショットの管理。)
- commitオブジェクト(コミット関連の情報とスナップショットを持つtreeへのポインタ情報。スナップショットやコミッター情報)
また、オブジェクトのファイル名はコミットIDと結びつく。
余談:ちなみになぜディレクトリが分かれているのか
object配下を見ると、ディレクトリが分かれているがなぜ分かれているのか?理由はファイルシステムにあるらしく、一つのディレクトリに格納できるファイルの数に数千程度の制限があるからだとか。大量のコミットがあったらこれに引っかかるらしく、生成されたコミットID40桁のうち先頭2桁をディレクトリ名、残り38桁をファイル名に使って回避しているらしい(賢いね)
おまけ:.git配下を観察してみる
.git配下を見てみるとこんな感じになっていた。知らない機能がいくつかあったのでまとめた
.git
└── hooks # Gitの各コマンドを実行した時に呼び出すことができる自作スクリプト置き場
└── info # 自分専用のGit追加情報定義箇所
| └── exclude # .gitignore相当。自分環境専用
└── logs # これまでの様々なログ。
└── objects # Gitが監視・管理するファイルやディレクトリの情報
| └── 00
| └── ...
└── refs # 最新コミットIDの管理
- hooksで自作スクリプトを実行できるの知らんかった。
- excludeもわりと便利そう。この仕組造っている間だけこのファイルの更新やめときたいなってときに使えそう。
参考・引用・協力
- Git
- SHA-1 - Wikipedia
- 5分でわかるSSL通信 SHA-1、SHA-2の違いとは?|ITフリーランスをサポートする【geechs job(ギークスジョブ)】
- SHA-1とは - ITを分かりやすく解説
- 【Git】Gitの仕組みを理解する【概念を図解でわかり易く】 | tetoblog
- 難しいGitコマンドは、仕組みから理解してみよう - Qiita
- 【読むだけ】Gitとは?仕組みを初心者にもわかりやすく解説【図解】 | キツネの惑星
- Gitの基本的な仕組みを知ろう|もう怖くないGit!チーム開発で必要なGitを完全マスター|Techpit
- 目指せ!脱初心者 Gitの基本を図解で解説 | 仙台を代表するホームページ制作会社|AndHA(アンドエイチエー)
- chatGPT
変更履歴
- Gitの差分管理に関する誤認識指摘を反映/ Thanks: @jun1s 様