聞いたら一生の宝、プログラミングの基礎の基礎

第10回gitの基礎を見直す

みなさんこんにちは。teratail開発チームの出川幾夫です。

gitはデファクトスタンダードとなっているバージョン管理システムで、チーム開発で今ではもはや必須のツールです。

gitは非常に強力なツールで開発者の多くが日常的に利用していますが、機能や概念が複雑で学習コストが高く、きちんと理解して利用するのは難しいのが欠点と言われています。その分きちんと理解して利用すれば、一人での開発もチームでの開発も非常に効率的かつ安全に行うことが可能になります。

そこで今回は開発でgitを利用している人のために、日常の開発にあたって必要となるgitによるバージョン管理の考え方について、あらためてまとめていきたいと思います。

バージョン管理をするもの、しないもの

gitはファイルであれば何でもバージョン管理が可能ですが、バージョン管理すべきものとそうでないものがあります。特に新たにリポジトリを作成して開発を始めるときは、はじめにこれらをはっきりさせておくことが非常に重要です。

基本的に開発者の書いたソースコードは全てバージョン管理を行い、以下に該当するものはバージョン管理しないとするのが一般的です。

  • コンパイル/ビルド後に生成される実行ファイル
  • パスワードやアクセスキーを含むファイル
  • ユーザによって変更が行われるディレクトリのファイル
  • 外部ライブラリ

バージョン管理をしないファイルは、プロジェクト開始時に.gitignoreにそのファイル名を記述しておくと混乱やミスが防げます。

ここでは上に挙げた、バージョン管理すべきでないファイルについて詳しく見ていきましょう。

コンパイル/ビルド後に生成される実行ファイル

コンパイル後に生成される実行ファイルなどはバージョン管理に含めません。履歴として残すべきなのはコンパイル前のソースコードであり、実行ファイル等は履歴として本質的でないからです。CやGoなどのビルド後のバイナリパッケージや、Sassから生成されるCSS等がこれに該当します。

パスワードやアクセスキーを含むファイル

パスワードやアクセスキーを記述する必要のあるファイルなど、ネットワークに流すと危険な情報もバージョン管理を行いません。これらの情報は個別に手動で管理するか、環境変数から読み込むようにします。

参考:githubにプッシュするapiのkeyやpassなどについて
https://teratail.com/questions/17010

ユーザによって変更が行われるディレクトリのファイル

Webアプリケーションでユーザのアップロードするファイルや画像を格納するディレクトリ内も当然バージョン管理は行いません。この変更は開発者による変更ではなく、機能の拡張や修正とは無関係だからです。

外部ライブラリ

外部ライブラリやリポジトリもなるべくgitでバージョン管理すべきではありません。バージョン管理をするリポジトリの容量が大きくなりパフォーマンスが悪くなりますし、今後ライブラリの更新に追従することが難しくなるからです。

参考: Laravelデフォルトのgitignoreファイルについて
https://teratail.com/questions/13162

外部ライブラリはパッケージ管理ツールをうまく活用することで管理がしやすくなります。RubyならBundler、PHPならcomposer、iOSであればCocoaPodsなどを使用します。Gemfile、composer.json、Podfileなどに依存するパッケージを記述し、更新のたびにbundle installcomposer installpod installで落としてくるようにするのが定石です。パッケージ管理システムで管理できないものはgit submoduleを利用して依存関係を記録しましょう。

近年のWebアプリケーションフレームワークでは、アプリケーション本体やそれに依存するパッケージもパッケージ管理ツールに任せる例が多くなってきました。パッケージをインストールするディレクトリをはじめに必ず明示的に決めておき、そこを.gitignoreに記述します。注意が必要なのが.lockファイルもバージョン管理すべき点です。インストール時に生成されるファイルなので管理しないものと思われがちですが、これは実際にインストールされた依存関係を記述するファイルです。これがないとインストールのたびにパッケージのバージョンが変わるということが起こりえます。

操作ミスを防ぐ、柔軟な操作をする

gitの操作は開発中は随時行う作業です。gitの強力な機能を積極的に活用することで、ミスを防いだり柔軟な操作をしたりと、作業の大きな効率化が実現できます。

変更をインデックスに記録する際に非常に有用なのがgit add -p-pオプションです。これを利用することで同一ファイル内の一部分だけをインデックスに記録してコミットすることができます。git add .は複数のファイルを一度にインデックスできますが、不要なファイルをインデックスに記録してしまうことがあるので、ファイル名を個別に指定するようにしたりgit add -pなどのオプションを使って柔軟かつ確実にコミットの内容を指定するとミスが少なくなります。

参考: git diffで表示される修正を次のcommitに含みたくない
https://teratail.com/questions/20907

git stashはワーキングツリーの内容を一時的に保存することのできる機能です。git 2.5からgit worktreeというコマンドが追加され、一つのリポジトリで複数の作業を同時に行うことができるようになりました。

過去の履歴の中で発生したと思われるバグがあった場合、git bisectを使えば二分探索でバグが発生したコミットを効率的に探すことができます。git bisect start <bad-commit> <good-commit>で2つのコミット間での二分探索が開始され、問題がない状態ならgit bisect good問題がある状態ならgit bisect badを実行します。インストラクションにしたがってこの操作を続けることで、最終的に問題のあるコミットを特定することができます。

git blameで行ごとのコミットや作業者を特定したり、git revertで特定のコミットを打ち消すようなコミットを利用してバグを解消しましょう。

git resetはHEADを任意の場所に移動させるためのコマンドで、コミットミスや漏れを修正するなどさまざまなシーンで利用可能なコマンドです。オプションによりどこに移動させるかが変わり、どう違うのかを正しく理解しておく必要があります。

  • git reset --soft {ref}はHEADのみを、
  • git reset --mixed {ref}もしくはgit reset {ref}はHEADとインデックスを、
  • git reset --hard {ref}はHEADとインデックスとワーキングツリーの全てを、

それぞれ{ref}で指定したコミットに戻します。git reset前のコミットに戻る場合はgit reflogでコミットを探すのが便利です。

git checkout . (ピリオド)とgit reset --hard HEADの違い
https://teratail.com/questions/24069

さまざまな開発フロー

gitはプロジェクト管理としても非常に有用です。チーム開発の際にはブランチ戦略をはじめに決めておくことが非常に大切です。ここではgitを利用した開発フローの幾つかを紹介します。

Vincent Driessenが提唱したもっとも有名な開発フローがgit-flowです。git-flowでは開発者はdevelopブランチからfeatureブランチを切って完成したものをdevelopブランチにマージしていき、それをreleaseブランチにマージすることでリリースとします。masterブランチは、リリース済みのreleaseブランチをマージしてタグをつけることで成長します。これは効率的にバージョン管理ができるようになる一方、ブランチの操作や管理が非常に煩雑になります。

GitHub Flowはgit-flowのこうした問題点が指摘される中生み出されました。ブランチは、デプロイ可能なものが積み上げられるmasterブランチと、開発を行うトピックブランチの2つという、git-flowと比較して非常にシンプルな構成です。masterブランチが更新されるたびに随時その変更がデプロイされます。ブランチの進行方向が一つなので非常にわかりやすく、変更を直ちにデプロイするような開発体制に向いています。

GitLab-flowはGitHub flowを改良したもので、ブランチの構成はGitHub flowにリリースに必要なブランチを追加したものとなっています。たとえば、masterブランチはproductionブランチへマージされ、このブランチを自動テストの対象やステージング環境と紐付けます。その後productionブランチがreleaseブランチにマージされ、このブランチを本番環境にデプロイします。teratail開発チームではこのGitLab-flowを採用しています。

チームの開発のやり方やプロジェクトの性質によってどのフローが最適かは大きく変わってきますので、よく考えながらブランチ戦略を決定しましょう。

終わりに

今回はgitのツールとしての基礎について、バージョン管理の考え方や日常の開発に役立つ部分を紹介しました。

gitはいまも非常に積極的に開発されていて、開発を安全かつスムーズに行うための機能が随時追加されています。さまざまな機能を利用して開発をうまく進めていきましょう。

teratail【テラテイル】|思考するエンジニアのためのQAプラットフォーム
https://teratail.com/

おすすめ記事

記事・ニュース一覧