バージョン管理とは何か?
1つのファイルや、ファイルの集合に対して、時間とともに加えられていく変更を記録するシステム。後から過去のバージョンを呼び出すことができるようにするためのもの。
- 履歴を保存する
- 過去のバージョンに戻る
- 何が起こったのかを知る
- バックアップ
- 共同作業をする」
「ワーキングティレクトリでは自由に編集や機能追加を行い、ステージングエリアに意味のあるまとまりとしてまとめ、リポジトリに意味のある単位でバージョン管理してもらう」という流れ。
untracked(未追跡)状態とは、ステージングエリアにもリポジトリにも登録されていない状態のことを指します。
- 迷った時は、とにかくgit statusを実行してみる。
ステージングエリアとは?
ファイルをコミットする前に変更内容を一時的に登録しておくバッファのようなもの
ステージングエリアはなぜ必要なのか?
この仕組みは「一つのコミットには、主題となる変更とは無関係な変更を含めない」とうポリシーの元になりたっている。
ステージングを用いることにより、関連性の強い変更のみを選別して焦点が明確なスナップショットを作成してからコミットを行うことができるようになる。
- gitが管理しているさまざまなファイルの状態
ex) new file: リポジトリにコミットされるのは初めてのファイルである
modified: 今回のステージングでファイルの内容が変更される
deleted : 今回のステージングで消去される
gitでファイルの変更差分を確認するためには、「どの領域にあるファイルの差分を確認するのか?」という視点が重要になる。
それは、ワーキングディレクトリとステージングエリアとの比較なのか、それともステージングエリアとリポジトリとの比較なのか、それともワーキングディレクトリとリポジトリとの比較なのか、コマンドを打つ際に明確にしておく必要がある。
また、ファイルの差分確認に限らず「どの領域に対してなんの操作を行うのか」という視点を持つ癖を身につける。
git commit
- ステージングエリアに登録されているファイル・ディレクトリを一つの束として、リポジトリの歴史として記録します。
- この記録をもとにファイルを過去の状態に戻したり、削除されてしまったファイルを復元したりすることができるようになる。
git commit -a -m <コミットメッセージ>
-aというオプションにより、最新のステージングエリアを比較して変更があった部分をステージングしてコミットする。
変更のあったファイルをまとめてaddしてからcommitを実行できる。
注意点として、新規に作成されたファイルは-aオプションの対象外
コミット単位はどれくらいが適切?
- 基本的には一つのコミットには一つの作業だけを含めるべき。
- 「粒度」は小さくが正義
gitの基本コマンドのおさらい
- プロジェクトを管理下に置くには、git initで初期化します。
- ワーキングディレクトリに追加したファイルはuntracked(未追跡)状態として、git addを利用して変更を選択的にステージングエリアに追加することができる。
- ステージングエリアに新規に追加したファイルは、new fileとして監視対象になる。
- 監視対象になったファイルに変更があれば、modified(変更済み)状態として認識される。
- ワーキングディレクトリと、ステージングエリアに加えた修正は、git diffで確認することができる。
- ステージングエリアに加えた修正は、git commitによって一つの束としてリポジトリにコミットされGitに履歴管理される。
- 履歴登録されたコミットは、git logで確認できる。
git resetは、一度実行すると二度と取り消すことのできない危険をはらんだコマンドです。
そのファイルの変更内容が不要であると確実に言い切れる時以外は使用を控えるのが無難です。
アンステージング(2パターン)
- ワーキングディレクトリの状態はそのままにして、単純にステージングエリアに登録されたファイルの変更を消す
- ステージングエリアに登録されたファイルの変更で、ワーキングディレクトリを上書きする
ワーキングディレクトリの状態を上書きするためには
- 一度git checkout を使い、ステージングエリアの変更内容をワーキングディレクトリに上書きした後で
- ステージングエリアとの差分をなくし、ステージングエリアへの登録内容をgit rm --cached コマンドで取り消します。
git reset
ステージングエリアの変更をワーキングディレクトリに戻す方法
git clean
ワーキングディレクトリにあるファイルでステージングしたことのない追加対象外のファイルを一度に消す方法
複数ブランチでの運用が活発化してくると、テキストベースだけでコミット履歴を追いかけるのが困難になってきます。
git log --graphコマンドを使うことで、コミット履歴を視覚的にわかりやすく確認できるようになる。
git reverコマンド
過去のコミット履歴から指定したスナップショットをなかったことにする。
なかったことにするといっても履歴からその部分を抹消するのではなく、そのコミットによって加えられた変更内容を打ち消すコミットを新たに加えるというもの。
.gitignore
Gitで管理したくないファイルを無視させる
.gitignoreの書式
-
.gitignore自体は普通のテキストファイルです。
-
ここにインデックスに登録したくないファイルやディレクトリを指定する。
-
1つずつ書かずとも正規表現を使って指定することができる。
-
.gitignoreファイルは、リポジトリノルートディレクトリにおくか、特定のサブディレクトリに置いて利用
- #(ハッシュ記号)で始まる行はコメントとして扱われる
- 空行は無視される
- !(エクスクラメーション記号)で始まる行はパターンに該当しないものを表す
- /(スラッシュ記号)で終わる場合はディレクトリのみを表す
- /で始まる行はgitリポジトリのルートディレクトリからを表す
- 複数の条件にマッチするパターンがある場合は最後にマッチした条件が適用される
これらは、globパターンというシェルスクリプトなどで使われる簡易的な正規表現。
(アスタリスク)は0個以上の文字にマッチする。
[a,b,c]は括弧内の任意の文字(この場合はa,b,cのいずれか)にマッチする。
[0-9]のようにハイフン区切りの文字を各括弧で囲んだ形式は、2つの文字の間の任意の文字にマッチする。
アスタリスクを2つ続けてネストされたディレクトリにマッチさせることもできる。
a/**/zのように書くと、a/z,a/b/z,a/b/c/zなどにマッチする。
まとめ
- バージョン管理には、集中管理と分散管理があり、Gitは分散管理型である。
- Gitには、ワーキングディレクトリ、ステージングエリア、リポジトリの3つのエリアがある。
- ステージングエリアにはgit addで変更を追加し、git commitでコミットすることでリポジトリに束で変更履歴を記録でする。
- git logを使うことで履歴を確認できる
- ファイルの修正も、変更の追加の取り消しも、履歴の取り消しも、記録した履歴の修正も、後から行うことができる
- Gitは自分好みにカスタマイズできる
HEADは、現在のリポジトリの先頭を指しているもので、インデックスとも呼ばれる。
HEADが指している状態のファイルの状態が、ワーキングディレクトリに書き出されている。
HEADはブランチを参照している。
ブランチのことを意識していなくても、初めてのコミットの段階で、masterブランチが自動的に作成され、HEADは最初、masterブランチを参照している
ブランチを切り替えると、HEADは切り替えたブランチを参照するようになります。
これがブランチのチェックアウト
*コミットとHEADとブランチ
コミットを行うと、現在のコミットに子供となるコミットを作成し、そこにHEADが指しているブランチを移動します。例えば、ブランチ1が指すコミットBでコミットを行うと、新規コミットDを作成し、HEADが指しているブランチ1の参照をコミットDに変更します。
*ブランチによる分岐
ブランチ1とブランチ3が、同じコミットDを指しているとします。
現在のブランチは、ブランチ1です。
つまりHEADはブランチ1を参照しています。
この状態で、コミットを行うと、コミットDの子コミットEが作成され、HEADが指しているブランチ1の参照が、コミットEに移動します。
ここで大切なのは、ブランチ3は、変わらずコミットDを指し続けているということです。
ここで、ブランチ3をチェックアウトして、コミットをしたとすると、コミットDは新しいコミットFを作成します。コミットは、HEADの現在の参照であるブランチ3をコミットFに移動します。
なぜブランチを使ったコード運用がいいのか
Gitでブランチを用いることの一番のメリットは、各ブランチを起点に別々にコミットを作成し続け、結果的に、機能や意味の単位を分けて、各ブランチの先頭を参照することによって、個別にバージョン管理を行うことができる点。
機能追加と不具合改善、それぞれに対応するブランチを作成し、おのおののブランチでコミットとプッシュを進めていけば、リモートリポジトリから情報をフェッチするだけで進歩もお互いにわかり、個別にバージョン管理をすることができます。
仮に機能追加が不要となった場合でも、機能追加のブランチを破棄するだけで、機能追加のコミットからの参照が外れ、作成元のブランチにも、不具合改善を行っているブランチにも全く影響なく開発を中断することができる。
マージ
各ブランチで開発が進んだら、各ブランチの開発内容を併合したいタイミングがやってくる。
そこで利用するのがマージ
ブランチとプッシュ
ローカルリポジトリで開発を進め、一段落したら開発中のブランチの内容を、リモートリポジトリに同期する。
プッシュは、ローカルリポジトリで作成している全てのブランチの情報を伝えてしまうわけではなく、特定のブランチの情報を、リモートリポジトリの特定のブランチに同期する。
ブランチを指定してプッシュする仕組みによって、開発者はローカルリポジトリで好きなブランチを作成して開発を進めることができる。
ローカルリポジトリで開発上の都合で作成した複数のブランチのマージを多用して、同期すべきブランチにまとめた上で、他の開発者が参照しているリモートリポジトリのブランチにプッシュして同期を行う。
プル
リモートから各ブランチの最新の情報をフェッチし、ローカルリポジトリの特定ブランチへマージする2つの作業を束ねているのが、プル
ブランチを用いたチーム開発の流れ
1,メインブランチからトピックブランチを作成する。
2,機能実装を行い、コミットする。
3,定期的にトピックブランチをリモートリポジトリにプッシュする。
4,定期的にメインブランチの内容をフェッチしてローカルリポジトリにマージする
5,トピックブランチをメインブランチにマージする
6,リモートリポジトリのメインブランチにプッシュする
1,メインブランチからトピックブランチを作成する。
トピックブランチとは、1つのトピックに絞った開発を行うためのブランチ。
一つの機能や一つのバグ修正など、粒度は様々。
メインブランチは、開発の中で中心となっているブランチで、プロジェクトメンバーの大多数が参照しているブランチ
トピックブランチの作成は、メインブランチのきりのいいところから行われる。
2,機能実装を行い、コミットする。
実際に開発を行って、ローカルリポジトリのトピックブランチにて、コミットを繰り返す。
3,定期的にトピックブランチをリモートリポジトリにプッシュする。
ローカルリポジトリの開発内容は、他のチームメンバーからは見えなくなります。
定期的にリモートリポジトリのトピックブランチに最新の状態をプッシュし、メンバーに見てもらいながら進める。
方法は、プルリクエストやコードレビュー
4,定期的にメインブランチの内容をフェッチしてローカルリポジトリにマージする
ローカルリポジトリのトピックブランチは、メインブランチの特定のコミットから分岐しているため、メインブランチで進んでいる開発内容とは一見無関係です。
しかし、チーム開発では、少なくともメインブランチで何が起きているか、自分の作成している内容と競合する内容がないかは、定期的に確認する。
定期的にメインブランチの内容をフェッチして、マージ、して更新。
5,トピックブランチをメインブランチにマージする
トピックブランチの開発が落ち着いたら、もしくは、レビューを通ったなどで一区切りが着いたら、トピックブランチをメインブランチにマージを行う。
マージの前にメインブランチを最新の状態にプルしてからマージする。
6,リモートリポジトリのメインブランチにプッシュする
マージが無事に完了したら、リモートリポジトリのメインブランチにプッシュする。
これで、全員がトピックブランチの取り込まれたメインブランチを参照することができる。
新しいブランチは「現在HEADが指しているブランチが参照しているコミット」に対しての新しい参照として作られる。
削除するブランチが参照しているコミット履歴にマージされていない変更(コミット)が残っている場合はGitが削除を拒否する。
誤ってマージしていないものを削除してコミットを見失うのを未然に防げる。
また、チェックアウト中のブランチは削除できないため、指定のブランチにチェックアウトしている場合は他のブランチにチェックアウトしてから行う。
2種類のリモートリポジトリのセットアップ
・ リモートリポジトリの内容をローカルリポジトリに同期して開発を始める
・ ローカルリポジトリの内容を空のリモートリポジトリに同期して開発を始める
git remote addコマンドは、リモートリポジトリへのアップロードする際毎回必要なわけではなく、一度Gitに設定すれば、その後はリモートリポジトリがどこなのか覚えておいてくれる。
今回はリモートリポジトリにoriginという名前を付けているので、設定後のあらゆるGitコマンドでは、リモートリポジトリとしてoriginを指定することになる。