🐙

脱初心者!知ってるとちょっと便利なGitコマンド4選

2021/12/21に公開

この記事ではGit初心者向けに個人的に便利だなと感じたコマンド4つを紹介したいと思います。

  1. 【git log】コミットの履歴を確認したい
  2. 【git reset】コミットを取り消したい
  3. 【git stash】コミットせずにブランチを変えたい
  4. 【fixup】複数のコミットを一つにまとめたい

想定読者

ブランチを切って、コミットして、GitHubにプッシュして、マージして...といったGitの基本操作は知っているけど、その他の機能についてはあまりよく知らないという初心者

1. 【git log】コミットの履歴を確認したい

コマンド
git log --oneline

コミットの履歴を確認したいときのためのコマンドです。--onelineというオプションをつけると各コミットの情報を1行で表示してくれるので見やすくて便利です。
以下が実行例になります。

実行例
b81b2f9 (HEAD -> main) 3回目のコミット
7d99597 2回目のコミット
31ffb6d 初めてのコミット

「b81b2f9」「7d99597」「31ffb6d」といった文字列はハッシュと呼ばれるものです。これはコミットを識別するためのIDのようなもので、コミットの内容を後から変更したいときなど、様々な場面で必要になってきます。
個人的にはコミットの履歴を確認するというよりはハッシュの値を調べるためgit log --onelineを使うことが多いです。

もっとコミットの詳細な情報を見たいというときは--onelineを外しましょう。

コマンド
git log
実行例
commit b81b2f91ebca6294b94e39cddb0510ca7d76381e (HEAD -> main)
Author: knknk98 <hogehoge@gmail.com>
Date:   Sat Dec 18 22:05:29 2021 +0900

    3回目のコミット

commit 7d99597d392fd9137e072e8a167715782ea061b7
Author: knknk98 <hogehoge@gmail.com>
Date:   Sat Dec 18 22:05:18 2021 +0900

    2回目のコミット

commit 31ffb6d9c5cdb508236afddd2bca64da60789f16
Author: knknk98 <hogehoge@gmail.com>
Date:   Sat Dec 18 22:04:57 2021 +0900

    初めてのコミット

git logコマンドには他にも、表示するコミットを絞り込んだり表示形式を変えたりなど様々なオプションが存在します。もっと知りたいという方は以下のドキュメントをご覧ください。
https://git-scm.com/docs/git-log
https://git-scm.com/book/ja/v2/Git-の基本-コミット履歴の閲覧

2. 【git reset】コミットを取り消したい

「間違えてコミットしてしまった!取り消したい!」というとき、git resetコマンドを使うことができます。

直近のコミットを取り消すコマンド
git reset --soft HEAD^

git resetコマンドは上記のように

git reset --モード 取り消したいコミット 

という感じで使うことが多いです。
ここではモードと取り消すコミットの指定方法について説明します。

モード:soft/mixed/hard

モードというのは取り消す範囲のことで、soft < mixed < hardの順に範囲が大きくなります。[1]

具体例で説明すると、下図のようにtest.txtとhello.txtという2つのファイルがあり、「2回目のコミット」を取り消したいけどまだコミットしてない変更があるというシチュエーションを想像してください。

2回目のコミットを取り消したいけど、まだコミットしてない変更があるという状況

soft

--softを指定した場合、具体的にどう変わるのか見てみしょう。
git reset --soft HEAD^ 実行後にコミット履歴を見てみると「2回目のコミット」が消えていて、コミットの取り消しが成功していることがわかります。

reset実行後のlog
$ git log --oneline
bc2347c (HEAD -> main) 1回目のコミット

では2回目のコミットの内容(2回目のコミットですという文章)は消えてしまっているのでしょうか?ここでファイルの状態を見てみましょう。[2]

2回目のコミットでの変更分が「ステージされている変更」に戻り、コミットしてない変更はそのまま
「2回目のコミットです」という文章は消えずに「ステージされている変更」として残っています。また、まだコミットしてなかった変更である「こんにちは、世界!」という文章も残っています。

このように--softは「コミット」という動作だけを消してその内容は残しておくといったものになります。
コミット内容を取り消したいというより「直前のコミット内容をちょっと修正したいなあ」というときに便利そうですね。

mixed

--mixedを指定した場合の挙動は基本的には--softと同じです。違うのはまだコミットしてない変更がgit addされてる、つまりステージされてる状態のときです。
まずresetをする前にgit add .をして「こんにちは、世界!」を「ステージされている変更」にします。

「こんにちは、世界!」が「ステージされた変更」になっている
その後、git reset --mixed HEAD^を実行します。

どちらのファイルもただの「変更」になっている
--softの場合、test.txthello.txtも両方「ステージされている変更」になります。2回目のコミットでの変更分が「ステージされている変更」に戻り、コミットしてない変更はそのままの状態になるからです。
しかし--mixedの場合はどちらもステージされていない変更になっています。
このように--mixedはコミットの取り消しに加えて、ステージの取り消しもされるモードになります。ステージ取り消し=完全に内容が消えるのではなく、ステージされていない変更に戻るということですね。

ちなみに何もモードを指定せずにgit resetをした場合、モードはmixedになります。

hard

最後に--hardを指定した場合についてです。--soft--mixedではコミット履歴からコミットは消えるもののコミット内容は残っていました。そうではなく完全に消し去りたいというときはどうしたら良いのでしょうか?そんなときに使えるのが--hardです。
今までと同様にしてgit reset --hard HEAD^を実行すると、下図のように「2回目のコミットです」も「こんにちは、世界!」も残っていないことがわかります。

「2回目のコミットです」も「こんにちは、世界!」も残っていない
このように--hardを使えば2回目のコミット内容も作業中の内容も完全に消すことができます。
作業中の内容も消えてしまうという点が要注意です。全ての変更をコミットした後か、今作業中のものも一緒に取り消したいというときに使うようにしましょう。

まとめ

今までの内容をまとめると以下の表のようになります。

soft mixed hard
取り消したいコミット→ ステージされている変更 ステージ前の変更 消える
コミットしてない変更(ステージしてる)→ ステージされている変更 ステージ前の変更 消える
コミットしてない変更(ステージしてない)→ ステージ前の変更 ステージ前の変更 消える

なお、これらをより正確に表現するとHEAD・インデックス・作業ディレクトリが云々という感じになります。
その辺りの詳細を知りたい方はこちらのサイトが参考になると思います。
https://git-scm.com/docs/git-reset
https://git-scm.com/book/ja/v2/Git-のさまざまなツール-リセットコマンド詳説

取り消すコミットの指定方法

次に取り消すコミットの指定方法についてです。
よくある指定方法として「コミットハッシュで指定」「直近のコミットから何個前かで指定」の2つが挙げられます。
コミットハッシュで指定する場合、前項で紹介してgit logコマンドで取り消したいコミットのハッシュを調べて

コマンド
git reset --soft b81b2f9

のようにします。(b81b2f9のところに取り消したいコミットのハッシュ値を入れてください)
直近のコミットから何個前かで指定したい場合は以下のようにします。書き方は色々あるので好きなのを使えば良いと思います。

// 直近のコミット(どれも同じ意味)
HEAD^ 
HEAD~
@^

// 直近のコミットから3個目(どれも同じ意味)
HEAD^^^
HEAD~~~
@^^^
HEAD~{3}
HEAD~3

3. 【git stash】コミットせずにブランチを変えたい

チーム開発をしていると「作業途中だけど急遽別ブランチの作業をしないといけなくなった」ということがしばしばあると思います。
そんなときブランチを変えようとするとエラーが出てきてしまいます。

コミットせずにブランチを変えようとした際のエラー例
error: Your local changes to the following files would be overwritten by checkout:
	test.txt
Please commit your changes or stash them before you switch branches.
Aborting

しかしまだまだ作業途中だしコミットしたくない……。そんな時に役立つのがgit stashです。

git stash変更をコミットせず一時的に別の場所に保存(退避)しておいてくれるコマンドです。
それでは早速使ってみましょう。

今、test.txtというファイルがあり、test1というブランチで作業していたとします。ここでこの変更をコミットせずtest2というブランチに移動したいとします。

「test1ブランチだよ」をコミットせずにtest2ブランチに移動したい

作業内容を保存する

まずはtest1ブランチでの作業内容を保存します。

コマンド
git stash

このようなメッセージが出てきたら成功です。

実行例
Saved working directory and index state WIP on test1: 38ff9ee 1

git stashを実行すると作業内容が一時的に別の場所に保存され、test1ブランチは変更を加える前の状態に戻ります。

変更した部分が消えている
これでエラーがでなくなったのでgit checkoutでtest2ブランチに移動することができます。

コマンド
git checkout test2

作業内容を戻す

今度はtest1ブランチを元の状態に戻してみましょう。
まずgit checkout test1でtest1ブランチに戻ります。
そして保存している作業一覧を表示し、今回読み込みたいのがどの作業なのか確認します。

コマンド
git stash list
実行例
stash@{0}: WIP on test1: 38ff9ee 1
stash@{1}: WIP on hogehoge: 38ff9ee 1

今回読み込みたいのはstash@{0}の作業であることが確認できました。
固有の値であるコミットのハッシュと違い、保存した日時が新しい順にstash@{0},stash@{1},...となっていくため、新しく作業を保存したりすると番号が変わってしまう点に注意してください。

そして

コマンド
git stash apply stash@{0}

とすることで作業内容をtest1ブランチに戻すことができます。

作業内容が元に戻っている

もう元に戻したのでstashのリストから作業を消したいというときは

コマンド
git stash drop stash@{0}

で消すことができます。
あるいはgit stash apply stash@{0}の代わりに

コマンド
git stash pop stash@{0}

とすることで作業を戻す時に同時にstashリストから消すことができます。

git stashについてもっと知りたいという方はこちら↓
https://git-scm.com/docs/git-stash
https://git-scm.com/book/ja/v2/Git-のさまざまなツール-作業の隠しかたと消しかた

4. 【fixup】複数のコミットを一つにまとめたい

最後にfixupの紹介です。
皆さん、このような経験はないでしょうか?

  • コミットしてから実装し忘れに気づく
  • レビューで誤字脱字や細かいミスに気づく

その結果このような細かすぎるコミットを何度もしてしまった経験はないでしょうか?

a6c45df (HEAD -> test) fix typo
9dfe821 fix typo
4326240 fix typo
31ffb6d hogeを実装したと思ったらできてなかった
80b9cc6 hogeを実装した
7d99597 初めてのコミット

本来なら最初の「hogeを実装した」というコミットだけで済むところが、細かい見落としのせいでコミットが5つに分かれてしまっています。
このようにコミットがただの作業ログのようになってしまっていると、綺麗ではないですし後から見返しにくくなってしまいます。特定の実装だけ取り消して欲しいと言われたときも細かいミスでコミットが何個にも分かれてしまっていると面倒ですしね。

そこでfixupを使って複数のコミットを1つにまとめてみましょう。

既にコミットしているとき

まずはまとめたい変更が既にコミットしてあるときについてです。

例えば、上記の例の「hogeを実装した」というコミットに「hogeを実装したと思ったらできてなかった」というコミットをまとめようとします。

「hogeを実装した」の1個前のコミット(初めてのコミット) を指定して

git rebase -i 7d99597

とするとvimのエディタが開きます。

hint: Waiting for your editor to close the file... 
pick 80b9cc6 hogeを実装した
pick 31ffb6d hogeを実装したと思ったらできてなかった
pick 4326240 fix typo
pick 9dfe821 fix typo
pick a6c45df fix typo
~以下略~

そのままだと閲覧用になるのでキーボードの「i」をクリックして編集モードに切り替えます。
そして「hogeを実装したと思ったらできてなかった」の「pick」を「fixup」に変更し、「hogeを実装した」の直後に移動させます。(今回は最初から直後になっているのでそのままでOKです)

pick 80b9cc6 hogeを実装した
fixup 31ffb6d hogeを実装したと思ったらできてなかった
pick 4326240 fix typo
pick 9dfe821 fix typo
pick a6c45df fix typo
~以下略~

そして「esc」ボタンを押して編集モードを終了し、「:wq」と入力しエンターを押します。

実行例
Successfully rebased and updated refs/heads/test.

このようなメッセージがでてきたら成功です。(testはブランチ名)
それではgit logでコミット履歴を確認してみましょう。

実行例
a6c45df (HEAD -> test) fix typo
9dfe821 fix typo
4326240 fix typo
b6b1ea7 hogeを実装した
7d99597 初めてのコミット

「hogeを実装したと思ったらできてなかった」が「hogeを実装した」というコミットにまとめられていることがわかります。

これからコミットするとき

次にまとめたい変更をこれからコミットするときについてです。
前回同様「hogeを実装した」にコミットをまとめたいとします。
まずはgit logで「hogeを実装した」のハッシュを確認しましょう。

コマンド
git log --oneline
実行例
a6c45df (HEAD -> test) fix typo
9dfe821 fix typo
4326240 fix typo
31ffb6d hogeを実装したと思ったらできてなかった
80b9cc6 hogeを実装した
7d99597 初めてのコミット

「hogeを実装した」というコミットのハッシュが80b9cc6であることがわかりました。
次に現在の変更をコミットします。いつものようにgit add .をしたあとgit commit -m "コミットメッセージ"とする代わりに

git commit --fixup 80b9cc6

としてください。これで「fixup! hogeを実装した」というコミットが作成されました。

最後に「fixup! hogeを実装した」コミットを「hogeを実装した」コミットにまとめます。まとめたいコミットの1個前のコミット を指定して

コマンド
git rebase -i --autosquash 7d99597

とし、vimのエディタを開きます。

実行例
pick b6b1ea7 hogeを実装した
fixup 8330a5b fixup! hogeを実装した
pick 31ffb6d hogeを実装したと思ったらできてなかった
pick 4326240 fix typo
pick 9dfe821 fix typo
pick a6c45df fix typo

「fixup! hogeを実装した」の1番左がfixupになっており、「hogeを実装した」の直下に移動されていることを確認します。
万が一されていなかったら「既にコミットしているとき」と同様にしましょう。
「:wq」と入力しエンターを押します。

Successfully rebased and updated refs/heads/test.

このようなメッセージがでてきたら成功です。(testはブランチ名)

参考
https://chaika.hatenablog.com/entry/2019/02/25/170000

終わりに

本記事では「git log」「git reset」「git stash」「fixup」の4つを紹介しました。
Gitにはこれ以外にも様々なコマンドがあります。沢山のコマンドの使い方を覚えてより便利に開発を進めましょう!

脚注
  1. modeには他にもmergeやkeepがあります ↩︎

  2. 画像はVSCodeですが「git status」コマンド等でも確認できます ↩︎

Discussion