Railsのバージョン管理におけるGitの基礎についてまとめてみました

業務レベルでRailsの開発するにあたって、バージョン管理ツールGit及ブランチに対する考え方GitFlowの基本について、学習してみましたので、メモとしてここに残しておきます。

#Gitの歴史
もともと、linuxのカーネルの開発のバージョン管理ツールが途中でライセンスが必要となったため、
該当する無償ツールがなかったため、linuxの開発メンバーがバージョン管理ツールを開発したとの経緯

##Githubとは
PCなどでローカルで管理したリポジトリをオンラインで管理できるサービス

#Git操作の流れ
ワークツリー：作業場のこと
ステージ：コミット前の準備場所
　→作業場の修正を全部コミットするのではなく、準備完了分のみコミットケースもあるj
　　一度準備完了のものをステージに追加して、それをリポジトリにコミットして、スナップショットを作成する
リポジトリ：スナップショットの保管場所のこと
コミット：変更内容をスナップショットとして記録すること
スナップショット：いつ、誰、何の修正をして、修正したファイルをどこにあるのかと一連の情報を記録したものgitというと、圧縮ファイル、ツリーファイル、コミットファイルにその情報が記録されている
 
・ポイント
①コミット時、差分ではなく、スナップショットとして保存する
②コミットは、直前のコミット情報を保持することで、コミットを遡ることで、以前の情報に戻すことができる

##Git内部でファイル管理
###git addでの処理の中身
　①ワークツリーで変更または新規作成したファイルを圧縮して、圧縮後のファイルをリポジトリに格納する（圧縮ファイル名は、圧縮ファイル＋ヘッダの内容をハッシュを利用して暗号化をかけて生成文字列）
　②圧縮ファイル名と通常ファイル名のマッピング情報を作成して、インデックスファイル（ステージに存在している）に追記または、更新をかける
　　例：index.html → 圧縮ファイル名
・git commit での処理の中身
　③ステージにあるインデックスファイルをコピーして、ツリーファイルを作成して、リポジトリに格納する。ツリーファイルは、階層ごとにひとつのファイルとして保存される
　④上記③と同時に、ツリーファイルを指すためのコミットファイルを作成する。
・ファイルを新規作成してコミットする場合
　①index.htmlを新規作成（ワークツリー）
　②git add実行
　③圧縮ファイルAを新規作成（リポジトリ）
　④インデックスファイルに下記一行が追加される（ステージ）
　　index.html　圧縮ファイル名A  blob
　⑤git commit実行
　⑥ツリーファイル1を新規作成、中身は④のインデックスファイルと同じ(リポジトリ)
　　index.html　圧縮ファイル名A  blob
　⑦コミットファイル1を新規作成
　　ツリーファイル名：ツリーファイル名1
　　親コミットファイル名：空白　※初回コミットの場合
　　作成者：ダミー　
　　コミット日時：ダミー
　　コミットメッセージ：ダミー

###ファイル変更してコミットする場合
　①index.htmlを変更（ワークツリー）
　②git add実行
　③圧縮ファイルBを新規作成（リポジトリ）
　④インデックスファイルに下記が変更される（ステージ）
　　変更前：index.html　圧縮ファイル名A　
　　変更後：index.html　圧縮ファイル名B
　⑤git commit実行
　⑥ツリーファイル2を新規作成、中身は④のインデックスファイルと同じ(リポジトリ)
　　index.html　圧縮ファイルB
　⑦コミットファイル2を新規作成
　　ツリーファイル名：ツリーファイル名2
　　親コミットファイル名：コミットファイル名1
　　作成者：ダミー　
　　コミット日時：ダミー
　　コミットメッセージ：ダミー

###ディレクトリ(./subdir)を新規追加してコミットする場合
フォルダのみ追加された場合、git addを実行しても何も起こらない。（インデックスファイルに何も反映されないため）

###新規作成したディレクトリ(subdir)にファイル(home.css)を新規追加してコミットする場合
①subdirの配下にhome.cssを新規追加（ワークツリー）
②git add実行
③圧縮ファイルCを新規作成（リポジトリ）
④インデックスファイルが下記通り変更される（ステージ）
	   変更前：index.html　圧縮ファイル名B    blob
	   変更後：index.html　圧縮ファイル名B    blob
⑤git commit実行
⑥ツリーファイル名3とツイーファイル名4を新規作成
⑦コミットファイル3を新規作成
　　ツリーファイル名：ツリーファイル名3
　　親コミットファイル名：コミットファイル名2
　　作成者：ダミー　
　　コミット日時：ダミー
　　コミットメッセージ：ダミー

※gitオブジェクトとは：コミットファイル、圧縮ファイル、ツリーファイルのこと
※gitオブジェクトの格納場所：.git/objects/
※最後にコミットしたコミットファイル（マスター）の確認用コマンド：git cat-file -p HEAD
※ツリーファイルの中身確認用コマンド：git cat-file -p xxxxxx　＃xxxxはツリーファイルのファイル名先頭6桁
※ハッシュIDと圧縮ファイル名について
ハッシュIDとは、ファイルのヘッダー（ファイル内容の文字数など、ファイルのメタ情報）とファイルの中身を、SHA-1というハッシュ関数で40文字の英数字に変換したもの。ハッシュIDのうち、先頭2文字をディレクトリ名とし、残り38文字をファイル名にして保存する

#Gitの基本コマンド	
###ローカル基本操作
git init：.gitフォルダとその配下のフォルダ構成を作成する
git clone(gcl)：リモートリポジトリ名：リモートリポジトリのリポジトリがローカルリポジトリにコピーされる
git add (ga)：ワークツリーの変更内容ステージに追加
git commit -m (gcmsg)：ステージの変更をリポジトリにコミットし、コミットファイルとツリーファイルを作成
git commit --amend(gc!)：直前のコミットをやり直し
git status(gst)：ワークツリーとステージの差分、ステージとリポジトリの差分を表示してくれる
git log:コミットの履歴を表示する
glop：コミットの履歴の差分を表示する
    glo,glod,glods：コミットの履歴を1行で表示する
    glols：コミットの履歴で変更ファイル一覧を表示
git rm(grm)：ワークツリーでファイルを削除して、ステージに削除内容を追加する
git rm --cache(grmc)：ワークツリーにファイルを残しながら、ステージに削除内容を追加する
git mv(grm)：ワークツリーでファイル名を変更して、ステージに変更内容を追加する
git reset --hard(grhh)：ワークツリーを特定バージョンのコミットで上書きする
git reset (grh) HEAD：ステージをHEADコミットで上書きする
　　　　　　　　　　(ワークツリーは変更しない、ステージを取り消し時使用)
git checkout(gco) コミットID ファイル名：ワークツリーでの修正を特定のコミットバージョンに元に戻す

###差分関連
git diff(gd)：ワークツリーとステージの差分を比較する。（a:ステージ、b:ワークツリー）
git diff --staged(gds) コミット名：コミットとステージの差分を比較する。（a:対象コミット、b:ステージ）

###リベース関連
git reset (grh) HEAD^：HEADを一つ前のコミット移動し、その後、ステージをHEADコミットで上書きする(ワークツリーは変更しない、履歴をリベース時使用)
git rebase -i (grbi)：インターアクティブモードで、コミットのマージやコミット履歴を整える
git rebase --continue(grbc)：リベース処理を継続させ、次の編集箇所へ移動
git commit --amend(gc!)：最新のコミットをやり直す

###リモート関連
git remote ：リモートリポジトリ一覧を表示
git remote -v ：リモートリポジトリ一覧をURL形式で表示
git remote add リモート名 リポジトリ名：リモートリポジトリを追加
git remote rename リモート名前 リモート名後：リモート名を変更
git remote rm リモート名：リモート名を削除
git push origin ブランチ名：ローカルリポジトリの変更をリモートリポジトリのブランチに反映させる
git push -u リモート名：上位(デフォルト)リモート名を変更
git fetch リモート名：リモートリポジトリからファイルを取得にして、ローカルリポジトリに反映す（保存先：.git/remotes/リモート名/ブランチ名）
git pull origin master：git fetchとgit mergeの処理を一つのコマンドで行う。

###ブランチ関連
git checkout -b ブランチ名：ブランチを新規作成して、そのブランチに切り替える
git branch(gb) ブランチ名：ブランチを新規作成
git branch(gb) -m 旧ブランチ名 新ブランチ名：ブランチの名称を変更
git branch(gb) ブランチ名 コミット名：特定のコミットからブランチを新規作成
git checkout(gco) ブランチ名：ブランチの切替
git branch -a ：→ブランチの全ての情報を表示する
git merge ブランチ名：指定されたブランチの変更を現在のブランチにマージする
git branch -d ブランチ名：対象ブランチを削除する（マスタにマージされていない、変更がまだ残っている場合は削除しない）
git branch -D ブランチ名：対象ブランチを削除する（マスタにマージされていない、変更がまだ残っている場合も強制削除）
git revert コミットID：特定のコミット（例：ブランチからのマージ）の適用を元に戻す(マージコミットをリバートする場合、どっちの親に戻すか指定が必要)

###作業の退避
git stash(gstu)：ワークツリーとステージの変更内容を一時的にstashとい場所に退避させる
git stash apply(gstaa)：最新の作業の退避を復元（stash{0}が最新、以降はインクリメント）
git stash drop スタッシュ名：特定の作業退避を削除

###タグ
git tag タグ名 コミット名：コミットに対して、タグをつける
git show タグ名：タグがつけられたコミット一覧を表示する
git push origin タグ名：タグをGitHubにプッシュする
	
#バージョン管理除外する方法
 ./gitignoreファイルに除外対象を追加

#ブランチとマージ
###コンセプト
コミットとは、スナップショットを記録すること
　→コミットファイル、ツリーファイル、圧縮ファイルでスナップショットを特定できる
ブランチとは、コミットを指し示しているポインタのこと
　→中身：コミットファイルのIDを記録する
　→格納場所：./git/refs/
HEADとは、現在作業中のブランチを指し示しているポインタのこと
　→中身：ref ブランチ名（作業中ブランチが指し示しているポインタを参照する）
　→格納場所：./git/HEAD
コミットすると、現在作業中のブランチが直近のコミットに指す示すようになる
ブランチを分けることにより、同じチームで複数人が同時並行で作業することが可能になる。
マスタ自体もブランチ扱いで、１ブランチに対して、１リモートリポジトリに

###ブランチの切り替え
git checkout ブランチ名
　→すでに存在しているブランチへの切り替え
　→HEADをブランチに指し示し直すことで、ブランチの切り替えを行う。
　　実行前：HEAD→マスタブランチ
　　実行後；HEAD→ブランチ名
git checkout -b ブランチ名
　→現在作業中のブランチの最新コミットからブランチを新規作成して、新規作成したブランチへ切り替え

###ブランチでの変更をマージ
git merge ブランチ名
　→ブランチの変更分を作業中ブランチに取り込む、マージすること
　　マージ前：マスタ：コミット2→コミット1
　　        ブランチA：コミット2'→コミット1
  　マージ後：マスタ：コミット3→コミット2→コミット1
       	    ※コミット3は、ブランチAの変更分のマージコミットになる
       　　　ブランチA：コミット2'

###マージコミットと通常コミットとの違い
マージコミット：親コミットが二つがある（それぞれのブランチの最新コミット）
通常コミット：親コミットが一つしかない

###マージの三種類
①早送りマージ
　→マージ対象ブランチが作成後、作業ブランチは修正がない場合　
　　作業ブランチのポインタをマージ対象ブランチに指し示し直すだけ
②AUTOマージ
　→マージ対象ブランチが作成後、作業ブランチは修正がある場合
　　マージコミットを作成して、作業ブランチがマッジコミットを指し示すようにする
③コンフリクトマージ
　コンフリクト解消の流れ
　→①git status：コンフリクトの対象ファイルを確認する
　→②対象ファイルを手動で開いて編集して、コンフリクトを解消する
　→③対象ファイルを再度コミットする
	
###ブランチを利用した開発の流れ
リリース用ブランチ：マスタブランチ
　→マスタの最新状態が常にリリースと同じ状態
　　最新状態でバグが発生した場合、マスタブランチ一つ前のバージョンに切り替えてリリースすると、バグがない状態でのリリースができる
開発ブランチ：トピックブランチ

###リモートブランチについて
git fetch コマンドでリモートブランチをローカルのorigin/ブランチ名に取得して格納することができる

#gitHubを利用した開発手順
###プルリクエストの運用
プルリクエストとは、チームでの開発手法の一つで、自分を開発したコードをリモートリポジトリにあげて
チームメートにレビュー依頼を送り、コードレビューを受ける。
コードに問題がある場合、チームメートから指摘を受け、修正して再度プルーリクエストを送る
コードに問題がない場合、チームメートから承認をもらい、トピックブランチからマスタブランチへマージを行う

##プルリクエスト実施の流れ
①ローカルリポジトリのmasterブランチの状態を最新にする
　→git pull origin master 
②トピックブランチを新規作成する
③トピックブランチで開発を行い、変更を加える
④上記③の変更をコミットする
⑤上記④でのコミットをGitHubのトピックブランチへpushする
⑥チームメートにプルリクエストを送る
⑦チームメートがプルリクエストを受信後、コードレビューを行う。
⑧コードレビューが通った場合、GitHubのmasterブランチに、トピックブランチの内容をマージする
⑨マージ作業後、トピックブランチが不要となるため、トピックブランチを削除する.  

#リベース機能
リベースとは、マージ作業後、コミット履歴を綺麗に整えてくれるとのこと。

###リベースの流れ
目的：レビュー済のブランチ変更をマスタブランチにマージすること
前提：トピックブランチのプルリクエストが送付済、コードレビューが済んだ状態とする
① masterブランチに切り替え、git pull を実行して、masterブランチを最新にする
②トピックブランチに切り替え、git rebase masterを実行して、トピックブランチにマスタブランチの変更を取り込む
③マスタブランチに切り替え、git rebase トピックブランチを実行して、マスタブランチがトピックブランチと同じコミットを指すようにする

###リベースとマージの動きの違い
　作業前：
　　・トピックブランチ：コミット2→コミット1
　　・マスタブランチ：コミット3→コミット1
　リベース後：
　　・トピックブランチ：コミット2'→コミット3→コミット1
　　・マスタブランチ：コミット3→コミット1
　　※リベースすることで、コミット2が消えた
　マージ後：
　　・トピックブランチ：コミット2→コミット1
　　・マスタブランチ：コミット3'→コミット3→コミット1→コミット2
　　　※マージの場合、コミット2が消えないこと

###リベースとマージの使い分け
リベース：マージ対象ブランチのリベース前のコミットがpush前かつ、コンフリクトが少ない場合
マージ：マージ対象ブランチのマージ前のコミットがpush後かつ、コンフリクトが多い場合
※コンフリクトが多いのが、リベースのマージ作業はコミット単位で行われるため
※コンフリクトが多いかどうかは、プルリクエストを試すことでわかる
注意事項：GitHubにPush後には、リベースは絶対してはいけない
　→リベースすることで、ローカルリポジトリにコミット2が消えたが、GitHubにコミット2が存在するため、整合性が取れない

###git pullコマンド実行時、リベースとマージの挙動の違い
　マージ：マージコメントが残ってしまう
　リベース：マージコメントが残らない。最新データ取得だけしたいので、マージコメントが不要なので、リベースを使用したほうが良い。
　実行コマンド：git pull --rebase
　全体的な設定：git config --global pull.rebase true
           　 git config branch.master.rebase true #マスタブランチだけに上記設定を適用したい場合
            
#コミット履歴のコミットメッセージ変更、コミット並び替え、コミットの削除
目的：まだpushされていない、直近数件の誤ってコミットした履歴を見直したいこと。

###コミット履歴メッセージの変更
①git rebase -i HEAD~3
　→リベースの対象を指定する
②エディターを開き
　メッセージ変更したい履歴をpick→editにへ変更して保存
　→上記によりリポジトリがeditのコミットが適用される前の状態になった。
　　ステージがeditコミットが適用されているの状態となる
⑤git commit --amend -m '変更後コミットメッセージ'
⑧git rebase --continue

###コミット履歴の並び替え
①git rebase -i HEAD~3
　→リベースの対象コミットを指定する
②エディターを開き
　並び替えしたい履歴の順番を変える

###コミット履歴の結合
①git rebase -i HEAD~3
　→リベースの対象コミットを指定する
②エディターを開き
　結合したい履歴をsquashにへ変更して保存

###コミット履歴の分割
①git rebase -i HEAD~3
　→リベースの対象コミットを指定する
②エディターを開き
　分割したい履歴をpick→editにへ変更して保存	
③git reset --HEAD^
　ステージをHEADより一つ前のコミット状態に戻す
　ここでは、ワークツリーの状態が変わらない
④git add 分割コミット1
⑤git commit -m '分割コメント1'
⑥git add 分割コミット2
⑦git commit -m '分割コメント2'
⑧git rebase --continue

###コミット履歴の削除
①git rebase -i HEAD~3
　→リベースの対象コミットを指定する
②エディターを開き
　削除したいコミットを削除して保存
　→リポジトリもステージもワークツリーも上記②の対象コミットが削除された状態になる
　→git reset --hard 対象コミットID の実行と同じ挙動

#ブランチモデルとgit-flow
ブランチモデルとは、開発を進めるにあたって、どのようにブランチを活用していくかのルールのこと。
git-flowとは、web業界で"A successful Git branching model" というブランチモデルの導入を簡単にするためのツール

master：リリースするためのブランチ。プロダクトと同じ状態
release：developブランチから派生
　　　　　リリース準備用のブランチ。リリース番号をつけるなどの微調整
develop：masterブランチから派生
　　　　　開発用ブランチ
feauture：developブランチから派生
　　　　　　機能追加開発用のブランチ
hotfixed:masterブランチから派生
　　　　　　リリース後、クリティカルバッグの対応用ブランチ。
　　　　　　バグ対応後、マスターブランチとdevelopブランチにそれぞれマージ