#ケース別commit履歴の整理法
正直な話、たった一人でプロダクトを開発していると、僕みたいな人間はcommitの粒度などに無頓着になってくるのですが、最近は開発したプロダクトのコードレビューをしていただく機会が多くなったこともあり、気をつけ始めています。
相手としても読みにくいだろうし、見せるこちらとしてもやはり恥ずかしいですもんね。。。
この記事は、ローカルのcommit履歴を整理するための手順をまとめたものです。
僕自身の備忘録でもあり、おそらく今後もよく見返すであろう手順をここにまとめています。
僕は驚くほど無知なので、もし何かより良い提案などございましたら、ぜひ教えていただければ幸いです。
(テンションおかしいのは無視してください)
##ケース1「あっ。直前のcommitを修正したい...」
やだーーー!!
fugas_controllerのhogeアクションとviewのhoge.html.erbを消して「delete fugas#hoge」とcommitしたのに、ルーティングの設定が残ったままじゃん!
このcommitを取り消そう。
commitを取り消すにしても、commitを間違えた歴史は残したくないから、revert
じゃなくて、reset
ですね。
(参考記事:Git commit 取り消したい )
indexとワーキングツリーはそのままでいいから、--hard
じゃなくて、--soft
ですね。
(参考記事:[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法 )
取り消すのは直前のcommitなので、HEAD^
ですね。
git reset --soft HEAD^
routes.rbを変更したから、indexに追加しよう。
git add config/routes.rb
変更はちゃんと全部indexに追加されてるかなぁ〜〜〜???
git status
で確認しよう。
git status
どれどれ〜??
modified: app/controllers/fugas_controller.rb
deleted: app/views/fugas/hoge.html.erb
modified: config/routes.rb
問題ないですね。
よし、commitしよう。
git commit -m "delete fuga#hoge" -m "fugaコントローラーのhogeアクションを削除した。"
完了!!
##ケース2「げっ!いくつか前のcommitを修正したい...」
やだーーー!!
fugas_(ry
それもいくつか前のcommitだなぁ...。修正しなきゃ〜...。
で?修正したいのは、どれくらい前のcommitだっけ?
git log
で確認や!
修正内容まで見なくても、コミットメッセージだけ表示してくれればわかるので、--oneline
つけとこう。
git log --oneline
どれどれ??
dc77776b (HEAD -> refactoring) modify footer
3fb04106 delete fugas#hoge
0764bcaf delete landing_page
29970ee1 fix pagenate bug
"delete fugas#hoge"は2つ前のcommitか。
了解了解。
q
を入力して離脱しよう。
さて、ここからがちょっと面倒くさい。
commit履歴が少し過去にまで遡るので、delete fugas#hoge
のコミットまでgit resetしてしまうと、modify footer
のコミットまで消えてしまいます。
だからここで使うコマンドは、git rebase -i <変更したいコミットの「1つ前」のハッシュ値>
です。
これを使えば、ピンポイントで特定のcommitの内容だけを修正できる。
注意すべきは、rebase -iで指定するのは、**「変更したいコミットの1つ前のコミットのハッシュ値」**だということ。
ここではつまり、delete landing_page
のハッシュ値になる。
だから打つべきコマンドは、下になる。
git rebase -i 0764bcaf
すると次の入力画面が現れる。
pick 3fb04106 delete fugas#hoge
pick dc77776b modify footer
# Rebase 0764bcaf..dc77776b onto 0764bcaf (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
さて、過去のcommitを書き換えるには、この画面の1行目のpick 3fb04106 delete fugas#hoge
を、edit 3fb04106 delete fugas#hoge
に書き換えなくてはならない。
しかし、初心者にとっては、ここが意外にも難所だったりします。
ここで表示される入力画面は、普段触る機会の少ない『Vim』と呼ばれるクセのあるエディターだからです。
Vimの使い方については、下の記事が詳しいです。
(参考記事:知識0から始めるVim講座)
読む暇もないという方は、これから僕が唱える呪文を順番に打ってください。
表示されたばかりの状態では文字入力はできないので、まずはi
のキーを叩くことで、文字入力できる状態にします。
叩いた?文字入力できるようになった?
では次に、1行目のpick 3fb04106 delete fugas#hoge
を、edit 3fb04106 delete fugas#hoge
に書き換えます。
書き換えられた?
次に、escキー
を押します。
押した?また文字入力できなくなった?
次に、:wq
を押して、保存して終了します。
終了した?
もし下の画面が出力されたら、とりあえず成功です。
Stopped at 3fb04106... delete fugas#hoge
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
そしたら、delete fugas#hoge
のコミットに追加したいファイルなり変更なりをstagingしましょう。
git add config/routes.rb
そしていつものようにcommmitするのですが、ここでちょっと注意。
オプションは--amend
にするのだ。
git commit --amend
commitした??
すると、またこんな感じでVimの画面が表示されたと思う。
delete fugas#hoge
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Thu Jun 25 15:06:15 2020 +0900
#
# interactive rebase in progress; onto 7339e063
# Last command done (1 command done):
# edit 3fb04106 delete fugas#hoge
# Next commands to do (2 remaining commands):
# pick dc77776b modify footer
とくにコミットメッセージなど変更するつもりがないなら、:wq
で編集画面を終了しましょう。
メッセージを修正したいなら、さっき紹介したi
から始まる手順を踏めばいい。
さて、これでcommitできたわけだけれど、ここで安心するのはまだ早い。
まだrebase作業は終わっていない。
最後に下のコマンドを入力しましょう。
git rebase --continue
これで終わり!!!
お疲れ様!!
##ケース3「あ〜。1つのファイルに複数の変更を加えてstagingしちゃったけど、変更はそれぞれ分けてcommitしたいな...」
とりあえず、git diff
でどのへんを変更したのか確認しよう。
もうデータはstagingしちゃったので、変更前との差分を見たいなら--staged
をつけよう。
(参考記事:git 差分を見る)
git diff --staged
どれどれ??
diff --git a/config/routes.rb b/config/routes.rb
index 02fcf8e4..ebcf4fa1 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -18,14 +18,14 @@ Rails.application.routes.draw do
#get "how_to_use", to: "static_pages#how_to_use"
#
- #get '/landing_page', to: 'static_pages#landing_page'
index 02fcf8e4..ebcf4fa1 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -18,14 +18,14 @@ Rails.application.routes.draw do
#get "how_to_use", to: "static_pages#how_to_use"
#
- #get '/landing_page', to: 'static_pages#landing_page'
なるほどね。
まずはconfig/routes.rbをindexから外さないとね。
indexからファイルをunstagingするには、git reset <ファイル名>
かgit rm --cached <ファイル名>
ですね。
(参考記事:git add の取り消し方法と、関連コマンドまとめ)
git reset config/routes
さて、unstagingしたこのファイル変更箇所の一部分だけをstagingし直すには...。
ヘェ〜、git add -p <ファイル名>
なんてあるのか。なるほど。
(参考記事:Git 変更のあるファイルの一部だけをコミットしたい。)
git add -p config/routes.rb
どれどれ?
diff --git a/config/routes.rb b/config/routes.rb
index 02fcf8e4..ebcf4fa1 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -18,14 +18,14 @@ Rails.application.routes.draw do
#get "how_to_use", to: "static_pages#how_to_use"
#
- #get '/landing_page', to: 'static_pages#landing_page'
+
get "/select_plan", to: "static_pages#select_plan"
get "/update", to: "static_pages#update"
get "/invitation_or_premium", to: "static_pages#invitation_or_premium"
- get "/new", to: "static_pages#new"
+
(1/1) Stage this hunk [y,n,q,a,d,s,e,?]?
hunk?
あぁ、stagingする単位のことか。
って、変更が全部1つにまとまっちゃってんじゃ〜ん!
変更を分割するには、s
を入力してenter。
(1/1) Stage this hunk [y,n,q,a,d,s,e,?]? s
Split into 2 hunks.
@@ -18,10 +18,10 @@
#get "how_to_use", to: "static_pages#how_to_use"
#
- #get '/landing_page', to: 'static_pages#landing_page'
+
get "/select_plan", to: "static_pages#select_plan"
get "/update", to: "static_pages#update"
get "/invitation_or_premium", to: "static_pages#invitation_or_premium"
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?
この変更をstagingしよう。
y
を入力して、enter。
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? y
@@ -22,10 +22,10 @@
get "/select_plan", to: "static_pages#select_plan"
get "/update", to: "static_pages#update"
get "/invitation_or_premium", to: "static_pages#invitation_or_premium"
- get "/new", to: "static_pages#new"
+
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?
この変更はあとでcommitするので、今はstagingしない。
n
を入力してスキップ!
OK、終了終了。
よし、これで1つのファイルの2つの変更のうち1つだけstagingできたぞ〜!
できてるよね??
不安なので、git status
でindexを確認しよう。
git status
どれどれ〜?
On branch fix_bug
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: config/routes.rb
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: config/routes.rb
よしよし。ちゃんとconfig/routesrbが分割でstagingされてる。
じゃあ、まずは今stagingしたファイルをcommitしよう。
git commit -m "delete landing_page_path" -m "ランディングページのルーティングを削除"
それから残りの変更もstagingして、
git add config/routes.rb
commit!
git commit -m "delete new_path" -m "static_pages#newのルーティングを削除"
終わり!!!
##ケース4「あぁ〜、いくつか前のcommit、粒度粗いし分割したい〜〜〜」
過去のcommitの分割は、「ケース1」「ケース2」「ケース3」を合わせた知識でできます。
rebase -i
で、過去のcommitに移動したあと、
そのcommitをgit reset --soft HEAD^
で取り消す。
その後は、すでにindexにある分割してcommitしたいファイルを、git reset <ファイル名>
でunstagingしたり、
「ケース3」で紹介した手順で、1つのファイルの複数の変更を分割してstagingして、何回かに分けてcommitするなどすれば、commitの粒度を細かくできます。
このときのcommitは、commit --amend
ではなく、commit -m "コミットメッセージ"
を使います。
最後に git rebase --continue
を打ってrebaseを終了し、
git log --oneline
で確認すれば、
きちんとcommitが分割されていることがわかります。
##宣伝
Terminalの英語が読めない僕とあなたのために、BooQsという学習の科学に基づいた英単語学習サービスを開発しています。
BooQsの中でもとくに人気のあるコンテンツは、NGSL(New General Service List)と呼ばれる、一般的な英文の9割を網羅した英単語帳です。
よろしければぜひ!
BooQs(ブックス) は、
— 挫折させない英単語サービス『BooQs』 (@BooQs_net) March 19, 2020
楽しく効率的に英単語を覚えられるサービスです。
現在、『基礎英単語』『学術英単語』『TOEIC英単語』『ビジネス英単語』を学ぶことができます。
ぜひご活用ください!!#BooQshttps://t.co/Ygvf3QeZKL