git reset — 付箋ごと戻す(--soft・--mixed・--hard)
git reset は、HEAD が乗っているブランチの付箋を、指定したコミットへ動かすコマンドです。付箋を動かしたあと、ステージと作業ツリーをどこまで付箋に合わせて戻すかを、--soft / --mixed / --hard の3つから選びます。この回では、前の回の checkout との違いから入り、3つのオプションの差を「3つの場所のどれが戻るか」で見ていきます。最後に、reset で消えたように見えるコミットを取り戻す方法と、許可画面での見分け方を扱います。
- 上のターミナルは、実際に打ったときと同じ出力です(Git は英語で答えます)
- 下の中で起きていることは、コマンドの結果、Git の中の何が変わったかを図にしたものです。丸がコミット、黒い札がブランチ、ピンクの札が HEAD です
- 右の説明をスクロールして読み進めると、左のターミナルでコマンドが1つずつ打たれます。
→キーでも1つずつ進みます(←で1つ戻ります。スマホでは「次へ」ボタン)。最後まで進んでから→を押すと次の区間へ移ります。黄色く光っているのが、そのコマンドで変わった場所です - どのブランチからも辿れなくなったコミットは、グラフに薄く(点線で)残ります
1. reset は付箋そのものを動かす — checkout との違い
前の回で見たとおり、git checkout は付箋を動かさず、HEAD を付け替えるコマンドでした。ブランチの付箋はそのまま残るので、何も失われません。
git reset は逆です。HEAD はブランチに乗ったままで、HEAD が乗っているブランチの付箋そのものを動かします。同じ「1つ前に戻る」でも、どの付箋が動くかがまったく違います。
| コマンド | 動く付箋 | ブランチの付箋 |
|---|---|---|
git checkout HEAD~1 | HEAD だけ(detached HEAD になる) | 元のコミットに残る |
git reset --hard HEAD~1 | HEAD が乗っているブランチ(HEAD も一緒についていく) | 1つ前のコミットへ動く |
checkout と reset — どの付箋が動くか
mainは「バターを追加」を指していて、HEAD はmainに乗っています。書きかけの変更はありません。ここから「1つ前に戻る」を2通りでやってみます。まず前の回の
checkoutです。HEAD だけ が1つ前のコミットへ移り、detached HEADになりました。mainの付箋は「バターを追加」に貼られたままです。$ git checkout HEAD~1 Note: switching to 'HEAD~1'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example: git switch -c <new-branch-name> HEAD is now at e5c0a62 パンを追加mainに乗り直すと、作業ツリーも「バターを追加」の中身に戻ります。checkout は付箋を1枚も動かしていません。$ git checkout main Previous HEAD position was e5c0a62 パンを追加 Switched to branch 'main'次は
resetです。HEAD はmainに乗ったまま、mainの付箋そのもの が1つ前へ動きました。作業ツリーの中身は、さっきの checkout と同じ「パンを追加」の時点です。$ git reset --hard HEAD~1 HEAD is now at e5c0a62 パンを追加mainから辿れるのは3つだけになりました。「バターを追加」はグラフに薄く残っています。どのブランチからも辿れなくなっただけで、コミットそのものはまだあります。$ git log --oneline e5c0a62 (HEAD -> main) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加
reset のあと、git log には「バターを追加」が出なくなりました。ただしコミットが削除されたわけではありません。どの付箋からも辿れなくなっただけです。取り戻し方は7節で扱います。
2. どこまで戻すか — --soft・--mixed・--hard
reset は、まず付箋を動かします。ここは3つとも同じです。違うのは、そのあとステージと作業ツリーも付箋に合わせて戻すかどうかです。
| オプション | コミット(付箋) | ステージ | 作業ツリー |
|---|---|---|---|
--soft | 戻る | そのまま | そのまま |
--mixed(何も付けないときの既定) | 戻る | 戻る | そのまま |
--hard | 戻る | 戻る | 戻る |
3つの場所の並び(コミット → ステージ → 作業ツリー)で、soft はコミットだけ、mixed はステージまで、hard は作業ツリーまで戻ると覚えてください。作業ツリーに手を出すのは --hard だけです。
3. --soft — 直前のコミットをやり直す
git reset --soft HEAD~1 は、付箋だけを1つ前へ戻し、ステージと作業ツリーはそのまま残す使い方です。直前のコミットの中身がステージに載った状態、つまり「コミットする直前」に戻ります。そこで足りなかった変更を足して、コミットし直します。
Claude Code も、過去の作業ログで git reset --soft HEAD~1 を10回打っています。コミットしたあとに入れ忘れや入れすぎに気づき、コミットを作り直すときの使い方です。
git reset --soft HEAD~1 — 直前のコミットをやり直す
「パンを追加」をコミットした直後に、ジャムも一緒に入れるはずだったと気づきました。このコミットを作り直します。
$ git log --oneline e5c0a62 (HEAD -> main) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加--softは 付箋だけ を動かします。mainは「卵を追加」に戻りましたが、ステージと作業ツリーには「パンを買う」が残っています。端末には何も出ません。$ git reset --soft HEAD~1「パンを買う」の変更が ステージに載ったまま です。コミットする直前の状態に戻った、ということです。
$ git status On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: todo.txt忘れていた1行を足します。
$ echo "ジャムを買う" >> todo.txtステージに載せます。
$ git add todo.txt作り直したコミットができました。親は「卵を追加」です。
$ git commit -m "パンとジャムを追加" [main 2d0559a] パンとジャムを追加 1 file changed, 2 insertions(+)履歴の上では、最初からこのコミットだったように見えます。元の「パンを追加」は、どのブランチからも辿れなくなって、グラフに薄く残っています。
$ git log --oneline 2d0559a (HEAD -> main) パンとジャムを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加
新しいコミットは、元の「パンを追加」を書き換えたものではありません。別の新しいコミットです。元のコミットは、付箋から外れてグラフに薄く残っています。直前のコミットを作り直すだけなら git commit --amend という書き方もあり、こちらも同じように「新しいコミットを作って付箋を付け替える」動きをします。
4. --mixed(既定)— ステージも戻す
オプションを付けずに git reset HEAD~1 と打つと、--mixed になります。付箋を動かし、ステージも付箋の先のコミットの中身に戻します。作業ツリーには触りません。
git reset HEAD~1(--mixed)— ステージも戻す
オプションを付けない
resetは--mixedです。mainが1つ前へ動き、ステージも その中身に戻りました。作業ツリーに残った「バターを買う」を、Unstaged changes after resetの行が知らせています。$ git reset HEAD~1 Unstaged changes after reset: M todo.txt今度は「バターを買う」が ステージに載っていない 変更として出ます。
--softとの違いはここだけです。$ git status On branch 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: todo.txt no changes added to commit (use "git add" and/or "git commit -a")作業ツリーとステージの差は「バターを買う」の1行です。書いた内容は何も失われていません。
$ git diff diff --git a/todo.txt b/todo.txt --- a/todo.txt +++ b/todo.txt +バターを買う
--soft との違いは、取り消したコミットの変更が「ステージに載っている」か「載っていない」かだけです。どちらも作業ツリーのファイルは消えません。--mixed で戻したあとは、git add をやり直して、必要な分だけコミットし直せます。
5. --hard — 作業ツリーまで戻す
--hard は、付箋を動かしたうえで、ステージと作業ツリーの両方を付箋の先のコミットの中身で上書きします。コミットしていない変更は、add 済みかどうかに関係なく消えます。
git reset --hard — 作業ツリーまで戻す
todo.txtに書きかけの「醤油を買う」があります。一度も add していないnotes.txtもあります。$ git status On branch 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: todo.txt Untracked files: (use "git add <file>..." to include in what will be committed) notes.txt no changes added to commit (use "git add" and/or "git commit -a")もう1つ、新しいファイル
recipe.txtを作ります。$ echo "じゃがいも 玉ねぎ 牛肉" > recipe.txtこちらは add だけしておきます。コミットはしていません。
$ git add recipe.txt--hardは付箋を動かし、ステージと作業ツリーの両方 をその中身で上書きします。HEAD is now atの行に、戻った先のコミットが出ます。$ git reset --hard HEAD~1 HEAD is now at e5c0a62 パンを追加「バターを買う」と一緒に、書きかけの「醤油を買う」も消えました。この書きかけは一度もコミットしていないので、もう取り戻せません。
$ cat todo.txt 牛乳を買う 卵を買う パンを買うnotes.txtは残っています。Git が追跡していないファイルには、reset --hardは手を出しません。一方、add したrecipe.txtは追跡中だったので、作業ツリーからも消えました。$ git status On branch main Untracked files: (use "git add <file>..." to include in what will be committed) notes.txt nothing added to commit but untracked files present (use "git add" to track)
この場面で起きたことを整理すると、次のとおりです。
| reset --hard の前の状態 | reset --hard のあと | 取り戻せるか |
|---|---|---|
| コミット「バターを追加」 | 付箋から外れる | 取り戻せる(7節の reflog) |
| 書きかけの「醤油を買う」(コミットしていない) | 消える | 取り戻せない |
add しただけの新しいファイル recipe.txt | 消える | ふつうの操作では取り戻せない(下の注) |
一度も add していないファイル notes.txt | そのまま残る | (消えていない) |
コミットしていない変更は、reset --hard で消えると reflog でも戻せません。reflog が覚えているのはコミットだけで、書きかけの変更はそこに載らないからです。逆に、Git が一度も追跡していないファイル(一度も add していないファイル)には手を出しません。ただし、add しただけの新しいファイルは「追跡中」なので消えます。
注: add した時点の中身は、Git の内部に部品として残っていることがあり、専門的な調べ方で拾える場合があります。ただし、ふつうのコマンドでは戻せないので、消えるものとして扱ってください。一度も add していない書きかけは、Git のどこにも記録されていないので、本当に戻せません。
コミットを書かずに git reset --hard と打つと、行き先は HEAD(今いるコミット)になります。付箋は動きませんが、ステージと作業ツリーは HEAD の中身に上書きされます。つまり「コミットしていない変更を、全ファイルまとめて捨てる」コマンドです。許可画面で見かけたら、書きかけが残っていないかを必ず確かめてください。
6. git reset HEAD ファイル — add を取り消す
reset の後ろにファイル名を書くと、働きが変わります。付箋は動かさず、そのファイルのステージだけを HEAD の中身に戻す、つまり add の取り消しです。作業ツリーのファイルには触りません。
Claude Code が打った reset のうち、いちばん多いのがこの形です(git reset -q HEAD ファイル が28回)。-q は「何も表示しない」という指定で、動きは変わりません。コミットに入れるつもりのないファイルまで add してしまったとき、そのファイルだけをステージから外すために打っています。
git reset HEAD ファイル — add を取り消す
todo.txtに1行足します。$ echo "ジャムを買う" >> todo.txtメモ用の
notes.txtも作ります。$ echo "特売は水曜" > notes.txt両方を add しました。ところが
notes.txtは、コミットに入れるつもりがなかったとします。$ git add todo.txt notes.txtresetの後ろにファイル名を書くと、付箋は動かさず、そのファイルの ステージだけ を HEAD の中身に戻します。-qは「何も表示しない」という指定です。$ git reset -q HEAD notes.txtnotes.txtはステージから外れ、追跡していないファイルに戻りました。作業ツリーのファイルはそのままです。$ git status On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: todo.txt Untracked files: (use "git add <file>..." to include in what will be committed) notes.txt同じことは
git restore --stagedでもできます。前の回で見た、add の取り消しの新しい書き方です。$ git restore --staged todo.txttodo.txtもステージから外れました。作業ツリーの「ジャムを買う」は残っています。$ git status On branch 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: todo.txt Untracked files: (use "git add <file>..." to include in what will be committed) notes.txt no changes added to commit (use "git add" and/or "git commit -a")ファイル名を書いたときに使えるのは
--mixedだけです。--hardや--softを付けると、Git は止まります。$ git reset --hard HEAD todo.txt fatal: Cannot do hard reset with paths.
前の回で見た git restore --staged ファイル は、この使い方を分けて名前を付けたコマンドです。ファイルを指定したときは、どちらも同じ結果になります。
| 書き方 | 付箋 | ステージ | 作業ツリー |
|---|---|---|---|
git reset HEAD ファイル(git reset ファイル も同じ) | 動かない | そのファイルだけ HEAD の中身に戻る | そのまま |
git restore --staged ファイル | 動かない | そのファイルだけ HEAD の中身に戻る | そのまま |
ファイル名を書いた reset で使えるのは --mixed だけです。--hard や --soft を付けると fatal: Cannot do hard reset with paths. のように止まります。1つのファイルを作業ツリーごと戻したいときは、前の回の git checkout HEAD -- ファイル か git restore --staged --worktree ファイル を使います。
7. 消えたように見えるコミットは reflog で取り戻せる
reset で付箋から外れたコミットは、git log には出なくなります。それでも、コミットそのものはまだリポジトリに残っています。行き先を知る手がかりが reflog です。reflog は、HEAD がこれまでに指してきたコミットを、新しい順に記録したものです。
HEAD@{0}は今いる場所、HEAD@{1}は1つ前にいた場所です- reset の直後なら、
HEAD@{1}が「reset する前にいたコミット」です git reset --hard HEAD@{1}で、付箋をそこへ戻せます
reflog で取り戻す — git reset --hard HEAD@{1}
「バターを買う」を1行足します。
$ echo "バターを買う" >> todo.txtコミットします。
mainは「バターを追加」を指しています。$ git commit -am "バターを追加" [main fbb4973] バターを追加 1 file changed, 1 insertion(+)1つだけ戻すつもりが、2つ戻してしまいました。
mainは「卵を追加」を指しています。$ git reset --hard HEAD~2 HEAD is now at 8b41d07 卵を追加git logには2つしか出ません。でもグラフには、「パンを追加」と「バターを追加」が薄く残っています。$ git log --oneline 8b41d07 (HEAD -> main) 卵を追加 3f2a9c1 牛乳を追加git reflogは HEAD が指してきたコミットの記録 です。HEAD@{0}が今いる場所、HEAD@{1}が1つ前にいた場所です。HEAD@{1}の行が、さっきのコミット「バターを追加」です。$ git reflog -2 8b41d07 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2 fbb4973 HEAD@{1}: commit: バターを追加HEAD@{1}へ付箋を戻します。mainは再び「バターを追加」を指しました。$ git reset --hard HEAD@{1} HEAD is now at fbb4973 バターを追加4つとも戻りました。reset で消えたように見えたコミットは、付箋が外れていただけ です。
$ git log --oneline fbb4973 (HEAD -> main) バターを追加 e5c0a62 パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加
reflog の行には「何をしてそこへ来たか」が書かれています。reset: moving to HEAD~2 は「reset で HEAD~2 へ動いた」、commit: バターを追加 は「コミットしてそこへ進んだ」という意味です。reset のあとに別の操作をすると番号がずれるので、HEAD@{1} と決め打ちせず、まず git reflog で行き先を確かめてから戻してください。前の回のように git branch 名前 HEAD@{1} で付箋を貼っても取り戻せます。
reflog が覚えているのは、HEAD が指したことのあるコミットだけです。一度もコミットしなかった書きかけの変更は、reflog には載りません。また、reflog の記録は永久ではなく、既定では一定期間(どこからも辿れないものは30日)を過ぎると、片付けのときに消されることがあります。取り戻すなら早めに、です。
8. Claude Code の許可画面で見かけたら
reset は、この講座で扱うコマンドの中でも、許可する前にいちばん注意して読みたいものです。分かれ目は--hard が付いているかどうかです。--soft と --mixed は作業ツリーに触らないので、書いたファイルは消えません。--hard は、コミットしていない変更をまとめて消し、その変更はどうやっても戻せません。
| コマンド | 何が起きるか | 消えうるもの |
|---|---|---|
git reflog / git log / git status | 見るだけ | なし |
git reset -q HEAD ファイル / git restore --staged ファイル | そのファイルの add を取り消す(作業ツリーはそのまま) | 注意 add した後にさらに書き換えていたら、add した時点の版(ステージにだけあった中身) |
git reset --soft HEAD~1 | 付箋だけを1つ戻す。変更はステージに残る | 注意 直前のコミットが付箋から外れる(reflog で取り戻せる) |
git reset HEAD~1 / git reset --mixed HEAD~1 | 付箋とステージを戻す。作業ツリーはそのまま | 注意 直前のコミットが付箋から外れる(reflog で取り戻せる)。ステージにだけあった中身 |
git reset --hard(行き先なし) | 付箋は動かさず、ステージと作業ツリーを HEAD の中身で上書き | 未コミットの変更すべて(add しただけの新しいファイルも) |
git reset --hard HEAD~1 / git reset --hard コミットID | 付箋を動かし、ステージと作業ツリーもその中身で上書き | 未コミットの変更すべて。外れたコミットは reflog で取り戻せる |
git reset --hard HEAD@{1} | 取り戻すための reset。中身は上と同じ --hard | 未コミットの変更すべて |
--hard の許可を求められたら、先に git status の結果を見てください。Changes not staged for commit や Changes to be committed に自分の書きかけが並んでいたら、それが消えるものです。残したいなら、先にコミットするか、--soft / --mixed で済まないかを Claude Code に聞き返します。一度も add していないファイル(Untracked files に出るもの)は、reset --hard では消えません。
次の回では、付箋を戻すのではなく「打ち消すコミットを足す」git revert を扱います。
まとめ
git resetは、HEAD が乗っているブランチの付箋そのものを動かす。checkout は付箋を動かさず、HEAD を付け替える--softはコミットだけ、--mixed(既定)はステージまで、--hardは作業ツリーまで戻すgit reset --soft HEAD~1は「直前のコミットをやり直す」使い方。変更はステージに残るgit reset HEAD ファイルは add の取り消しで、git restore --staged ファイルと同じ結果になるreset --hardはコミットしていない変更を消し、それは reflog でも戻せない。一度も add していないファイルは残る- 付箋から外れたコミットは消えていない。
git reflogで探してgit reset --hard HEAD@{1}で戻せる