現場で必要な Git コマンドと git-flow ~ git rm / git reset / git merge / git rebase |研修コースに参加してみた
今回参加したコースは 現場で必要な Git コマンドと git-flow ~ git rm / git reset / git merge / git rebase です。
2022 年 12 月に IPA から発表された DX スキル標準にあるチーム開発のスキルに必要な学習項目に「 Git / Git ワークフロー」が入りました(他にはリーダブルコードなど)。
もちろん、開発現場ではずいぶん前から使われていますが、改めて DX を進める上で Git を標準的に使うとよいですよ、となりました。
さて、その Git について SE カレッジでは「 Git 入門」というコースでワークツリー / ステージングなどのアーキテクチャ、 git init / git pull などの基本操作を紹介していました。 ただ開発現場ではこれではまったく足りず、次のコースが要望されていたところ、このコースが登場しました。
受講してみると、ググって Tips 的には知っていた git rm / git reset (オプション含む)の違いや git merge / git rebase の違い、 git-flow によるブランチ運用など、しっかり体系的に学べました!
では、どのような内容だったのか、レポートします!
もくじ
コース情報
想定している受講者 | Git の clone, branch, merge, add, commit, push の基本コマンドを使えること |
---|---|
受講目標 | 応用的な Git のコマンドが使えるようになる |
講師
この参加してみたレポートでは初登場となる 山田 湖太郎 さんが登壇されました。 とはいえ、参加レポートではおなじみの会社、クロノスさんに所属されています。 また、このコースの前段の「 Git 入門」では同じクロノスの藤丸さんが講師でした。
このコースのリポジトリ構成
今回の研修では同じ PC 内でディレクトリを分けて、リポジトリを構成しました。
- リモートリポジトリ: /remote ディレクトリ
- ローカルリポジトリ: /local ディレクトリ
リポジトリも簡単に以下のように作成すれば準備は完了です。
- 空のリポジトリを /remote に作成
- /remote リポジトリを /local にクローン
なお、 git config
で user.name や user.mail は設定済みです。
また、研修では git commit
までを復習し、以下のような commit が入っています。
- /local リポジトリにコミットされているもの
$ git commit -m "create index.html" [master (root-commit) f0ccac9] create index.html 1 file changed, 2 insertions(+) create mode 100644 index.html
- index.html には Hello Git \ How are you と記述されている
git rm
git commit までの入門コースの内容をおさらいしたところで、入門では登場しなかった git コマンドを使っていきましょう。
まずは git rm です。
git rm
- ファイルを git の管理下から削除
では、試してみましょう。
- 空の delete.html を作ってコミット
$ touch delete.html $ git add delete.html $ git commit -m "create delete.html" [master 8a039a7] create delete.html 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 delete.html
- git rm を実行
$ git rm delete.html rm 'delete.html'
- 確認
$ git status On branch master Changes to be committed: (use "git restore --staged
..." to unstage) deleted: delete.html - 削除した操作がワークツリーに残っている
- 削除をコミット
$ git commit -m "delete delete.html" [master ab532ce] delete delete.html 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 delete.html
これでファイルを削除し、リポジトリからも削除しました。
git log
続いて、 commit や rm などコミットのログを確認してみましょう。
git log
- コミットのログを出力
出力してみましょう。
$ git log
commit ab532cebe18b15070aa0c2ec204fdec0198718c2 (HEAD -> master)
Author: Kronos Taro <sample@kronos-jp.net>
Date: Thu Jan 26 15:38:20 2023 +0900
delete delete.html
commit 8a039a75059ba33a1da5c644670ca4da1bc8a21a
Author: Kronos Taro <sample@kronos-jp.net>
Date: Thu Jan 26 15:35:22 2023 +0900
create delete.html
commit f0ccac99d930a5af236870a3bded4ace2142b74e
Author: Kronos Taro <sample@kronos-jp.net>
Date: Thu Jan 26 15:33:09 2023 +0900
create index.html
git log には他にもオプションが沢山あります。
--oneline
1 コミット 1 行で出力$ git log --oneline ab532ce (HEAD -> master) delete delete.html 8a039a7 create delete.html f0ccac9 create index.html
--grep
コミットメッセージから検索$ git log --oneline --grep="create" 8a039a7 create delete.html f0ccac9 create index.html
git reset
先程の git rm はファイルを削除することでしたが、 git reset はまた異なります。
git reset
- コミットを取り消す
では、試してみましょう。
- git log で取り消す前の状態を確認
$ git log --oneline ab532ce (HEAD -> master) delete delete.html 8a039a7 create delete.html f0ccac9 create index.html
- 最新のコミットを取り消す
- 注意 取り消すコミットを指定するのではなく「どこまで戻すか」を指定する
$ git reset "HEAD^" Unstaged changes after reset: D delete.html
"HEAD^"
の^
で一つ前を意味する(この例では HEAD (最新)の 1 つ前)8a039a7
のようにコミット ID でも指定可能
- ログを確認
$ git log --oneline 8a039a7 (HEAD -> master) create delete.html f0ccac9 create index.html
- ファイルを確認
$ ls index.html $ git status On branch master Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) deleted: delete.html no changes added to commit (use "git add" and/or "git commit -a")
- git rm 同様、ワークツリーには残っている
- ファイルは無い
この状態で、 git reset のオプションを試してみましょう。
--hard
- コミットの取り消しと、ワークツリーからも削除
- もう一度削除をコミット
$ git add delete.html $ git commit -m "delete delete.html" [master e787047] delete delete.html 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 delete.html
--hard
を実行$ git reset --hard "HEAD^" HEAD is now at 8a039a7 create delete.html
- ファイルを確認
$ ls delete.html index.html
ややこしくなりますが、ファイル削除を取り消したので、ファイルが戻ってきました。
git reset のオプションは他にもあり、どこまで戻すのかで変わります。
--soft
- 取り消されたコミットで行われた変更は、ステージングエリアに追加された状態( git add 後)になる
--mixed
- 取り消されたコミットで行われた変更は、ワークツリーに追加された状態になる(オプションなしと同じ状態)
git reflog
git reset –hard で取り消されたコミットは git log では出力できず、 git reflog を使うと出力できます。
git reflog
- HEAD の操作履歴を出力
この git reflog を使って、先程の git reset –hard を取り消してみましょう。
- git reflog で HEAD の移動履歴を出力
$ git reflog -n 3 8a039a7 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^ e787047 HEAD@{1}: commit: delete delete.html 8a039a7 (HEAD -> master) HEAD@{2}: reset: moving to HEAD^
8a039a7
がgit reset --hard
なので、これを取り消してe787047
まで戻す$ git reset --hard e787047 HEAD is now at e787047 delete delete.html
- ログを確認
$ git log --oneline e787047 (HEAD -> master) delete delete.html 8a039a7 create delete.html f0ccac9 create index.html
もとに戻っていますね。 「やらかした! と思ったけど、やらかしてなかったから戻したい!」というときに使うのかな ・・・ 。
git diff
リモートリポジトリとローカルリポジトリとでアプリケーションの挙動が違う場合、よく「 diff 出てる?」と確認しますが、そのコマンドが git diff です。
git diff
- 差分を比較
試してみましょう。
- index.html を修正
$ echo "I'm fine" >> index.html
- index.html を確認
$ cat index.html Hello Git How are you? I'm fine
- git diff で差分を比較
- オプションを指定しない場合、ステージングエリアとワークツリーの差分を表示
$ git diff diff --git a/index.html b/index.html index 2ceec6d..1a68dcf 100644 --- a/index.html +++ b/index.html @@ -1,2 +1,3 @@ Hello Git How are you? +I'm fine
+I'm fine
が差分だということがわかります。 コースでは--stat
オプションで変更したファイル数も確認しました。
また、 git diff ではコミットの比較もできます。
- 変更をコミット
$ git commit -a -m "modify index.html" [master bb01fe8] modify index.html 1 file changed, 1 insertion(+)
HEAD^
とHEAD
を比較$ git diff "HEAD^" diff --git a/index.html b/index.html index 2ceec6d..1a68dcf 100644 --- a/index.html +++ b/index.html @@ -1,2 +1,3 @@ Hello Git How are you? +I'm fine
練習問題
ここで練習問題をやってみましょう。
- list.html ファイル(中身は空)を作成
- index.html の末尾に Hello Excercise を追加
- 1, 2 の変更をそれぞれ別のコミットとし、適切なコメントを付与
- 2 のコミットを –hard オプションを指定して取り消し
ぜひ読者の皆様もチャレンジしてみてください。
git-flow を使ったチーム開発の進め方
ローカルリポジトリでの操作が一区切りしたところで、ここからは git を使ったチーム開発の進め方です。
現場の開発でよく使われているのが git-flow です。
- 主に 5 つのブランチを使って開発
- master
- リリースしているブランチ
- develop
- master からブランチを切る
- 基本的に develop では作業しない
- feature
- develop から機能ごとにブランチを切る
- 各自の作業は feature ブランチで行う
- 作業完了で feature ブランチを develop にマージ
- よく feature/タスク名 で命名
- release
- ブランチは develop からブランチを切る
- 結合テストを実施
- リリース判定が OK なら release から master にマージ
- hotfix
- バグなど緊急で修正するときに使用するブランチ
ちなみに、私が製品開発していたときは少人数だったこともあって GitHub flow を使っていました。 git-flow を見ると、複雑な印象を受ける一方、大規模大人数になるとこれだけ厳密にやったほうがいいんだろうな、という気持ちになりますね。
なお、 GitHub flow に加えて GitLab Flow というものもあり、こちらの資料にその歴史と特徴がよくまとまっていました。
Git の最新アップデートから考える開発手法の潮流 – Speaker Deck よりスライド 26 ブランチ戦略
git merge
実際に git-flow のようなブランチを作成して、 git merge を試してみましょう。
- develop ブランチを作成
$ git checkout -b develop * develop master
- ファイルを追加してコミット
$ echo list > list.html $ git add list.html $ git commit -m "add list to list.html" [develop 50e1390] add list to list.html 1 file changed, 1 insertion(+) create mode 100644 list.html
- develop ブランチを master ブランチにマージ
- マージする master に移動(マージされるブランチではないので注意)
$ git checkout master develop * master
- master ブランチでもファイルを変更してコミット
$ echo merge >> index.html $ git add . $ git commit -m "add merge to index.html" [master c33c060] add merge to index.html 1 file changed, 1 insertion(+)
- develop ブランチをマージ
$ git merge develop Merge made by the 'recursive' strategy. list.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 list.html
- マージする master に移動(マージされるブランチではないので注意)
- ログを確認
--graph
でツリー表示
$ git log --oneline --graph -n 5 * ec7290d (HEAD -> master) Merge branch 'develop' |\ | * 50e1390 (develop) add list to list.html * | c33c060 add merge to index.html |/ * e787047 delete delete.html * 8a039a7 create delete.html
merge もコミットになるのですね。
git rebase
続いて merge にちょっと似ている git rebase です。
git rebase
- ブランチを統合し、履歴を一本化
では、 master ブランチの変更を develop ブランチに rebase で取り込んでみましょう。
- develop ブランチでファイルを作成してコミット -> ログを出力
$ git log --oneline -n 3 f0ba507 (HEAD -> develop) add edit to edit.html 50e1390 add list to list.html e787047 delete delete.html
- master ブランチでファイルを変更しコミット -> ログを出力
$ git log --oneline -n 3 a7f1a4f (HEAD -> master) add rebase to index.html ec7290d Merge branch 'develop' c33c060 add merge to index.html
- develop ブランチから git rebase で master ブランチでの変更を取り込む
$ git rebase master Successfully rebased and updated refs/heads/develop.
- ログを確認
$ git log --oneline --graph -n 5 * 0f850d7 (HEAD -> develop) edit.html * 6c85596 (master) add rebase to index.html * 4707c8a Merge branch 'develop' |\ | * eaf97f2 add list to list.html * | adf0fc3 add merge to index.html |/
merge と違い、マージしたというコミットなしで直接追加されていますね。
rebase を使うかどうかは賛否があるほか、使いどころにもよります。 たとえば master から develop へは rebase を使い、 develop から master へは merge を使う、などです。 個人的には merge でしっかり履歴を残したいなぁと思う派です。
最後に git-flow のワークフローで作業するときのブランチ操作をコマンドでおさらいして、このコースは修了しました。
まとめ
現場で使う Git コマンドをほぼ網羅して試してみました。 Git コマンドは本当に沢山あるので、受講するまではググって「こういうときにはこのコマンド」のように Tips を身につけている感覚でした。
ただ、コースでしっかり git rm / git reset の違い、 git merge と git rebase の違いなどを体系的に学ぶと、ケースに応じたコマンドの使い分けができそうです。
また、山田さんがおっしゃったように git-flow を使うことが現場では多いとのこと、ブランチの運用を見ていると、単体 / 結合テストを入れるタイミングもブランチごとにあり、エンタープライズな開発では必須かも知れませんね。
現場で Git を使っていて、 Tips だけで事故りたくないという方には、とってもオススメのコースでした!
label SEカレッジを詳しく知りたいという方はこちらから !!
IT専門の定額制研修 月額 28,000 円 ~/ 1社 で IT研修 制度を導入できます。
年間 670 講座をほぼ毎日開催中!!
SEプラスにしかないコンテンツや、研修サービスの運営情報を発信しています。