# バージョン管理(Git)について

アプリケーションは日々機能の充実を図る傍目に、アップデートを行っています。 それは、いわゆるバージョンが変わるということです。 プログラミングを行う上で、バージョン管理は重要です。 現代の開発において必須とも言えます。 なぜバージョン管理が必要か、またバージョン管理をどのように行っているのかを説明しています。

# 内容

  • バージョン管理について知ろう
  • Git について
  • GitHub について

# バージョン管理について知ろう

バージョン管理とは、プロジェクトの中で更新されていく様々な成果物の変更の履歴を成果物そのものも含めて記録することです。バージョン管理自体は、特別な作業ではありません。

例えば、新しく作成したファイルに「新規ファイル 20180101」など年月日を付けて管理し、ファイルを編集するたびにファイル名に付けられた年月日を更新していくといった作業もバージョン管理のひとつです。

アプリだと皆さんも目にしたことはあるかもしれませんが、「Ver2.0.3」等の形でバージョンを管理しています。 アプリを使っているとアップデートをしなければならないときもあると思いますが、あれはバージョンが変更されたということですね。共同開発のイメージとしては画像のとおりになります。

image

# なぜバージョン管理する必要があるのか

バージョン管理の理由には以下のようなものが挙げられます。

  • 不具合が起きたとき、最後に動作していたバージョンに戻すため。
  • どのような変更が行われたか確認するため。

以前の状態のファイルがあれば、誤って作成した場合でも、以前のファイルを使ってもとに戻すことができますね。 特にプログラミングでは、1 文字違うだけで動かなくなってしまうこともあります。 そのようなときに、前にどんなことを行っていたかわからなかったり、いつの間にか誰かに変更されて 思っていた機能と異なる挙動になってしまったりすると、手がつけられなくなることは 容易に想像できます。

# バージョン管理の方法

では、プログラミングにおいて、バージョン管理する際に、ファイル名で管理する手法を試してみたところ、以下のようになりました。

  • file1.html
  • file1(v1.1微修正版).html
  • file1(v2).html
  • file1(v2改定).html
  • file1(v2改定最終).html
  • file1(v2改定最終) (2).html
  • file1(v2改定最終) (2)_1月2日_追加更新.html

このような管理の仕方で問題ないでしょうか...。 いいえ、実際には多くの問題が発生します。 プログラミングにおいて、ファイルは 1 つで完結するとは限りません。 そのため、ファイル名を変えて管理するという方法を採用すると、幾重にも同じような内容の異なる名前のファイルが増えて現実的ではありません。 1 つのファイルを他のプログラムファイルが参照している場合もあります。 そのような場合に、ファイル名が変わってしまうと、それこそバグの原因になってしまいます。

大量のファイルを、正確に更新履歴を残し、戻したいときに戻せて、ファイル名を変更せずに バージョン管理するニーズを叶える必要があります。

しかし、人力作業によるバージョン管理は、ファイル名で管理する他に現実的な手法は確立していません。 よって、何らかのツールに頼ることになります。

バージョン管理の方法で主流なツールとして、 CVS(Concurrent Versions System)や SVN(Apache Subversion)、Git などがあります。 この学習サイトでは、 Git のみを取り扱います。

# Git について

このセクションでは 公式ドキュメント (opens new window) を参考に、一部、翻訳の表記ゆれと説明を修正して説明しています。公式が表現している説明とは異なる場合があります。

# Git とは

Git とは、分散型バージョン管理システムです。ファイルのバージョン管理をサポートするツールです。 Git を使うことで、ファイル名によるバージョン管理よりも遥かに細かく、正確にバージョン管理を行うことができるようになります。 Git でバージョン管理を行えば、変更ログを作成することができるため、いつ、どのファイルをどのように変更したかを確認することができます。また、特定のバージョンに戻したい場合も、コマンド 1 つで簡単に行うことができます。

その他、Git を使うことによって以下のようなことができます。

  • 以前のバージョンに簡単に戻せる。
  • 編集履歴を行単位で確認できる。
  • 作業中のファイルに影響なく、別のファイルの変更のために、ファイルを一時的に退避ことができる。
  • リモートリポジトリを使えば、複数人・複数台のコンピュータの環境で利用できる。

# 変更の保持

Git は、データをファイルのスナップショットの集合のように考えます。 Git で全てのコミットをするとき、もしくはプロジェクトの状態を保存するとき、Git は基本的に、その時の全てのファイルの状態のスナップショットをとり、そのスナップショットへの参照を格納します。 効率化のため、ファイルに変更が無い場合は、Git はファイルを再格納せず、既に格納してある、以前の同一のファイルへのリンクを格納します。 Git は、データを一連のスナップショットのように考えます。

画像引用 1.3 使い始める - Git の基本 (opens new window)

Q. 「スナップショットをとる」とは?

A. その瞬間のすべてを保持するということです。コピーする感覚や、写真を撮ってその時間に流れていた時間を切り出す感覚に似ています。

# 3 つのファイル状態

以下は、Git で管理しているファイルの状態を示します。 Git で管理しているファイルは、以下のどれかの状態になっています。ファイルごとに異なります。

  • ワーキングディレクトリ(Working Directory)
    実際に私達が見ることができ、普段作業しているファイルやディレクトリの部分です。
  • ステージングエリア(Staging Area)
    ファイルに変更がある状態で、ローカルリポジトリへのスナップショット対象ではありますが、それらがまだローカルリポジトリに無い状態であることを意味します。 ローカルリポジトリにスナップショットを作成する対象とするための印を付けている状態です。
  • ローカルリポジトリ(.git directory)
    Git によってスナップショットが作られ、安全に保存されている状態です。 このリポジトリ内のファイルは、私達が直接見ることはできません(実際には見ることができますが理解できません。) が、必要に応じてコマンドから、ログを確認したり、ワーキングディレクトリをその時の状態にしたりすることができます。

画像引用 1.3 使い始める - Git の基本 (opens new window)

# Git の基本的なワークフロー

基本的なワークフローは次の様に進みます。

  1. テキストエディタでワーキングディレクトリのファイルを修正します。
  2. 追加(Add)します。これは、修正されたファイルをステージングエリアに追加して、ファイルに印がついた状態にすることです。
  3. コミット(Commit)します。これは、ステージングエリアにあるファイルを取得し、永久不変に保持するスナップショットとしてローカルリポジトリに格納することです。

1 ~ 3 を繰り返し行うことで、バージョンを更新していきます。

Q. 個人開発で Git の必要性がわからない。

A. 個人でプログラムを書いているときに、前の状態に戻したいという状況にならず、必要性の実感がない人もいると思います。 実際に、Git の学習コストは低くありませんし、Git によるバージョン管理は必須ではありません。しかし、バージョン管理を行うということは、 プログラミングを行うこととは別の作業であるため、普段から Git を使用して、徐々にバージョン管理に関して慣れていくことが重要です。 また、後のセクションで説明しますが、以前の状態に戻したいというニーズの他に、リモートリポジトリの使用や、ブランチを分けて作業をするニーズがあります。 こちらも、必要性の実感がないかもしれませんが、リスク管理の上では個人開発でも十分に使う機会があります。 Git に初めて触れるときには、思っていたことと異なる操作が実行されてしまうことがあります。 間違った操作をして、「バージョン管理でしっかりファイルを管理しようと思っていたら、ファイルが全部どこかに消えてしまった。」ということもあり得ます。 本当に必要なタイミングで初めて Git を使い、このような事件を起こさないように、 普段から Git に慣れておくことをおすすめします。

# Git で管理する範囲(ワーキングディレクトリ)

コンピュータに Git をインストールしたからといって、コンピュータの中のすべてのファイルがバージョン管理されるということはありません。 特定のディレクトリ(フォルダ)を指定して、そこを Git の管理対象にすることにより、バージョン管理ができるようになります。この指定したディレクトリを含めた下の階層すべてが管理対象であり、ワーキングディレクトリとなります。

git init コマンドを使用することにより、特定のディレクトリを Git の管理対象にするという初期化が実行されます(ファイルは消えません)。 一度、ディレクトリを Git の管理対象にすると、そのディレクトリの中に .git ディレクトリが作成されます。 この .git ディレクトリの中は、 Git がシステム的に管理しているファイルが入っており、ローカルリポジトリとしても機能します。つまり、過去のバージョンのファイルも格納されていることになります。もしこのディレクトリを削除してしまうと、管理対象とした Git の記録は無かったことになり、過去のバージョンのファイルはすべて消えます。

特定のディレクトリを Git の管理対象として指定したあとに、その中に適当なディレクトリを作り、そこを別の Git の管理対象とすることもできます。これを、サブモジュール (opens new window) といいます。サブモジュールを使う理由は様々ですが、多くの場合使う必要はありません。

Git リポジトリ の中に Git リポジトリ を入れたり、サブモジュールを入れたりすると、管理が少し難しくなり、思わぬ挙動となる恐れがあります。 git init コマンドを使用するディレクトリは、間違って既に管理中の Git リポジトリの中で初期化しないように予めご自身でコンピュータ内のファイルやディレクトリの配置ルールを決めて整理しておくことをおすすめします。

WARNING

以降、コマンドの説明がありますが、ここでは参考程度の紹介となります。 興味がある方は、YouTube やネット記事で調べて見てください。

# コマンドの説明: Init

通常のディレクトリに対して Git の管理対象として監視を開始します。

git init
1

# コマンドの説明: Add

修正されたファイルをステージングエリアに追加して、ファイルに印がついた状態にします。

# 構文
git add {ファイル名}

# 例
git add index.html
git add sample.css
1
2
3
4
5
6

# コマンドの説明: Commit

ステージングエリアに追加したファイルをローカルリポジトリに保存します。コミットを実行するごとに時刻情報とともに格納されるため、ファイルを編集した履歴やその内容を確認することができるようになります。 commit コマンドの -m 引数は必須で、コミットの内容に関して簡単に記述します。

# 構文
git commit -m {コメント}

# 例
git commit -m "デザインの修正を行った。"
1
2
3
4
5

# コマンドの説明: Log

git log
1

コミットログから、誰がいつ何をしたかわかります。Git リポジトリ用のサードパーティ製のツール (opens new window)を使用すると、下の図のようにきれいに時系列に並び、容易に確認することができます。

image

また、詳細なファイルの変更内容も、行単位でわかります。

image

上の画像は、左が変更前の状態、右が変更後の状態を表しています。
19 行目の 3eaf7c という値が削除され ffffff に値が置き換わったことを示しています。 また、 22 ~ 31 行目に行が追記されています。
この 1 コミットで、上述のようなファイルの変更が発生したということを、ご覧のように確認することができます。

# コマンドの説明: Checkout (Branch)

ブランチとは Git のバージョン管理の仕組みのひとつで、現在の作業中の内容から分岐した作業履歴を残すことができるというものです。 はじめは main ブランチのみがリポジトリに存在していますが、自由にブランチを増やしていくことができます。 分岐したブランチは他のブランチの影響を受けないため、同じリポジトリ中で複数の変更を同時に進めていくことができます。

image

上記画像の通りブランチが枝状に分岐しています。水色がもともと編集していた main ブランチですね。 分岐したブランチは他のブランチと合流(マージ)することで、一つのブランチにまとめ直すことが出来ます。

このようにすることで、他のメンバーの作業による影響を受けることなく、自分の作業に取り込むことができます。 また、作業単位で履歴を残すことで、問題が発生した場合に原因となる変更箇所の調査や対策を行うことが容易になります。

# ブランチ一覧表示
git branch

# ブランチを作って切り替える
git checkout -b {ブランチ名}

# ブランチを切り替える
git checkout {ブランチ名}
1
2
3
4
5
6
7
8

# コマンドの説明: Merge

ブランチとブランチを結合することを指します。
新しく main ブランチから別のブランチを作成した場合は main ブランチに統合する必要があります。
その機能をマージと言います。

git merge {取り込むブランチ名}
1

例えば、a ブランチ と b ブランチをマージする際に、どちらも file1.txt の 1 行目に変更を加えたコミットがある場合。 マージを行う際に、競合が発生する場合があります。コンフリクトの発生と言います。 その際は、コンフリクトを解消するために、該当箇所を修正します。

 <<<<<<< HEAD
 AAAA
 =======
 BBBB
 >>>>>>> branchB
1
2
3
4
5

これは、現在のブランチは「AAA」に変更しているが、取り込むブランチ名(branchB)は CCC に変更しているという意味です。 優先したい方だけ残すように修正し、git add {filename}git commit -m {comment} を実行します。

# GitHub について

# Git と GitHub の違い

Git とは別に GitHub というものがあります。 名前は似ていますが、異なるものです。 Git はツールの名前で、GitHub はサービスの名前です。そして、GitHub のようなサービスを Git のリモートリポジトリと言います。

例えば、E メールと Gmail の関係に似ています(注:完全な例えではありません)。E メールは標準化の規格の総称であり、 Gmail は Google が提供する E メールサービスです。サービスは他にも Microsoft の Outlook、 Yahoo! Japan の Yahoo! メール、などがあります。E メールの規格に則った形であれば、ご自身でも E メールサービスを作ることができます。 GitHub はサービスですが、 GitHub と同じ立ち位置のサービスとしては、Bitbucket 、GitLab、 Backlog などがあります。仕様に従って構築すれば、ご自身専用の Git リモートリポジトリサービスを作ることもできます。

GitHub のようなサービスをリモートリポジトリと説明していますが、実際にはもっと多くの機能を提供しており、 リモートリポジトリはその機能の一部です。一般的に GitHub は、ソースコードのバージョン管理システムに Git を使用するソースコードホスティングサービスと説明されています。つまり、ソースコードを他の人と共有可能なサービスということです。ここでは、GitHub の機能一部である、リモートリポジトリとして説明しています。

# GitHub の考え方

Git と GitHub には、それぞれ、リポジトリがあります。手元のコンピュータで管理している Git リポジトリはローカルリポジトリと言い、GitHub にあるリポジトリは、リモートリポジトリと言います。リポジトリは、ファイルやディレクトリを入れて保存しておく貯蔵庫という意味です。

  • リモートリポジトリ
    特定のサーバー上に設置して複数人で共有したり、バックアップしたりするためにあります。GitHub のことです。
  • ローカルリポジトリ
    開発者ごとのコンピューターの中にあります。.git ディレクトリのことです。

image

上の図は、Git と GitHub の関係性を表した図です。

以降、コマンドの説明がありますが、これは GitHub に限らず、他のリモートリポジトリと接続する場合でも同様の構文です。

# コマンドの説明: Push

プッシュとは、ローカルリポジトリにあるファイルをリモートリポジトリに送信して保存する機能です。いわゆるアップロードに近い感覚です。

# 構文
git push origin {ブランチ名}

# 例
git push origin main
1
2
3
4
5

# コマンドの説明: Pull

プッシュでは自分が行った変更をリモートリポジトリにアップロードして同期します。 しかし、複数人で開発している場合は自分以外の作業者が変更をプッシュする場合があります。 すると自分のローカルリポジトリの状態とリモートリポジトリの状態が変わってしまいます。 このとき、リモートリポジトリの変更をローカルリポジトリに同期させることをプルといいます。

# 構文
git pull origin {ブランチ名}

# 例
git pull origin main
1
2
3
4
5

# コマンドの説明: Clone

GitHub 上にあり、手元にない状態から、それらのプログラムをダウンロードする際に、クローンコマンドを使用します。

# 構文
git clone {リポジトリのパス}

# 例
git clone git@github.com:localinnovation-inc/something.git
1
2
3
4
5

# まとめ

  • 開発でバージョン管理する必要がある。
  • バージョン管理は Git がおすすめ。
  • リポジトリはリモートリポジトリとローカルリポジトリの 2 種類に分かれている。
  • コミットすることでローカルリポジトリに履歴を登録できる。
  • 多数の git コマンドがある。
  • Git はツール、GitHub はサービス。
  • ブランチを分けて開発すると安全。