Git | Gitの学びについてのメモ
Gitとは,プログラムのソースコードなどの変更履歴を記録・追跡するための分散型バージョン管理システムである.ソースコードの変更履歴を順々に記録し,いつ,だれが,どのファイルのどの箇所を変更したかが分かり,記録の際に変更箇所についてのメッセージを付与することもできる.必要であれば,変更履歴に基づき,ソースコードを戻すこともできる.
本記事では,Gitの講座で学んだ事項を以下にメモする.
実施環境
Windows 11
Visual Studio Code (VS Code) version 1.74.3
Gitの基本的な仕組み
Gitはデータをスナップショット(特定時点のファイルやディレクトリ構造などを丸ごと保存したもの)として保存している.コミットは直前のコミットを記録しているので,コミットを辿ることで以前の状態に戻すことができる.
Gitの概要と前提情報
Gitの全体的な利用
ローカル(自分のPC)にワークツリーとローカルリポジトリが存在する.ローカル外にリモートリポジトリが存在する.ローカルツリーで作業を実施し,ファイルが変更したら,スナップショットを記録し,ローカルリポジトリに保存をする.その後,GitHub(リモートリポジトリ)へアップロードする.
なお,リポジトリとは,履歴データのデータベースを意味する.ローカルリポジトリとは,自分のPCの履歴データの置き場を意味する.ワークツリーとは,自分の作業しているディレクトリを意味する.ワークは作業場,ツリーはディレクトリを意味する.
ローカルの詳細
ローカルは以下3つのエリアに分かれている.
- ワークツリー
- ステージ
- リポジトリ
ワークツリーとは,ファイルを変更するディレクトリを意味する.ワークツリーで作業を行い,ファイルを更新させる."git add"することによって,ステージに移動し,コミット(スナップショットを記録)する準備がされる.実践では,変更が完了した分はステージにあげられることが多い.最終的に,"git commit"することによって,リポジトリに移動し,コミット(スナップショットを記録)される.
Gitのデータの管理方法
各々の場合のデータの管理方法を以下に記す.なお,blobオブジェクト,ツリーファイル,コミットファイルは".git/objects"ディレクトリの下に保存されている.
各々の場合の流れを以下に記す.
■"abc.txt"が作成され,ワークツリーからステージを経てリポジトリにスナップショットが記録されるときの流れ
git addによって,以下流れとなる.
- “abc.txt"の内容が圧縮されたblobオブジェクト(ファイルの中身を圧縮しただけのカタマリ)"A"がリポジトリに保存される(blobオブジェクトはハッシュIDで表示される)
- インデックスファイルは,"abc.txt"の中身は"A"であるというファイル構成の情報を含み,ステージで保存される
git commitによって,以下流れとなる.
- インデックスファイルの構成に基づき,ツリー1(ディレクトリ構造が木の枝に似ているため)というファイルがリポジトリで作られる.ツリー1には,インデックスに記載されたディレクトリのファイル構成が保存される.
- コミット1というファイルがリポジトリで作られる.コミット1には,ツリー1というファイル名,作成者(ユーザー名とemail),日付,コミットメッセージが記録される.
■新たなファイル"xyz.html"が作成され,ワークツリーからステージを経てリポジトリにスナップショットが記録されるときの流れ
git addによって,以下流れとなる.
- “xyz.html"の内容が圧縮されたblobオブジェクト"B"がリポジトリに保存される
- インデックスファイルには"abc.txt"の中身は"A"であるというファイル構成の情報を含んでいるが,"xyz.html"の中身は"B"であるというファイル構成の情報も追加され,ステージで保存される
git commitによって,以下流れとなる.
- インデックスファイルの構成に基づき,ツリー2というファイルがリポジトリで作られる.ツリー2には,インデックスに記載されたディレクトリのファイル構成が保存される.
- コミット2というファイルがリポジトリで作られる.コミット2には,ツリー2というファイル名,コミット1(親コミットとして記録:変更履歴を辿るため),作成者(ユーザー名とemail),日付,コミットメッセージが記録される.
■ファイル"abc.txt"が変更され,ワークツリーからステージを経てリポジトリにスナップショットが記録されるときの流れ
git addによって,以下流れとなる.
- “abc.txt"の内容が圧縮されたblobオブジェクト"C"がリポジトリに保存される
- ステージにあるインデックスファイルに関して,"abc.txt"の中身は"C"であるというファイル構成に変更される."xyz.html"の中身は"B"であるというファイル構成は残る.
git commitによって,以下流れとなる.
- インデックスファイルの構成に基づき,ツリー3というファイルがリポジトリで作られる.ツリー3には,インデックスに記載されたディレクトリのファイル構成が保存される.
- コミット3というファイルがリポジトリで作られる.コミット3には,ツリー3というファイル名,コミット2(親コミットとして記録:変更履歴を辿るため),作成者(ユーザー名とemail),日付,コミットメッセージが記録される.
Gitの基本的なコマンド
Gitリポジトリを新規作成する
“git init"によって,Gitリポジトリを新規作成する.
実際の流れは以下になる.
- ターミナルから"cd"によって適当な場所に移動し,"mkdir xxx"によってフォルダを作成する
- “cd"によって,xxxフォルダに移動する
- “git init"によって,リポジトリを新規作成する
- “ls -a"によって,隠れフォルダである".git"を確認することができる
- “ls .git/"によって,フォルダの中身を確認することができる
Gitリポジトリをローカルにコピーする
“git clone"は途中からプロジェクトに入る場合によく用いられる.
“git clone <URL>"によって,ローカルのワークツリーにリモートリポジトリ(GitHub)のファイルが作成され,かつ,ローカルにGitリポジトリ(.gitディレクトリ)がコピーされる.
実際の流れは以下になる.
- GitHubにあるレポジトリを選択し,"Code"をクリックし,URLをコピーする.レポジトリ名を検索することも可能である.
- ターミナルに戻り,"cd xxx"によって,任意のフォルダに移動する
- “git clone https://github.com/xxxx.git"によって,ローカルのワークツリーにリモートリポジトリのファイルが作成され,かつ,ローカルにGitリポジトリ(.gitディレクトリ)がコピーされる
- “cd xxx"によって,ダウンロードされたフォルダに移動し,"ls -a"によってファイル一覧を確認する
変更をステージに追加する
“git add <ファイル名>",もしくは"git add <ディレクトリ名>"によって,ワークツリーの変更をステージに追加する.ワークツリーのすべての変更をステージに追加する場合は,"git add ."を実行する.
実際の流れは以下になる.
- ワークツリーでファイルxyz.txtの内容を更新する
- ターミナルにて,"git add xyz.txt"によって,ステージに変更を追加する
変更を記録する(コミット)
「git commit」によって,ステージに上げた変更をローカルリポジトリに記録する.他にも以下のようなコミットの方法がある.メッセージを記録するほうがおすすめとなる.
- 「git commit -m “メッセージ"」: Gitのエディタを立ち上げることなく,メッセージ付きで記録することができる
- 「git commit -v」: Gitのエディタを立ち上げ,メッセージを記録することができる
変更状況を確認する
ワークツリーでの作業が済み次第,ステージやコミットを実行するが,その状況について確認するのが,"git status"となる.
実際の流れは以下になる.
- ターミナルにて,"git status"によって,変更状況を確認する
- 状況を把握(赤色: ステージに追加されていない変更を確認可能,緑色: コミットすべき変更を確認可能,何もなければ,"nothing to commit"と確認可能)
- 赤色であれば,"git add ." + “git commit"を実行すれば,変更がステージに追加され,ローカルリポジトリに記録される.緑色であれば,"git commit"を実行すれば,ローカルリポジトリに記録される.
変更差分を確認する
■"git add"する前の変更確認(ワークツリーとステージとの変更差分)
- git diff (内容確認後,"q"を実行することで終了する)
- git diff <ファイル名>
■"git add"した後の変更確認(ステージとリポジトリとの変更差分)
- git diff –staged
変更履歴を確認する
“git log"によって,過去の変更履歴を確認することができる.終了したい場合には,"q"ボタンをクリックする.他にも以下のような確認方法がある.
- git log –oneline: 1行で表示する
- git log -p xxx.html: ファイルの変更差分を表示する
- git log -n <コミット数>: 表示するコミット数を制限する
ファイルの削除を記録する
ファイルの削除を記録する方法は以下2種類(“git rm <ファイル名>", “git rm –cached")がある.
■ファイルの削除+Git管理(ローカルリポジトリ)からも削除
- git rm <ファイル名>
- git rm -r <ディレクトリ名>
実際の流れは以下のようになる.
- ターミナルにて,"git rm abc.txt"を実行する
- 「rm 'abc.txt’」が表示される
- “ls -l"を実行すると,abc.txtが消えているのが確認できる
- “git status"を実行すると,コミットすべき変更(deleted: abc.txt)がステージにあると表示される
- ステージから下げる場合には,"git reset HEAD abc.txt"を実行する
- 続けて,"git checkout abc.txt"を実行すると,ワークツリーにおける当該ファイルへの変更の取り消しとなり,abc.txtが復活する
■ファイルを残す+Git管理(ローカルリポジトリ)からは削除
- git rm –cached <ファイル名>
実際の流れは以下のようになる.
- “git rm –cached abc.txt"を実行する
- 「rm 'abc.txt’」が表示される
- “ls -l"を実行すると,abc.txtが存在しているのが確認できる
- “git status"を実行すると,コミットすべき変更(deleted: abc.txt)がステージにあると表示され,abc.txtがuntracked filesだと表示される
- ステージから下げる場合には,"git reset HEAD abc.txt"を実行する
ファイルの移動を記録する
“git mv <旧ファイル><新ファイル>"を実行することでファイルの移動を記録することができる.例えば,abc.txtがワークツリーにあり,"git add abc.txt"によって変更がステージに追加されている場合,"git mv abc.txt xyz.txt"を実行することで,ワークツリーのファイル名が変更され,ステージに追加されている"abc.txt"も"xyz.txt"に変更が記録される.
GitHubにPushする
“git push"することによって,ローカルリポジトリの変更記録をリモートリポジトリに記録することができる.
- GitHubの"profile menu"から"Settings"をクリックする
- “Developer settings"をクリックする
- “Personal access token"をクリックする
- “Generate new token"をクリックし,tokenを発行する(tokenはコピーし,メモ帳などに保管; GitHubにプッシュする際に毎回利用する)
- “profile menu"から"Your profile"をクリックする
- “Repopsitories"タブをクリックし,"New"をクリックし,新規レポジトリを作成する
- レポジトリ内の"git remote add origin https://github.com/xxxx"をコピーする.
- ターミナルを開き,対象となるフォルダに移動し,gitに貼り付け,実行する (今後はoriginという名でGitHubリポジトリにアップロードしたり,GitHubリポジトリを取得できる)
- ローカルリポジトリにgit commitされた記録に関して,"git push -u origin main" (git push <リモート名> <ブランチ名>)を実行し,リモートリポジトリにプッシュする (初回は"-u"を含める)
- 登録したusernameを入力し,続いてtokenを入力する
- GitHubに移動し,作成したリポジトリ内にプッシュしたファイルが格納されていれば成功となる
コマンドをショートカットさせる
- git config –global alias.ci commit: “commit"コマンドは"ci"コマンドに略せる
- git config –global alias.st status: “status"コマンドは"st"コマンドに略せる
- git config –global alias.br branch: “branch"コマンドは"br"コマンドに略せる
バージョン管理したくないファイルの対処方法
VS Codeから,"File"をクリックし,"New File"を選択し,".gitignore"を作成する.
■".gitignore"ファイルに以下のように記載する
- #から始まる行はコメントと見なされ無視される
- 指定したファイルを除外する: xxx.html
- ルートディレクトリを指定する: /root.html
- ディレクトリ以下を除外する: dir/
■".gitignore"ファイル作成の手順
- 対象のディレクトリの下に".gitignore"を作成し,上記に基づき,ファイル名などを記載する
- “git add .gitignore"によって,変更がステージに移動する
- “git commit"によって,変更がローカルリポジトリに移動し,記録される
ワークツリーにおけるファイルへの変更を取り消す
- git checkout — <ファイル名>: 当該ファイルを取り消す
- git checkout — <ディレクトリ名>: 当該ファイルを取り消す
- git checkout — .(ピリオド): 全変更を取り消す
ステージにおけるファイルへの変更を取り消す
- git reset HEAD <ファイル名>: 当該ファイルを取り消す
- git reset HEAD <ディレクトリ名>: 当該ファイルを取り消す
- git reset HEAD .(ピリオド): 全変更を取り消す
直前のコミットをやり直す
- “git add abc.txt"を実行し,"git commit"を実行し,誤りに気付く
- abc.txtファイルを正しく修正し,保存する
- “git add abc.txt"を実行する
- “git commit –amend"を実行する(リモートリポジトリにPushしたコミットはやり直しNG.他者が実行前のリポジトリをコピーしていたら,アップロードできなくなるため)
- “git log -p -n 1″によって,本当に修正されたか確認する(“j"ボタンで下に移動,"p"ボタンで上に移動,"q"ボタンで終了)
GitHubとやり取りする
リモートの状態を確認する
“git remote"によって,設定しているリモートリポジトリの情報(リモート名,リモートURL)を出力することができる.なお,対応するURLを表示する場合は,"git remote -v"を実行する.
リモートリポジトリを新規追加する
- GitHubで新規レポジトリを作成する
- “git remote add <リモート名> <リモートURL>"を実行する.例) git remote add backup https://github.com/user/repo.git
- “git remote"を実行すると,設定したリモートリポジトリの情報である"back"が出力される
- “git remote -v"を実行すると,"back"のURLが出力される
リモートから情報を取得する(フェッチ)
“git fetch <リモート名>"によって,ローカルリポジトリに情報を取得することができる.ワークツリーに情報は渡されない.
実際の流れは以下になる.
- “git fetch <リモート名>"によって,ローカルリポジトリに情報を取得することができる.ワークツリーに情報は渡されない.
- “git branch -a"によって,git branchのすべての情報が出力される
- 2の情報に基づき,"git checkout remotes/xxx/main"を実行することによって,ワークツリーの内容を切り替える
- “ls -l"によって,ファイル一覧を確認し,内容を確認する場合は,"cat xxx.html"を実行すると,中身を確認することができる
- ワークツリーと戻す場合は,"git checkout main"を実行する
- 取得したリモートリポジトリの内容を自分のワークツリーに取り込むには,"git merge origin/main"を実行する
リモートから情報を取得する(プル)
“git pull <リモート名> <ブランチ名>"(省略形: git pull)によって,ローカルリポジトリおよびワークツリーに情報を取得することができる.
実際の流れは以下になる.
- “git pull main branch"を実行し,情報を取得する
- “ls -l"で取得したファイルを一覧で確認する
- “cat xxx.html"でファイルの中身を確認する
注意点:以下理由により,基本はプルよりもフェッチを使うほうが安全である.
ワークツリーにmainブランチとxyzブランチがあり,自分がmainブランチにいるときに,"git pull origin xyz"を実行すると,リモートリポジトリからローカルリポジトリに情報が格納される.続けて,ワークツリーの自分がいるmainブランチにxyzの情報がマージされてしまう.
リモートの情報を詳しく確認する
“git remote show <リモート名>"を実行することによって,以下情報が表示される.
- FetchとPushのURL
- リモートブランチ
- git pullの挙動
- git pushの挙動
リモートを変更・削除する
■変更する場合
“git remote rename <旧リモート名> <新リモート名>"を実行することによって,リモートを変更することができる.
例) git remote rename test new_test
実際に使う際の流れは以下になる.
- “git remote"によって現在のリモートを確認する
- testという名のリモートをnew_testに変える場合,"git remote test new_test"を実行する
- “git remote"によってリモートが変更されたかを確認する
■削除する場合
“git remote rm <リモート名>"を実行することによって,リモートを削除することができる.
例) git remote rm new_test
実際に使う際の流れは以下になる.
- “git remote"によって現在のリモートを確認する
- new_testを削除したい場合,"git remote rm new_test"を実行する
- “git remote"によって,new_testリモートが削除されたかを確認する
ブランチとマージ
ブランチの有用性
並行して複数機能を開発する際にブランチは非常に有用となる.ある時点からAさんはレコメンド追加機能を開発し,Bさんはヘッダーを修正する場合,ブランチを分岐することによって,お互いに影響を受けずに開発することができる.
ブランチの仕組み
コミットをするたびにスナップショットが保存される.2回目以降のコミットでは親コミットの情報が入力されるようになる.このコミットが時系列順に連なる.
ブランチはコミットを指し示したポインタに過ぎない.ある時点で複数のブランチが同じコミットを指し示したとしても,1つのブランチ(main)が新たにコミットをするとmainブランチが指すコミットともう1つのブランチ(test)が指すコミットは異なる.
なお,HEADは現在自分が作業しているブランチを示す.
以上より,ブランチによって,mainブランチとtestブランチは各々異なる開発を並行して行うことが可能となる.
ブランチの新規追加
“git branch <ブランチ名>"を実行することでブランチを新規追加することができる.
実際の流れは以下になる.
- “git branch"を実行することによって,現在のブランチの状況を確認する
- “git branch test"を実行することによって,testブランチを作成することができる
- “git branch"で実際に作成されたかを確認する.米印がついている方が現在いる場所となる.
- それぞれのブランチがどのコミットを指しているかの確認をするために,"git log –oneline –decorate"を実行する.
ブランチを切り替える
“git checkout <既存ブランチ名>"を実行することで既存ブランチに切り替えることができる.ブランチを新規作成と切り替えを同時にする場合は,"git checkout -b <新ブランチ名>"を実行する.
ブランチを切り替えることで,ある時点から分岐して並行した開発を行うことができる.
実際の流れは以下になる.
- “git branch"を実行することによって,現在のブランチの状況を確認する.米印がついている方が現在いる場所となる
- mainブランチに現在いる場合,"git checkout test"を実行することによって,testブランチに移動することができる
- “git branch"を実行することによって,testブランチに米印がついているかを確認することができる
変更をマージする
マージとは,他の人の変更内容を取り込む作業を意味する."git merge <ブランチ名>"や"git merger <リモート名/ブランチ名>"を実施することによって,マージすることができる.mainブランチにいるときに,"git merge test"を実行するとtestブランチの内容がmainブランチに取り込まれる.
なお,マージには以下3種類ある
- Fast Forward: 早送りになるマージ.枝分かれしていなかったときは,取り込むブランチにポインタが移る.
- Auto Merge: 基本的なマージを意味する.2つのブランチが枝分かれしている場合,testブランチの内容がmainブランチに取り込まれ,マージコミットという新しいコミットを作る.新しいコミットは,2つの親コミットを持つことになる.
- conflict:
コンフリクトの解決の仕方
コンフリクトとは,2つのブランチが枝分かれしており,各々同じファイルの同じ箇所を同時に変更し,マージした場合,Gitがどちらを優先すべきかわからなくて起きる事象を意味する.
解決する方法は以下2種類ある
- ファイルの内容を書き換える
- “<<“, “==", “>>"の記述を削除する
コンフリクト解決の流れは以下になる.
- “git merge test"を実行し,コンフリクトが発生すると,CONFLICTと表示される
- “git status"を実行することで確認すると,"both modified"を記載されている
- 該当するファイルを確認すると,mainブランチで記載の箇所とtestブランチで記載の箇所が明記されている.ファイルの内容を書き換え,"<<“, “==", “>>"の記述を削除する.
- “git status"を実行することで確認すると,"both modified"を記載されている
- “git add test"を実行する
- “git commit"を実行すると解決することができる
コンフリクトが起きないようにするための運用
- 複数人で同じファイルを変更しない
- pullやmergeする前に変更中の状態をなくしておく(commitやstashをしておく)
- pullする前に,"git branch"を実行し,自分のブランチの場所を確認する
ブランチの変更・削除
■変更する場合
“git branch -m <ブランチ名>"を実行することによって,自分がいるブランチの名前を変更することができる.
実際の流れは以下になる.
- “git branch"を実行することによって,現在のブランチの状況を確認する.米印がついている方が現在いる場所となる
- mainブランチに現在いる場合,"git checkout test"を実行することによって,testブランチに移動するる
- “git branch -m new_test"を実行することによって,testブランチをnew_testブランチに名前を変更することができる
- “git branch"を実行することによって,ブランチの名前変更を確認する
■削除する場合
“git branch -d <ブランチ名>"を実行することによって,ブランチを削除することができる.ただし,mainにマージされていない変更が残っている場合は削除されない.
強制削除する場合は,"git branch -D <ブランチ名>"を実行する.
実際の流れは以下になる.
- “git branch"を実行することによって,現在のブランチの状況を確認する.米印がついている方が現在いる場所となる
- “git branch -d test"を実行することによって,testブランチを削除することができる
- “git branch"を実行することによって,ブランチが削除されたかを確認する
ブランチを利用した開発の流れ
mainブランチをリリース用ブランチにあて,開発はトピックごとにブランチを作成して進めるのが基本となる.
mainブランチをリリース用ブランチにするメリットは以下になる
- mainブランチは常に最新の状態に保つことができる
- リリース後にバグが発生しても1つ前のmainブランチで対処可能となる
- mainブランチ以外で実行すると,分からなくなる
実際に開発する際は,最新のmainに基づき新たな開発のブランチを作る.例えば,2つのブランチ(dev1, dev2)を作る.この場合,dev1の開発が完了したら,最新のmainにマージする.その後,dev2の開発が完了したら,dev1がマージされた最新のmainにマージする.追加で開発する場合,最新のmainブランチから新たなブランチ(dev3)を作る.
リモートブランチとは
ローカルリポジトリで開発を進めていくと,リモートリポジトリのブランチが進んでいることは往々にある.
もしも,リモートリポジトリのmainブランチがローカルリポジトリのmainブランチよりも内容が進んでおり,追加でfeatureブランチが作られており,その状況でローカルリポジトリが"git fetch"したときの状況を以下に記す.
この場合,ローカルリポジトリに,origin/ブランチ名で保存される.そのため,origin/mainおよびorigin/featureが保存される.
実際の流れは以下になる.
- “git fetch"を実行することによって,リモートリポジトリの内容を取得する
- “git branch -a"を実行することで,リモートブランチについて確認することができる
- “remotes/origin/feature"と"remotes/origin/main"を確認することができる
- “git merge origin main"や"git merge origin feature"を実行することで,これらの内容をマージすることができる
GitHubを利用した開発の手順
プルリクエストの流れ
プルリクエストとは,自分の変更したコードをリポジトリに取り込んでもらえるように依頼する機能を意味する.
実際の流れは以下になる.
- “git pull origin main"によって,mainブランチを最新に更新する
- “git status"で状況を確認する
- “git checkout -b pullrequest_test"によって,新たなブランチ(pullrequest_test)を作成する
- ファイルを変更する
- “git add pullrequest_test"によって,ステージに移動する
- “git commit"によって,ファイルをコミットする
- “git push origin pullrequest_test"によって,GitHubへプッシュする
- プルリクエストをチームメンバーに送るために以下を実施をする
- GitHubにログインし,"Compare & pull request"をクリックする
- “base"を"main","compare"を"pullrequest_test"を選択 (“pullrequest_test"から"main"にプルリクエストを送ることを意味する)し,"Create pull request"をクリックする
- タイトルには,修正した箇所の概要を記載し,本文に"レビューお願いします"などを記載し,"Create pull request"をクリックすると,プルリクエストをチームメンバーに送ることができる
- チームメンバーにコードレビューのお願いをするために以下を実施する
- GitHubの"Reviewers"をクリックし,レビューしてもらう人を選択する
- Reviewerは,メールでレビュー通知を受けるので,GitHubにログインし,Pull requestsのタブをクリックし,内容を確認する
- Reviewerは,"Files changed"タブをクリックすると変更点を確認できる
- 修正してほしい場合には,対象の行にマウスカーソルを近づけ,"+"をクリックし,コメントをし,"Add single comment"をクリックする
- 問題がなくなったら,Reviewerは,"File changed"タブの"Review changes"をクリックし,"Approve"にチェックを入れ,approveする.
- 問題なければ,GitHub側がプルリクエストをマージするために以下を実施する
- GibHubにログインし,"Marge pullrequest_test"をクリックする
- 続けて,"Confirm merge"をクリックする
- GitHub側は今までのブランチを削除するため,"Delete branch"をクリックする
- 内容をローカルに反映させるため,以下を実施する
- “git checkout main"を実施する
- “git pull origin main"を実施し,最新版を取り込む
- マージされたpullrequest_testブランチは不要なので,"git branch -d pullrequest_test"を実行する
GitHubを利用した全体的な流れ
GitHubを利用した全体的な流れは以下のように行われ,最後まで行くと,また1に戻り,繰り返される.
- mainブランチがある
- 開発する際にブランチを作成する
- 開発が完了したら,プルリクエストを送る
- プルリクエストのコードレビューや承認をする
- 送られたブランチをmainブランチにマージし,リリースする
GitHubを利用した実際の流れは以下になる
- ローカルのワークツリーにあるmainブランチから新たなブランチを作成する
- ファイルを変更し,"git add ."によって,ステージに移動する
- コミットすることで,ローカル内のローカルリポジトリに移動する
- 同盟のブランチをGitHubへプッシュする
- プルリクエストを送る
- コードレビューを経て,mainブランチにマージする
- mainブランチを本番サーバーにデプロイする(リリースする)
GitHubを利用した実践上でのポイントは以下になる
- mainブランチは常にデプロイできる状態に保つ
- 新開発はmainブランチから新しいブランチを作成してスタートする
- 作成した新しいブランチ上で作業し,コミットする
- 作業が完了せずとも,定期的にPushする(他のチームメンバーが開発状況を確認可能)
- 開発完了後,mainにマージするためにプルリクエストを使う
- バグの発生を防ぐため,必ずコードレビューを受ける
- コードレビューの承認後,mainブランチにマージする
- マージ後は,すぐにデプロイする(このために,テストとデプロイ作業は自動化にしておく)
GitHubを利用した実践
- “git status"により,現在の自分の居場所かつ最新かどうかを確認する
- “git pull origin main"により,mainブランチを最新の状態にする
- “git checkout -b githubflow_test"により,新たなブランチ(githubflow_test)を作成し,同時に当該ブランチに移動する
- “git add xyz.html"により,ステージに移動する
- “git commit -v"により,コミットする.変更内容を一応確認する
- コミットメッセージを記載し,保存する
- “git push origin githubflow_test"により,GitHubにプッシュする
- GitHubにログインし,"Pull requests"タブを確認し,プルリクエストを作成する
- レビューリクエストを要請する
- 問題なければ,承認者は"Merge pull request"をクリックする
- その後,"Confirm merge"をクリックし,マージする
- プルリクエストを要請されたブランチは,不要なので,"Delete branch"をクリックする
- mainブランチはすぐにデプロイする
- 最新のmainブランチを取得するため,以下を実行する
- “git checkout main"を実行し,居場所をmainブランチにする
- “git pull origin main"により,mainブランチの内容を取り込むことができる
- プルリクエストしたgithubflow_testブランチは不要なので,"git branch -d githubflow_test"により,当該ブランチを削除する
リベースで変更履歴を修正する
リベースする
■リベースの概要
リベースは,変更を統合する際に,履歴をきれいに整えるのに使われる."git rebase <ブランチ名>"を実行すると,ブランチの基点となるコミットを別のコミットに移動する.
■リベースとマージの違い
以下リベースとマージの一連の動きより,リベースによる「コミット3’」とマージによる「コミット4」は同じものになる.両者の違いを以下に記す.
- リベースのコミット3’は親コミットとしてコミット2を参照するが,マージのコミット4は親コミットとしてコミット2とコミット3を参照する
- リベースのコミット3は消えるが,マージのコミット3は消えないため,リベースは履歴が一直線になり,マージの履歴は枝分かれになる
■リベースの一連の動きは以下になる.
- ある時点のmainブランチ(コミット1)からtestブランチを作成する
- その後,mainブランチは更新し,コミットすることによってコミット2となる
- testブランチも更新し,コミットすることによって,コミット3となる
- testブランチ(コミット3)にいる状態でmainブランチを取り込むために,"git rebase main"を実行する
- testブランチはmainブランチの内容を取り込むだけでなく,最新のmainブランチを親コミットとして認識する「コミット3’」が作られる
- 同時に,コミット3は消える
- なお,mainブランチに移動し,"git merge test"を実行すると,マージの1種類であるFast Forwardが起こり,最新のmainブランチを取り込んだtestブランチのいる「コミット3’」に移動する
■マージの一連の動きは以下になる.
- ある時点のmainブランチ(コミット1)からtestブランチを作成する
- その後,mainブランチは更新し,コミットすることによってコミット2となる
- testブランチも更新し,コミットすることによって,コミット3となる
- mainブランチ(コミット2)にいる状態でtestブランチを取り込むために,"git merge test"を実行する
- mainブランチはtestブランチの内容を取り込み,新しいコミット4が作られる
リベースでのNG事項
GitHubにプッシュしたコミットをリベースすると,親コミットの整合性が取れなくなる可能性がある.よって,新たにプッシュする際に拒絶されることがある.
“git push -f"により,強制的にプッシュすることもできるが,Gitの履歴が壊れてしまうため,使わないほうがよい.
リベースとマージの比較
リベースもマージもファイルを統合するという観点では同じだが,作業の履歴を残したいならマージを使い,履歴をきれいにしたいならリベースを使うことが最良だと考えられる.
■マージの特徴
- メリット:コンフリクトの解決が比較的簡単
- デメリット:マージコミットがたくさんあると履歴が複雑化する
- コンフリクト時,コンフリクトが1度しか発生しない
■リベースの特徴
- メリット:履歴をきれいに保つことができる
- デメリット:コンフリクトの解決が若干面倒(コミットそれぞれに解消が必要)
- コンフリクト時,コンフリクトがコミットごとに複数発生する可能性がある
■おすすめ
- プッシュしていないローカルの変更にはリベースを使う
- プッシュした後はマージを使う
- 他のメンバーの変更状況を確認し,コンフリクトしそうなら,マージを使う
- ローカルからGitHubにプッシュしてプルリクエストを作成する(コンフリクトしているならば,GitHubがアラートを出すので,確認する.コンフリクトしているならば,マージを使う.
プルのデフォルトの設定をリベースに変更する方法
■前説
プルにはマージ型とリベース型がある.
マージ型は,"git pull <リモート名> <ブランチ名>"を実行する.マージコミットが残るので,マージしたという記録を残せる.
“git fetch"と"git merge"が合わさった実行なので,"git fetch"によって,リモートリポジトリからローカルのローカルリポジトリに情報を取得し,その後,"git merge"によって,ローカルリポジトリからワークツリーに情報を取得することができる.
例) git pull origin main
リベース型は,"git pull –rebase <リモート名> <ブランチ名>"を実行する.マージコミットが残らないので,GitHubの内容を取得したいだけの場合はこちらがおすすめとなる.
“git fetch"と"git rebase"が合わさった実行なので,"git fetch"によって,リモートリポジトリからローカルのローカルリポジトリに情報を取得し,その後,"git rebase"によって,ローカルリポジトリからワークツリーに情報を取得することができる.
例) git pull –rebase origin main
■設定変更
以下を実行することで設定変更が可能となる.
- “git config –global pull.rebase true": プルのデフォルトをリベース型にする
- “git config branch.main.rebase true": mainブランチでgit pullするときだけリベース型にする
リベースで履歴を修正する
リベースの説明をするが,GibHubにプッシュしていないコミットが対象とする.
直前のコミットをやり直す場合は, “git commit –amend"を実行する.複数のコミットをやり直す場合は,"git rebase -i <コミットID>"を実行する.直前3つのやり直しをするには,"git rebase -i HEAD~3″ を実行する.なお,"-i" はinteractiveの略となる.
HEAD以降の入力の意味は以下となる.
- HEAD~n: n番目の親を指定する.
- HEAD^n: マージした場合のn番目の親を指定する
■コミットの書き換え
リベースでコミットを書き換える一連の流れは以下となる(3つのコミットを変更する場合)
- “git rebase -i HEAD~3″コマンドでコミットエディタが立ち上がる(各々のコミットの前に"pick"が記載)
- 修正したいコミットをpickからeditにし,保存してコミットエディタを終了する(git logとは逆で,一番上が古く,一番下が新しい)
- ターミナルに戻ると,何をすべきかGitに記述してある.(①コミットの内容をやり直す: “git commit –amend"を実行, ②ファイルの修正をする: ファイルを修正し,ステージに追加した上で,"git commit –amend"を実行する,③このまま進める: “git rebase –continue"を実行する)
- 今回は,"git commit –amend"を実行する
- editにしたコミットの箇所でコミットの適用が止まり,立ち上がる
- コミットメッセージを修正し,保存する
- ターミナルに戻り,"git rebase –continue"によって,次のコミットに移動する(pickだとそのままのコミットの内容を適用する)
- “git log –oneline"によって,コミットメッセージの修正の状況を確認する
- “git status"によって,状況を確認する
■コミットを並び替える
リベースでコミットを並び替える一連の流れは以下となる(3つのコミットを並び替える場合)
- “git log –oneline -n 3″により,コミットの履歴が表示される(一番上が新しく,一番下が古い)."q"を押して終了する
- “git rebase -i HEAD~3″により,コミットエディタが立ち上がる(各々のコミットの前に"pick"が記載).コミットの履歴が表示される(git logとは逆で,一番上が古く,一番下が新しい)
- 並び替えしたいコミットを切り取り,任意の場所に貼り付けすればコミットの順番を変え,保存する
- ターミナルに戻ると,"Successfully rebased and updated xxx"を出力されていれば,成功となる
- “git log –oneline -n 3″により,コミットの履歴が本当に変わっているかを確認する
■コミットをまとめる
リベースでコミットをまとめる一連の流れは以下となる(3つのコミットを1つにまとめる場合)
- “git log –oneline -n 3″により,コミットの履歴が表示される(一番上が新しく,一番下が古い)."q"を押して終了する
- “git rebase -i HEAD~3″により,コミットエディタが立ち上がる(各々のコミットの前に"pick"が記載).コミットの履歴が表示される(git logとは逆で,一番上が古く,一番下が新しい)
- 3行とも"pick"から始まっているので,2行目以降の"pick"をすべて"squash"に書き換え,保存する
- ターミナルに戻ると,"Successfully rebased and updated xxx"を出力されていれば,成功となる
- “git log –oneline -n 3″により,コミットの履歴が本当に変わっているかを確認する
- 一応,"ls"によってファイルの有無も確認する
■コミットを分割する
リベースでコミットを分割させる一連の流れは以下となる(1つのコミットを2つに分割する場合)
- “git log –oneline -n 3″により,コミットの履歴が表示される(一番上が新しく,一番下が古い)."q"を押して終了する
- “git rebase -i HEAD~3″により,コミットエディタが立ち上がる(各々のコミットの前に"pick"が記載).コミットの履歴が表示される(git logとは逆で,一番上が古く,一番下が新しい)
- 3行とも"pick"から始まっているので,分割させるコミットの"pick"を"edit"に書き換え,保存する
- ターミナルに戻り,"git reset HEAR^"を実行し,"edit"と記載のコミットを取り消し,かつステージングしていない状態へ戻す
- “git status"により,ステージングしていない状態であることを確認できる
- “git add abc.txt"により,ステージングさせる
- 「git commit -m “abc.txtを追加"」により,コミットさせる
- 続けて,"git add def.txt"により,ステージングさせる
- 「git commit -m “def.txtを追加"」により,コミットさせる
- “git rebase –continue"を実行し,完了となる
- “git log –oneline -n 3″により,コミットの履歴が2分割しているかを確認する
タグ付けの方法
タグは,リリースしたポイントにタグ付けすることで,バグが起きたときに戻りやすかったり,いつ何をリリースしたのかが分かるというメリットがある.
タグの一覧を表示する
タグ付けをするには,"git tag"を実行する.タグの量が多すぎる場合もあるので,「git tag -l(エル) “202302"」とすると,"202302″が含まれているタグのみ出力する.
タグを作成する
タグには以下2種類がある.注釈付きのタグのほうがよく使われる.
- 注釈付きのタグ(annotated)
- 軽量のタグ(lightweight): 情報量を減らしたタグ
■注釈付きのタグ作成方法
「git tag -a [タグ名] -m “[メッセージ]"」を実行することにより,注釈付きのタグを作成することができる.名前だけではなく,コメントと署名も付けることができる.
なお,作成したタグを確認するには,"git tag"を実行する.
例)「git tag -a 20230206_1 -m “version 20230206″」
注釈付きのタグの作成から確認までの流れを以下になる
- 「git tag -a 20230206 -m “version 20230206″」により,注釈付きのタグを作成する
- “git tag"を実行すると,タグの一覧である"20230206″が出力される
- “git show"を実行すると,タグ付けした人の名前,タグ付けした日時,注釈メッセージ,コミット情報が表示される
■軽量タグの作成方法
“git tag [タグ名]"を実行することにより,注釈付きのタグを作成することができる.名前のみ付けることができる.後からタグ付けするためには,"git tag [タグ名] [コミット名]"を実行する.
例) 通常の軽量タグは,"git tag 20230206_1″を実行する.後からタグ付けする軽量タグは,"git tag 20230206_1 aifsd34j"を実行する.
■タグのデータを表示する
“git show [タグ名]"を実行することによって,タグのデータと関連付けられたコミットを表示することができる.
- タグ付けした人の名前
- タグ付けした日時
- 注釈メッセージ
- コミット情報(コミットID,作成者,日付,コミットメッセージ)
例) “git show 20230206_1″を実行する
タグをリモートリポジトリに送信する
“git push [リモート名] [タグ名]"を実行することによって,タグをリモートリポジトリに送信することができる.なお,"git push origin –tags"を実行すると,ローカルにあり,リモートリポジトリにないタグを一斉に送信することができる.
例) “git push origin 20230203_1″を実行する
タグをリモートリポジトリに送信するまでの流れは以下になる
- “git tag"を実行すると,タグの一覧である"20230206″が出力される
- “git push origin 20230206を実行すると,タグをリモートリポジトリに送信することができる
- GitHubを開き,送信されていることを確認する
- “Code" タグの"Release"をクリックし,"Tags"をクリックする
- タグである"20230206″が表示されるので,クリックする
- タグの詳細(タグ名,Author,タグ付けした日付,メッセージ)が表示され,タグのソースコードもダウンロードすることができる
スタッシュで作業を一時避難する方法
作業が途中でコミットする前に別のブランチで作業しなくてはならない際,当該作業を一時避難する目的でスタッシュは使われる.
作業を一時避難する
“git stash"もしくは"git stash save"(どちらも同じ意味)を実行することによって,作業を一時避難することができる.
作業を一時避難させるまでの流れは以下になる
- “git status"でどのブランチにいるかを確認する
- “ls"でファイル一覧を確認する
- ワークツリーで,abc.txtファイルを変更する
- “git status"により,ステージされていない変更が表示される
- “git stash"により,"Saved working directory and index state WIP on main"が表示され,作業が一時避難される
- “git status"で確認しても,ステージされていない変更は表示されなくなり,abc.txtファイルも変更前に戻る
避難した作業を確認する
“git stash list"を実行することによって,避難した作業の一覧を表示させることができる.なお,stash@{0}が最も新しいスタッシュリストとなる.
避難した作業を復元する
“git stash apply"を実行することによって,避難した作業を復元することができる.ステージの状況も復元させる場合は,"git stash apply –index"を実行する.特定の作業を復元させる場合は,"git stash apply [スタッシュ名]"もしくは"git stash apply stash@{1}"を実行する.
避難した作業を復元させるまでの流れは以下になる
- “git status"でどのブランチにいるか,コミットすべきものがあるかの確認をし,abc.txtファイルの内容も確認する.
- “git stash apply"を実行すると,ステージングされる前であるという表示が出力され,abc.txtファイルの内容も"git stash"する前に実行した作業が復元される
避難した作業を削除する
“git stash drop"を実行することによって,避難した最新の作業を削除することができる.特定の作業を削除する場合は,"git stash drop [スタッシュ名]"もしくは"git stash drop stash@{1}"を実行する.すべての作業を削除する場合は,"git stash clear"を実行する.
避難した作業を削除するまでの流れは以下になる
- “git stash list"でスタッシュリストを確認する
- “git stash drop"を実行すると,"Dropped xxx"と表示され,最新の作業は削除される
- “git stash list"で削除されたかを確認する
参照
Udemy | Git: チーム開発で必要なGitを完全マスター
以上