はじめに
こんにちは!リンクアンドモチベーションの宮田です!
本記事はリンクアンドモチベーション Advent Calendar 2024の2日目を担当しています。🎄
アドベントカレンダー初参戦ということでワクワクしながら書きましたが、至らない点もあるかと思います。精一杯書きましたので、どうぞ最後までお付き合いください!
4月に入社してからエンジニアとしての道を歩み始め、手厚い研修を経ました。とはいえ、まだまだわからないことだらけです。特に、GitやGitHubに関しては、
以前、記事を書いて、「わかったつもり」だったのに、初めて自分でチケットを実装し、プルリクエストを出す段階で壁にぶつかりました。
先輩「このコミットは別のPRで出してほしい!」
僕(別PRか。プッシュしちゃったけどどうしよう??(´・ω・))
どう操作すればいいのかわからず、戸惑ってしまいました。これをきっかけに、自分の知識の足りなさに改めて気づかされました。
そこで今回は、
- GitとGitHubの基本を復習
- VScodeでのGit操作がGitHub画面にどう反映されるかを確認
- GitHubにプッシュしたコミットを取り消す方法とその種類を解説
という3本立て(+α)で、僕自身の学びを共有していきます!
コミットの取り消し操作自体はそんな頻繁に行うものではないかもしれませんが、GitやGitHubについての理解を深める大変良い機会になりました!
この記事が、同じような悩みを持つ方の助けになり、「GitHub、なんかよくわからん……」という感覚を少しでも和らげられるきっかけになれば嬉しいです。
それでは、GitHubと仲良くなれるように頑張っていきましょう!!🐈🙌
目次
- はじめに
- GitとGitHubの基本を復習
- VScodeでのGit操作がGitHub画面にどう反映されるかを確認
-
GitHubにプッシュしたコミットを取り消す方法とその種類を解説
- 4.1. 強制プッシュ
- 4.2. 新しいコミットで取り消す
- チェリーピックという方法🍒
- 最後に
- 参考文献
GitとGitHubの基本を復習
復習といってもかなり簡単な説明になります
Git
Gitはソースコードの変更履歴を管理するためのバージョン管理システムです。コードの変更を記録して、過去のバージョンに戻したり、複数人が並行して作業する際の変更内容を統合したりすることができます。Gitはローカルで使用でき、個人での作業にも対応しています
GitHub
GitHubは、Gitを利用したリモートリポジトリサービスの一つです。Gitのローカルリポジトリは自分のPCにしか存在しませんが、GitHubを使うことで、インターネット上にリモートリポジトリを作成し、複数の開発者が同じプロジェクトで作業できるようになります。チームで開発を行う際に、各開発者が自分のPCでコードを編集した後、GitHubに変更をプッシュ(push) することで、全員が同じコードを共有できるようになります。
以下、自分で作成したお手製GitHub図解です。
ローカルリポジトリとリモートリポジトリとの間でやり取りを表したのが以下の図です。
別のローカルリポジトリもいるパターンです。
結局図で理解するのが一番良さそうです。
VScodeでのGit操作がGitHub画面にどう反映されるかを確認
今回は説明用にGitGitリポジトリを作ってみました。
GitHubで確認できるのはもちろん、リモートリポジトリです。
ブランチを切って、VScodeからpushすると、GitHub上にPR(Pull Request)が作成されます。今回はわかりやすいように「PUSH」というコミットメッセージにしてあります。
このPRをクリックすると、ConversationやCommitsでこのブランチからのコミット履歴が確認できます。(※コメントが雑なのは許してください)
コミット履歴といっても今は一つしかなく、わかりにくいと思うので、一度PRを作成したブランチから、追加で変更を加えてcommit
→push
してみます。
次のコミットメッセージはとりあえず「CHANGE」にしてみました。すると、以下の画面のように新規のコミットが追加されます。
一つのブランチからのcommit
→push
は一つのPRにまとめられます。
では、試しにもう一つ別のブランチ(Branch b)を作ってそこからPRを作成してみます。
すると、以下の画面のようになります。
(わかりやすいようにPR名を編集してあります)
最初に作成したブランチと今回作成したブランチで別のPRになっています。
PRはブランチ単位で作成される
GitHubにプッシュしたコミットを取り消す方法とその種類を解説
それでは本題です。プッシュ済みのコミットをどうやったらGitHub上から取り消すことが出来るのか。
取り消し方は2種類あります。
- 強制プッシュ
- 新しいコミットで取り消す
順番に説明していきます。
1. 強制プッシュ
まずは強制プッシュから説明します。今回僕が実際に使用した方法です。
ローカルリポジトリの状態が遅れている時、通常はプルをして、リモートリポジトリの最新の状態を取り込んでからでないとプッシュできません。
ただ、他のメンバーがまだプルをしていない状態や、1人で使っているリポジトリなどで、間違えてプッシュをした内容を書き換えても問題がない場合に、強制プッシュという方法があります。
では説明していきます。まずはGitHubの画面を確認してみましょう。
Commitsにそのブランチでプッシュされたコミットが古い順にならんでいます。
例えば、取り消したいコミットがあったとして、まずは、コマンドライン上で、コミットをリセット(なかったことに) します。
git reset HEAD^ #HEAD^は直近のコミットを指す
上のコマンドを使用しコマンドライン上でリセットすると、ローカルリポジトリでそのコミットがなかったことになります。
下の図のようなイメージです。
見てわかるように、リモートリポジトリとの間に差(履歴が遅れている状態)が生まれることになります。(まだこの段階ではローカルリポジトリでリセットした状態はリモートリポジトリには反映されません。)
push
はローカルリポジトリの変更をリモートリポジトリへ反映させる(履歴を進める)コマンドなので、
git push
すると、
git push
To https://github.com/ShoyaMiyata/GitGit.git
! [rejected] branchA -> branchA (non-fast-forward)
error: failed to push some refs to 'https://github.com/ShoyaMiyata/GitGit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
「リモートリポジトリの状態よりも遅れてるから、まずはリモートリポジトリの状態を受け入れてちょうだい」となるわけです。でも今回はローカルリポジトリで意図的にコミットを取り消して作った状況であり、リモートリポジトリにも反映させたいので、
git push -f
git push
に-f(--force)
オプションを付けることで強制的にリモートリポジトリに変更を反映させることができます。
つまり、「うっせぇだまれです。黙って従うです。」 ということができます。
実際に実行して確認してみると、
晴れて、プッシュしたコミットを取り消すことができました。
今回はあくまでPR段階で、まだ誰にもプルされていない状態だったので強制プッシュで問題なく対応できました。
他の開発者と協力して作業している場合、その影響をしっかり理解してから行うことが大事になります。
2. 新しいコミットで取り消す
先ほどの強制プッシュとは異なり、今度は新しいコミットで前のコミットをなかったことにする方法です。
今回使うのが git revert
というコマンドです。
強制プッシュで使用したgit reset
は指定したコミットまで戻すことで、作業をリセットしましたが、今回のgit revert
は既存のコミットを取り消す新しいコミットを作成することで、履歴を安全に修正できます。
①直近のコミットを取り消す場合は
git revert HEAD^
②遡って取り消したい場合は、git log
でコミットログを確認して、なかったことにしたいコミットのハッシュを取得して、
git log
commit 573eff4904eefc0177cdbcda1ffcf3a6266addbf # 例
Author: Shoya Miyata
Date: Sat Nov 30 11:10:29 2024 +0900
git revert 573eff4904eefc0177cdbcda1ffcf3a6266addbf
します。その状態で
git push
すると、GitHubの画面上では
頭にRevertと付いたコミットが追加で作成されています。
これならば、リモートリポジトリの履歴を保持できるので、他の人が前のコミットをプルしていた場合でも、Revertコミットをプルしてもらうことでミスを修正できます。
こんなイメージで使い分けが出来そうです。
一応今回は、別PRとして改めてコミットを出しなおすために、強制プッシュでコミットを取り消し、別のブランチで再度コミットをプッシュすることで対応できました。(もっとスマートな方法あればぜひ教えてください!)
チェリーピックという方法🍒
先ほど別PRを出すために、コミットを取り消してから、再度同じ内容をプッシュするという方法を取りましたが、
チェリーピックという方法を使えば、特定のコミットを選んで現在のブランチに適用することができます。 この「チェリーピック (cherry-pick)」という言葉は、さくらんぼ狩りのように、自分が欲しいものだけを選び取る行為から由来しているそうです🍒
たとえば、BranchAのコミットを別PRとして出したいとします。
今回はBranchCで受け入れていきましょう。
そしたら、ターミナル上でBranchCに移動してgit log --all
します。
git log --all
--all
オプションを付けることで、現在チェックアウトしていないブランチのコミット履歴も表示できます。(通常は現在のブランチのログのみが表示される)
git log --all
commit 295fa2b3737094248028ee4176940d1ba58d9ffb (refs/stash)
Merge: 7067c60 01a1306
Author: Shoya Miyata
Date: Sun Dec 1 14:17:57 2024 +0900
WIP on branchc: 7067c60 クラス作成
commit c200db35fe319da09bb657dc34774fb33cfc2347 (branch-A)
Author: Shoya Miyata
Date: Sat Nov 30 20:29:23 2024 +0900
別PRで出して
対象のログを確認できたので、git cherry-pick
をしていきましょう。
git cherry-pick <適用したいコミットのハッシュ>
git cherry-pick c200db35fe319da09bb657dc34774fb33cfc234 # 今回はこのハッシュ
問題なくコミットを受け入れることが出来たら、git push
をします。
BranchCを確認してみると、
無事にBranchAのコミットをBranchCに反映させられました。
この時、もともとのBranchAにはコミットが残っています。(必要ない場合は先ほど解説した強制プッシュを使うなどして、取り消す対応が必要です。)
今回はミスのコミットを別PRへ移すためにcherry-pick
を使いましたが、他のブランチのコミットを一部反映させたいというときにも使えそうです。
ブランチ移動の際にエラーが発生して困った場合は、こちらの記事もぜひご覧ください。
最後に
調べてインプットしたことを記事としてまとめる作業はかなり骨が折れますが、何度も、何度も自分で読み返して、間違いがないかを確認したり、どうやって構成したら伝わりやすいかを考え続けるうちに、最初は「わからない」がきっかけで調べ始めたことも気づけば、「自分のもの」になった感覚があります。
良質なインプットのためのアウトプット
良質なアウトプットのためのインプット
このサイクルをこれからも続けていきたいです!
最後までお読みいただきありがとうございました!