はじめに
本記事では、Gitの基本的な操作である「コミット」について、コマンドの裏側で何が起きているのかをイメージ図を用いて解説します。
-
対象読者
-
addやcommitを「おまじない」だと思っている人
-
-
この記事のゴール
- コミットしたときに
.gitフォルダの中で何が起きているか、図解でイメージできるようになること
- コミットしたときに
-
この記事でわかること
- コミットがBlob, Tree, Commitという3つのデータで作られる仕組み
そもそもGitって?
Gitとは、バージョン管理ツールです。
ソフトウェア開発では、頻繁にファイル変更が行われます。
個人開発でも「いつ どんな変更 を行ったのか」管理することは重要ですが、特にチームでの開発となると、
- 誰が
- いつ
- どのファイルに対して
- どのような
変更を行ったのかについて管理しておかないと、バグが発生したときや仕様変更を行う際の修正作業にたいへんな手間が発生します。
Gitはファイルの変更履歴を記録・追跡し、複数人での共同作業を効率化してくれる、いと素晴らしきバージョン管理ツールなのです。
概要
リポジトリ
GitおよびGitHubにおいて、変更履歴はそれぞれの リポジトリ で保存されます。
リポジトリとは、コードやファイル、各ファイルの変更履歴を保存する場所のことです。
リポジトリには ローカルリポジトリ と リモートリポジトリ があります。
- ローカルリポジトリ :自分のPC上にあるリポジトリ
- リモートリポジトリ :GitHubなどのサーバー上に配置された、みんなで共有するリポジトリ
ローカルリポジトリの構成
ローカルリポジトリは以下の3つのエリアに分かれます。
- リポジトリ
- ローカルで変更履歴を保存するための場所
- ステージングエリア
- 編集したファイルを一時的に保存しておくためのスペース
- ワークツリーでの編集は、一度ここを通してからリポジトリに保存される
- ワークツリー
- 作業スペース
- VSCodeなどで編集している部分
ローカルリポジトリでの作業は、これらのエリアを移動しながら進めます。
データの流れ:コミットの裏側で起きていること
Gitの裏側では、ファイルやディレクトリの情報が**「オブジェクト」**という単位で保存されています。
ワークスペースでtest.txtファイルを作成し、git addでステージングしてからgit commitでリポジトリに保存されるまでの流れを、このオブジェクトに注目して追っていきましょう。
1. ワークツリーでファイルが変更される
- ワークスペース上に
test.txtを作成し、内容を記述します
$ git add test.txt
2. リポジトリに圧縮(Blob)ファイルが作成される
-
git addを実行されたとき、Gitはtest.txtの中身をスキャンします - スキャンした内容を圧縮し、「Blobオブジェクト」と呼ばれるファイルを作成します
.
├── .git
│ ├── hooks
│ ├── info
│ ├── objects
│ │ ├── 30
│ │ │ └── d74d258442c7c65512eafab474568dd706c430 ←作成されたBlobオブジェクト
│ │ ├── info
│ │ └── pack
│ ├── refs
│ ├── config
│ ├── description
│ ├── HEAD
│ └── index
└── test.txt
3. インデックスファイルが更新される
- Blobオブジェクト単体では、それが何のファイルを圧縮した内容なのか分かりません
- そのため、Blobオブジェクトと
test.txtの関係を記録する必要があります - この記録が、
indexファイルに行われます
$ git commit -m "<コメント>"
4. ツリーファイルが作成される
-
indexファイルの内容がそのまま書き込まれた、「treeオブジェクト」が作成されます - Blobオブジェクトと元となるファイルの紐付けの情報が記録されます
-
indexファイルは毎回git addのたびに上書きされますが、treeオブジェクトはgit commitの度に、一つのスナップショットとして保存されます- イメージとしては、
-
index→「現在の作業の書きかけメモ」 -
tree→「ある瞬間でのディレクトリ構造の完成図」
-
- といった違いがあります
- イメージとしては、
.
├── .git
│ ├── hooks
│ ├── info
│ ├── logs
│ ├── objects
│ │ ├── 09
│ │ │ └── 5a057d4a651ec412d06b59e32e9b02871592d5 ←作成されたtreeオブジェクト
│ │ ├── 30
│ │ │ └── d74d258442c7c65512eafab474568dd706c430 ←Blobオブジェクト
│ │ ├── info
│ │ └── pack
│ ├── refs
│ ├── COMMIT_EDITMSG ←コミットコメントが記述される
│ ├── config
│ ├── description
│ ├── HEAD
│ └── index
└── test.txt
5. コミットファイルが作成される
- treeオブジェクトが作成された後、「commitオブジェクト」が作成されます
- commitオブジェクトには以下の内容が記録されます
- treeオブジェクトのファイル名
- 親commitのID(前のcommitはどれか)
- commitしたユーザーの情報
- 作成者
- 日付
- commitメッセージ
.
├── .git
│ ├── hooks
│ ├── info
│ ├── logs
│ ├── objects
│ │ ├── 09
│ │ │ └── 5a057d4a651ec412d06b59e32e9b02871592d5 ←作成されたCommitオブジェクト
│ │ ├── 0f
│ │ │ └── e415b5dfb46a619bd805c8bf1f8601aa8f775e ←treeオブジェクト
│ │ ├── 30
│ │ │ └── d74d258442c7c65512eafab474568dd706c430 ←Blobオブジェクト
│ │ ├── info
│ │ └── pack
│ ├── refs
│ ├── COMMIT_EDITMSG ←コミットコメントが記述される
│ ├── config
│ ├── description
│ ├── HEAD
│ └── index
└── test.txt
まとめ
git add
- リポジトリに圧縮(Blob)ファイルが作成される
- インデックスファイルが更新される
git commit -m "コメント"
3. ツリーファイルが作成される
4. コミットファイルが作成される
という流れで、Gitに変更履歴が保存されます。
上記の通り、Gitによる変更履歴の保存はスナップショットで行われます。
そのため、差分で保存しているバージョン管理ツールと比較して、素早く過去の状態に戻すことができるのです。

