インフラエンジニアの為のGit入門
IaCが台頭した昨今、インフラエンジニアにおいても、コード管理の重要性は高まっていると考えられます。
今回は、インフラエンジニアに向けた入門編としてコード管理のツールとしてデファクトスタンダードとなっている「Git」についてご紹介します。業務の価値提供に直接関わらないという理由で敬遠しがちですが、本記事を参考にしていただければ幸いです。
Gitとは?
「プログラムのソースコードなどの変更履歴を記録・追跡するための分散型バージョン管理システム」であるとWikipediaに記載されています。
「git」というコマンドがある以上、当然、Git自体も誰かが開発したソフトウェアになります。
歴史としては、初版が2005年12月21日にリリースされ、開発者は、Linux Kernelを作ったLinus Torvaldsなど多数が関わっています。
Git自体のコードは現在GitHubで管理されているようで、最古のCommitは2008年7月23日であり、2024年10月30日時点で、75,179のCommitが記録されていました。
Commit HistoryのCommit Authorから、GitのコードはGitHub社やGitLab社、複数の個人開発者によって管理されていることが読み取れます。
Gitのインストール方法
Gitがコード管理のデファクトスタンダードとなった今、ほとんどのOSにはGitが標準でインストールされています。
ですので、今さらGitのインストール方法を把握する必要はないかもしれませんが、あえて紹介するとすれば、公式のインストール手順になります。
案内によると、
Normally you can just do “make” followed by “make install”, and that
will install the git programs in your own ~/bin/ directory.
つまり、make installをするだけで「~/bin/ディレクトリ」が作成され、gitコマンドが実行できるようになるようです。
Gitの概念と仕組み
3つのエリアとその仕組み
論理的には、ワーキングツリー、ステージングエリア(インデックス)、ローカルリポジトリという3つのエリアに対して操作を行うことになります。
それぞれ、add、commitという操作を行うことでファイルを変更した情報が先のエリアに反映されていきます。
なお、ローカルリポジトリには概念として、「コミット」と呼ばれるものが作成されます。
この際のファイルシステム上の動きとしては、以下のようになります。
①ワーキングツリー上のファイルを更新した
→.gitディレクトリ配下では何も起こらない
②addした
→.git/objectsディレクトリ配下 にファイルのスナップショット情報が保持される(これはBlobオブジェクトと呼ばれる)
→.git/index が現在のステージングエリアの状態に合わせて更新される(indexファイルには、ファイル名、パーミッション、ファイルの内容のハッシュ値の一覧が記載されている)
③commitした
→.git/objectsディレクトリ配下 にディレクトリ構造の情報が保持される(これはTreeオブジェクトと呼ばれ、indexファイルと類似の内容が記載されている)
→.git/objectsディレクトリ配下 にコミット履歴情報が保持される(これはCommitオブジェクトと呼ばれ、Treeオブジェクトのハッシュ値、親のCommitオブジェクトのハッシュ値、コミットした人の情報が記載されている)
ブランチの概念とその仕組み
異なる目的の開発に影響を与えず、目的ごとに履歴を管理するためにブランチという概念があります。
ブランチを切り替えることで、切り替え前のブランチとは異なるコミット履歴の流れを作ることができます。
なお、ブランチごとにコミット履歴情報を持っているのではなく、各ブランチが共通のコミット履歴情報を使用し、ブランチはあくまで履歴のうちどこに居るかを指し示すポインタであることが重要です。
また、HEADというリポジトリ内に1つだけ存在するポインタがあり、それが切り替わったブランチを指し示します。
ファイルシステム上の動きとしては、以下のようになります。
①ブランチを作成した
.git/refs/heads/ブランチ名 ファイルが生成され、直近のコミットIDが記載される
②ブランチを切り替えた
→.git/HEAD に切り替えたブランチ名が記載される
→.git/index が切り替えたブランチのコミット状態に合わせて更新される(切り替え前と切り替え後で全く同じ状態だったとしても、indexファイルは同じ内容で更新される)
→ワーキングツリー上のファイルが切り替えたブランチのコミット状態に合わせて更新される
ブランチを削除した
→.git/refs/heads/ブランチ名 のファイルが削除される
ブランチの統合(マージ)
ブランチを利用して、異なる目的のコードを分けて管理するのもいいですが、それらをいずれは統合しないと、アプリケーションのデプロイ時にどのブランチのコードを使用したら良いか分からなくなります。
それぞれの目的の開発が完了し、その管理を統合する操作をマージと呼びます。
なお、マージには以下の3パターンがあります。
- 枝分かれしたブランチを統合する通常のマージ(統合後の新しいコミットが作られる)
- 枝分かれはしていないが、ブランチのポインタを進めるファストフォワード(ポインタの位置が変わるだけなので、コミットは作られない)
- 実際には枝分かれしていたが、そうではなかったように見せるリベース(統合元のコミットを削除し、あたかもそれを統合したように見せる新しいコミットが作られる)
ファイルシステム上の動きとしては、以下のようになります。
①新しいコミットが作られる通常のマージを行った
→.git/refs/heads/ブランチ名 が新しく作られたコミットIDで更新される
→.git/ORIG_HEAD がマージする前のコミットID(マージする前の.git/refs/heads/ブランチ名の値)で更新される
→.git/objectsディレクトリ配下 に新しく作られたコミットのディレクトリ構造の情報、コミット履歴情報が生成される
→.git/index が新しく作られたコミットの状態に合わせて更新される
→ワーキングツリー上のファイルが新しく作られたコミットの状態に合わせて更新される
ブランチのポインタを進めるファストフォワードのマージを行った
→.git/refs/heads/ブランチ名 がポインタを進めた先のコミットIDで更新される
→.git/ORIG_HEAD がポインタを進めた先のコミットIDで更新される
→.git/index がポインタを進めた先のコミット状態に合わせて更新される
→ワーキングツリー上のファイルがポインタを進めた先のコミット状態に合わせて更新される
統合前のコミットを削除して、新たなコミットを生成するリベースを行った
→.git/refs/heads/ブランチ名 がポインタを進めた先のコミットIDで更新される
→.git/ORIG_HEAD がリベース前のコミットID(リベース前の.git/refs/heads/ブランチ名の値)で更新される
→.git/objectsディレクトリ配下 に新しく作られたコミットのディレクトリ構造の情報、コミット履歴情報が生成される
→.git/index が新しく作られたコミットの状態に合わせて更新される
→ワーキングツリー上のファイルが新しく作られたコミットの状態に合わせて更新される
リモートリポジトリとのやりとり
他の開発者とコード(および変更履歴)を共有するためのリモートリポジトリに、コードや変更履歴をアップロードしたり、ダウンロードする操作について説明します。
アップロードする操作をプッシュと呼び、ダウンロードする操作をフェッチと呼びます。
プッシュすると、ローカルリポジトリの特定ブランチに関する情報がリモートリポジトリにアップロードされます。
フェッチすると、ローカルリポジトリ上にリモート追跡ブランチと呼ばれるリモートリポジトリの特定ブランチの情報が格納されます。
フェッチしただけでは、ローカルリポジトリのローカルブランチの情報は更新されず、ローカルブランチに対しては、リモート追跡ブランチの情報をマージすることで、リモートリポジトリのコードをワーキングツリーに反映することができます。
また、フェッチとリモートリポジトリのマージ操作を同時に行うことをプル(pull)と呼びます。
ファイルシステム上の動きとしては、以下のようになります。
①リモートリポジトリを追加した
→.git/config にリモートリポジトリの情報が登録される
②リモートリポジトリにローカルリポジトリの情報をプッシュした
→./.git/refs/remotes/origin/ブランチ名 が作成される
③リモートリポジトリの特定ブランチの直近コミット情報をローカルリポジトリにフェッチした
→.git/refs/remotes/origin/ブランチ名 がリモートリポジトリの同名ブランチの直近のコミットIDで更新される
→.git/FETCH_HEAD がリモートリポジトリの同名ブランチの直近のコミットIDで更新される
→.git/objectsディレクトリ配下 にリモートリポジトリの同名ブランチの直近のコミットに関する情報が生成される
④ローカルブランチにリモート追跡ブランチをマージした
→.git/refs/heads/ブランチ名 がマージしたリモート追跡ブランチの直近のコミットIDで更新される
→.git/ORIG_HEAD がマージする前のコミットID(マージする前の.git/refs/heads/ブランチ名の値)で更新される
→.git/index がマージ後の状態に合わせて更新される
→ワーキングツリー上のファイルがマージ後の状態に合わせて更新される
ここまで、Gitに関する基本的な概念を説明させていただきました。
Gitを使用して業務を行う一助となれば幸いです。