LoginSignup
9
8

More than 3 years have passed since last update.

【やらかし別】commit履歴を整理するためのGitコマンド

Last updated at Posted at 2020-06-25

ケース別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

これで終わり!!!
お疲れ様!!

(参考記事:rebase -i でコミットを修正する)

ケース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割を網羅した英単語帳です。
よろしければぜひ!

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8