0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Git】git resetのhard, soft, mixedの違い

Posted at

はじめに

Gitを使っていると「コミットをやり直したい」「変更をなかったことにしたい」という場面がよくあります。そのときによく登場するのが
git resetコマンドです。

ただし、--hard--soft--mixed といったオプションがあり、違いが分かりにくい…という声も多いです。

この記事では、それぞれの違いを 「対象」「影響範囲」 という観点で整理し、実際のユースケースも交えて解説します。

git resetの基本

git resetは「HEAD(現在のコミット位置)」を動かし、場合によってはインデックス(ステージングエリア)やワークツリー(作業ディレクトリ)を書き換えるコマンドです。

影響する範囲は以下の3つ:

  • HEAD … どのコミットを指すか
  • Index(ステージングエリア) … add 済みの変更
  • Working Tree(作業ディレクトリ) … 実際のファイル

各オプションの違い

1. --softオプション

特徴
コミットだけ取り消して、変更はステージング済みの状態に残す。

  • HEAD:移動する
  • Index:そのまま
  • Working Tree:そのまま
実際に動かしてみる

前提
・コミットA → コミットBの順でコミットが存在する
・現在HEADはコミットBを指している
・コミットAにはtest01というファイルが新規登録されている
・コミットBにはtest02、test03の2ファイルが新規登録されている
・作業ディレクトリにはtest01への修正、ステージングエリアにはtest02への修正が存在する

1.コミットログを確認

$ git log --pretty=oneline --name-only -n 2
b0f8f3a26127122d63f935515c5353a6b2be3942 (HEAD -> main) add test02 and test03
gitreset/test02
gitreset/test03
a9a4c5c61e519e55ae0e57b6b99826ba8db3347f (origin/main) add test01
gitreset/test01

test01を追加しているコミットと、test02,03を追加しているコミットが存在することを確認できます
HEADはtest02,03を追加したコミットを参照しています

2.作業ディレクトリとステージングエリアの状況を確認する

$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   gitreset/test02

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:   gitreset/test01

作業ディレクトリにはtest01への修正、ステージングエリアにはtest02への修正が存在することが分かる

3.test01を登録したコミットまでリセットする

$ git reset --soft HEAD~1

4.リセット後の状態を確認する

$ git log --pretty=oneline --name-only -n 1
a9a4c5c61e519e55ae0e57b6b99826ba8db3347f (HEAD -> main, origin/main) add test01
gitreset/test01

HEADがtest01を追加したコミットを参照していることが分かる

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   gitreset/test02
        new file:   gitreset/test03

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:   gitreset/test01

リセット後もtest01とtest02が作業ディレクトリとステージングエリアに残っていることが分かります
二つ重要なポイントがあります
一つはtest03がステージングエリアにあることです。これはリセットしたコミットにある修正がステージングエリアに戻っていることが分かります
二つ目はtest02が修正ではなく新規ファイルとなっていることです
理由はシンプルでtest02を追加したコミットがリセットされてしまったため、現在の最新コミットにはtest02が存在しません
そのためステージングに残ったtest02は新規ファイルとして認識されているというわけです

2. --mixed(デフォルト)オプション

特徴
コミットを取り消し、変更は残るがステージングは解除される。

  • HEAD:移動する
  • Index:リセットされる(ステージング解除)
  • Working Tree:そのまま
実際に動かしてみる

前提
・コミットA → コミットBの順でコミットが存在する
・現在HEADはコミットBを指している
・コミットAにはtest01というファイルが新規登録されている
・コミットBにはtest02、test03の2ファイルが新規登録されている
・作業ディレクトリにはtest01への修正、ステージングエリアにはtest02への修正が存在する

1.コミットログを確認

$ git log --pretty=oneline --name-only -n 2
8cf22adbb793c86939148ae2c7e85bdbb57f48e9 (HEAD -> main) add test02 and test03
gitreset/test02
gitreset/test03
a9a4c5c61e519e55ae0e57b6b99826ba8db3347f (origin/main) add test01
gitreset/test01

test01を追加しているコミットと、test02,03を追加しているコミットが存在することを確認できます
HEADはtest02,03を追加したコミットを参照しています

2.作業ディレクトリとステージングエリアの状況を確認する

$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   gitreset/test02

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:   gitreset/test01

作業ディレクトリにはtest01への修正、ステージングエリアにはtest02,03への修正が存在することが分かる

3.test01を登録したコミットまでリセットする

$ git reset --mixed HEAD~1
Unstaged changes after reset:
M       gitreset/test01

4.リセット後の状態を確認する

$ git log --pretty=oneline --name-only -n 1
a9a4c5c61e519e55ae0e57b6b99826ba8db3347f (HEAD -> main, origin/main) add test01
gitreset/test01

HEADがtest01を追加したコミットを参照していることが分かる

$ git status
On branch main
Your branch is up to date with 'origin/main'.

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:   gitreset/test01

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        gitreset/test02
        gitreset/test03

no changes added to commit (use "git add" and/or "git commit -a")

リセット後、test01とtest02,03はステージングから外れていることが分かります。

3. --hardオプション

特徴
完全に指定コミット時点の状態に戻る(変更はすべて消える)。

  • HEAD:移動する
  • Index:リセットされる
  • Working Tree:リセットされる
実際に動かしてみる

前提
・コミットA → コミットBの順でコミットが存在する
・現在HEADはコミットBを指している
・コミットAにはtest01というファイルが新規登録されている
・コミットBにはtest02、test03の2ファイルが新規登録されている
・作業ディレクトリにはtest01への修正、ステージングエリアにはtest02への修正が存在する

3.コミットログを確認

$ git log --pretty=oneline --name-only -n 2
01ff6f0772e74a6a08ea16eb22b45a14ad892623 (HEAD -> main) add test02 and test03
gitreset/test02
gitreset/test03
a9a4c5c61e519e55ae0e57b6b99826ba8db3347f (origin/main) add test01
gitreset/test01

test01を追加しているコミットと、test02,03を追加しているコミットが存在することを確認できます
HEADはtest02,03を追加したコミットを参照しています

2.作業ディレクトリとステージングエリアの状況を確認する

$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   gitreset/test02

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:   gitreset/test01

作業ディレクトリにはtest01への修正、ステージングエリアにはtest02への修正が存在することが分かる

3.test01を登録したコミットまでリセットする

$ git reset --hard HEAD~1
HEAD is now at a9a4c5c add test01

4.リセット後の状態を確認する

$ git log --pretty=oneline --name-only -n 1
a9a4c5c61e519e55ae0e57b6b99826ba8db3347f (HEAD -> main, origin/main) add test01
gitreset/test01

HEADがtest01を追加したコミットを参照していることが分かる

$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

リセット後、全ての修正が作業ディレクトリとステージングから外れていることが分かります。
※ untracked fileの場合は作業ディレクトリに残る

まとめ

  • --soft:コミットだけ消す(変更はステージング済みのまま)
  • --mixed:コミットとステージングを消す(変更は残る)
  • --hard:すべて消す(コミットも変更も残らない)

git resetはとても強力ですが、特に--hardは慎重に使いましょう。
慣れるまでは--soft--mixedを使って「取り消したいけど変更は残す」やり方から試すのがおすすめです。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?