依存関係のあるブランチをうまくPRに分けるGit操作方法
導入
解説すること
- 変更済みファイルやコンフリクトが起きている時にブランチを移動する手順 (
git stash) - コミットしたファイルを変更済みファイル(
git addされた)状態に戻す手順(git revert) - ブランチAで作業中にブランチAに干渉するような変更が
mainにマージされた時の対応手順(git merge)- 現在コミットしているファイル(現時点で
git push origin ~した時にpushされるファイル)一覧の確認手順(git diff --name-only)
- 現在コミットしているファイル(現時点で
何となくどんなことするかのエスパー用に使うコマンド達を適当に載せます
merge, stash {show, diff, apply, drop}, fetch origin pull/~/head:pr-~, revert
git stashについて
まず、mainブランチからブランチAを切ります。
このブランチAで作業をしていた時に緊急のタスクが依頼されたとします。
ここで緊急タスクに対処するべくブランチBを切ります。
ここでmainに移動してからブランチBを切ろうとしますが、ブランチAで変更したファイルがmainのファイルに干渉していて移動できないとします。
そんな時に使えるコマンドがgit stashです。
ちなみにstashには「こっそり隠す、(後で使うために)しまっておく」というような意味があります。
その意味の通り、ひとまず変更したファイル達をしまっておいて後で復元するといったことができます。
今回のようなmainに移れない、と言った状況に限らずブランチを切り替えたいのにコミットしたくないという状況全般で使えるコマンドですね。
よく使うオプションを載せます。
# 変更を退避
git stash
# 退避した変更を戻す(最新のstashを適用して削除)
git stash pop
# 退避した変更を戻す(削除しない)
git stash apply
# stashの一覧を見る
git stash list
# 特定のstashを削除
git stash drop stash@{0}
# 全部消す
git stash clear
個人的にはgit stash applyがおすすめですが、stashを削除するのを忘れないようにしましょう。
また、git stash listでは新しいものが上に積み重なっていきますので{n}の番号が0に近いほど新しいです。stackのようなイメージです。
git stash dropは単体で打つと一番新しいものが削除されます。
stashが溜まっていくとどれがどの状態か分かりづらくなることがあります。
そんな時はstashに名前をつけることができます。
今述べたことをまとめておさらいします。
# 名前をつけてstashを作成
git stash push -m "ログイン処理"
git stash push -m "プロフィール画面"
git stash push -m "エラーハンドリング"
→
→
見ての通り上に積み重なっていますね。
上に行くほど新しいので
$ git stash list
stash@{0}: On main: WIP: API エラーハンドリング
stash@{1}: On main: WIP: プロフィール画面のレイアウト修正
stash@{2}: On main: WIP: ログイン処理
となります。
さて、これでmainブランチに移動してブランチBを切ることができました。
流れの一例
- ブランチAで
git stashをして変更を一時保存 git checkout mainでmainブランチへ移動git checkout -b bでブランチBを作成し移動- ブランチBで作業が終わったらブランチAに移動して
git stash apply(もしくはpop)をして一時保存しておいた変更ファイル達を手元に戻す - ブランチAで残りの作業をする
git revertについて
ここではすでに作成したマージされていないPRに含まれるファイルをコミット前、つまりステージング状態に戻す手順を解説します。
例として以下のような状況を考えましょう。
まず、mainブランチからブランチAを切ります。
ここであなたはmainに移れずブランチAからブランチBを切ったとしましょう。
ブランチBで作業を終えたあなたはpushをしてPRを作成しました。
しかしここでgit stashの存在に気づいたあなたはmainから切り直すため、closeしてPRを閉じます。
あなたはmainに移ってブランチBを切りなおしました。
ブランチを切り直したあなたは気を取り直して再度PRを作成するべくファイルを再度ステージング(git add)しようとしますが、先程PRを作成してcloseしてしまっているため変更されたファイル群に含まれていません。
こんな時に役立つのがgit revertです。
コミットにはそれぞれユニークな番号が付いているため、そのコミット番号を指定してあげることで該当するコミットを打ち消すコミットを今いるブランチに作り出し、実質的に元の状態に戻すことができます(Git Graphという拡張機能が便利なのでよければ試してみてください)。
ブランチAから切ったブランチBでのコミット番号が123daf4dfa528だったとします。
そしたら今いるmainから作り直したブランチBで以下のようにコマンドを打ちます。
git revert 123daf4dfa528
すると123daf4dfa528に対応したコミットを打ち消すような新しいコミットが今いるブランチに作成されます。
あとは戻したいファイルを未ステージング状態に戻して:wqで閉じましょう。
→
これで新しいブランチBにすでにコミット済みのファイル群の変更を持ってこれました。
git mergeについて
さて、ここまでで変更内容を新しいブランチBに取り込んでPRを作り直しマージすることができました。
しかしこのPRにはブランチAにも影響するファイル変更が含まれていました。
つまり次はブランチAでコンフリクトが起き、pushできない状態です。
そんな時はgit mergeの出番です。
今回の場合はmainブランチの状態をブランチAに持ってくるので、ブランチAで以下のコマンドを実行します。
git merge main
この時持ってこられるmainはローカルのmainなので、上記のコマンドを打つ前には最新状態にしておく必要があります。
git checkout main
git fetch
git pull origin main
git checkout a
git merge main
これでmainの状態をブランチAに持ってくることができました。
この状態でcommitをするとブランチBで修正したファイルがPRに含まれてしまうように思えますが、直前に最新状態のmainを持ってきているので差分が存在せず、PRに含まれません。
ちなみにgit pushをした時に送られるファイル(つまりcommit済みのファイル)の一覧は
git diff origin/<ブランチ名>..HEAD --name-only
で確認できます。
終わりに
AIに頼むと見たことのないコマンドの実行許可を求められたりしますが、gitは極めれば何でもできます。
全てわかるようになりたいな。
git cherry-pickという便利なコマンドを最近知りました。簡単にいうと複数のコミットから欲しいもののみを選んで持ってこれるというもの。その様がさくらんぼの形に似ているからこう名付けられたのかな? よければこれも調べてみてください。