日本語 ▾ トピック ▾ 最新バージョン ▾ git-reset は 2.39.3 で最終更新されました

名前

git-reset - 現在の HEAD を指定された状態にリセットする

概要

git reset [-q] [<tree-ish>] [--] <pathspec>…​
git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>…​]
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]

説明

最初の3つの形式では、<tree-ish> からインデックスにエントリをコピーします。最後の形式では、現在のブランチヘッド (HEAD) を <commit> に設定し、オプションでインデックスと作業ツリーを一致させるように変更します。<tree-ish>/<commit> は、すべての形式でデフォルトで HEAD になります。

git reset [-q] [<tree-ish>] [--] <pathspec>…​
git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]

これらの形式は、<pathspec> に一致するすべてのパスのインデックスエントリを、<tree-ish> における状態にリセットします。(作業ツリーや現在のブランチには影響しません。)

これは、git reset <pathspec>git add <pathspec> の逆であることを意味します。このコマンドは git restore [--source=<tree-ish>] --staged <pathspec>... と等価です。

インデックスエントリを更新するために git reset <pathspec> を実行した後、git-restore[1] を使用して、インデックスの内容を作業ツリーにチェックアウトできます。あるいは、git-restore[1] を使用し、--source でコミットを指定することで、コミット内のパスの内容をインデックスと作業ツリーに一度にコピーできます。

git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>…​]

インデックスと <tree-ish> (デフォルトは HEAD) の間の差分でハンクを対話的に選択します。選択されたハンクは、インデックスに逆向きに適用されます。

これは、git reset -pgit add -p の逆、つまり、ハンクを選択的にリセットするために使用できることを意味します。--patch モードの操作方法については、git-add[1] の「対話モード」セクションを参照してください。

git reset [<mode>] [<commit>]

この形式は、現在のブランチヘッドを <commit> にリセットし、<mode> に応じてインデックス (<commit> のツリーにリセットする) および作業ツリーを更新する可能性があります。操作前に、ORIG_HEAD は現在のブランチの先端に設定されます。<mode> が省略された場合、デフォルトは --mixed です。<mode> は以下のいずれかでなければなりません

--soft

インデックスファイルや作業ツリーにはまったく触れません (ただし、すべてのモードと同様に、ヘッドを <commit> にリセットします)。これにより、変更されたすべてのファイルは git status が示すように「コミットされる変更」として残されます。

--mixed

インデックスをリセットしますが、作業ツリーはリセットしません (つまり、変更されたファイルは保持されますが、コミットの対象とはマークされません)。また、更新されなかったものを報告します。これがデフォルトのアクションです。

-N が指定された場合、削除されたパスは「追加する意図」としてマークされます (git-add[1] を参照)。

--hard

インデックスと作業ツリーをリセットします。<commit> 以降の作業ツリー内の追跡対象ファイルの変更はすべて破棄されます。追跡対象ファイルを書き込む際に邪魔になる untracked ファイルやディレクトリは単純に削除されます。

--merge

インデックスをリセットし、<commit>HEAD の間で異なる作業ツリー内のファイルを更新しますが、インデックスと作業ツリーの間で異なるファイル (つまり、まだ追加されていない変更があるファイル) は保持します。<commit> とインデックスの間で異なるファイルにステージングされていない変更がある場合、リセットは中止されます。

言い換えれば、--mergegit read-tree -u -m <commit> のようなことを行いますが、未マージのインデックスエントリは引き継ぎます。

--keep

インデックスエントリをリセットし、<commit>HEAD の間で異なる作業ツリー内のファイルを更新します。<commit>HEAD の間で異なるファイルにローカルな変更がある場合、リセットは中止されます。

--[no-]recurse-submodules

作業ツリーが更新される際、--recurse-submodules を使用すると、すべての有効なサブモジュールの作業ツリーもスーパープロジェクトに記録されたコミットに従って再帰的にリセットされ、そのコミットでサブモジュールの HEAD もデタッチされた状態になります。

これら3つのコマンドの違いについては、git[1] の「リセット、リストア、リバート」を参照してください。

オプション

-q
--quiet

静かに実行し、エラーのみを報告します。

--refresh
--no-refresh

混合リセット後にインデックスを更新します。デフォルトで有効です。

--pathspec-from-file=<file>

パススペックはコマンドライン引数の代わりに <file> で渡されます。<file> が正確に - の場合、標準入力が使用されます。パススペック要素は LF または CR/LF で区切られます。パススペック要素は、設定変数 core.quotePath (git-config[1] を参照) で説明されているようにクォートできます。--pathspec-file-nul およびグローバルな --literal-pathspecs も参照してください。

--pathspec-file-nul

--pathspec-from-file と共にのみ意味を持ちます。パススペック要素は NUL 文字で区切られ、他のすべての文字はリテラルに解釈されます (改行やクォートを含む)。

--

これ以上の引数をオプションとして解釈しません。

<pathspec>…​

操作の影響を受けるパスを制限します。

詳細については、gitglossary[7]pathspec エントリを参照してください。

add の取り消し
$ edit                                     (1)
$ git add frotz.c filfre.c
$ mailx                                    (2)
$ git reset                                (3)
$ git pull git://info.example.com/ nitfol  (4)
  1. 作業中に、これらのファイルの変更が良い状態にあることに気づきました。他のファイルや変更に取り組む予定であり、これらのファイルの変更が邪魔になるため、git diff を実行したときにそれらを見たくありません。

  2. 誰かがプルを要求し、その変更がマージに値すると判断しました。

  3. しかし、すでにインデックスを汚してしまっています (つまり、インデックスが HEAD コミットと一致していません)。ただし、これから行うプルが frotz.c または filfre.c に影響しないことが分かっているので、これらの2つのファイルのインデックス変更を元に戻します。作業ツリー内の変更はそのまま残ります。

  4. その後、プルしてマージし、frotz.cfilfre.c の変更は引き続き作業ツリーに残すことができます。

コミットを取り消し、やり直す
$ git commit ...
$ git reset --soft HEAD^      (1)
$ edit                        (2)
$ git commit -a -c ORIG_HEAD  (3)
  1. これは、コミットしたばかりのものが不完全だった、コミットメッセージのスペルを間違えた、またはその両方であることに気づいた場合によく行われます。作業ツリーは「リセット」前の状態のままになります。

  2. 作業ツリーファイルを修正します。

  3. 「reset」は古いヘッドを .git/ORIG_HEAD にコピーします。そのログメッセージから開始してコミットをやり直します。メッセージをさらに編集する必要がない場合は、代わりに -C オプションを指定できます。

    git-commit[1]--amend オプションも参照してください。

コミットを取り消し、トピックブランチにする
$ git branch topic/wip          (1)
$ git reset --hard HEAD~3       (2)
$ git switch topic/wip          (3)
  1. いくつかのコミットをしましたが、それらが master ブランチに入れるには時期尚早であることに気づきました。それらをトピックブランチで磨き続けたいので、現在の HEAD から topic/wip ブランチを作成します。

  2. それらの3つのコミットを取り除くために、masterブランチを巻き戻します。

  3. topic/wip ブランチに切り替えて作業を続けます。

コミットを完全に元に戻す
$ git commit ...
$ git reset --hard HEAD~3   (1)
  1. 最後の3つのコミット (HEADHEAD^、および HEAD~2) は悪く、二度と見たくありません。これらのコミットをすでに他の人に渡している場合は、これを行わないでください。(これを行うことの意味については、git-rebase[1] の「アップストリームのリベースからの回復」セクションを参照してください。)

マージまたはプルの取り消し
$ git pull                         (1)
Auto-merging nitfol
CONFLICT (content): Merge conflict in nitfol
Automatic merge failed; fix conflicts and then commit the result.
$ git reset --hard                 (2)
$ git pull . topic/branch          (3)
Updating from 41223... to 13134...
Fast-forward
$ git reset --hard ORIG_HEAD       (4)
  1. アップストリームからの更新を試みた結果、多くの競合が発生しました。今すぐマージに多くの時間を費やす準備ができていなかったので、後でそれを行うことにしました。

  2. 「pull」はマージコミットを作成していないので、git reset --hard (git reset --hard HEAD の同義語) はインデックスファイルと作業ツリーの混乱を解消します。

  3. トピックブランチを現在のブランチにマージし、早送りマージになりました。

  4. しかし、そのトピックブランチはまだ一般公開する準備ができていないと判断しました。「pull」または「merge」は常に現在のブランチの元の先端を ORIG_HEAD に残すため、そこへハードリセットすることで、インデックスファイルと作業ツリーはその状態に戻り、ブランチの先端もそのコミットにリセットされます。

ダーティな作業ツリー内でのマージまたはプルの取り消し
$ git pull                         (1)
Auto-merging nitfol
Merge made by recursive.
 nitfol                |   20 +++++----
 ...
$ git reset --merge ORIG_HEAD      (2)
  1. 作業ツリーにローカルな修正がある場合でも、他のブランチの変更がそれらと重複しないことが分かっている場合は、安全に git pull と言うことができます。

  2. マージの結果を検査した後、他のブランチの変更が満足のいくものではないと判断するかもしれません。git reset --hard ORIG_HEAD を実行すると、元の状態に戻ることができますが、ローカルの変更が破棄されてしまいます。それは望ましくありません。git reset --merge はローカルの変更を保持します。

中断されたワークフロー

大規模な変更の途中で、緊急の修正依頼によって中断されたとします。作業ツリーのファイルはまだコミットできる状態ではありませんが、緊急のバグ修正のために別のブランチに移動する必要があります。

$ git switch feature  ;# you were working in "feature" branch and
$ work work work      ;# got interrupted
$ git commit -a -m "snapshot WIP"                 (1)
$ git switch master
$ fix fix fix
$ git commit ;# commit with real log
$ git switch feature
$ git reset --soft HEAD^ ;# go back to WIP state  (2)
$ git reset                                       (3)
  1. このコミットは破棄されるので、使い捨てのログメッセージで構いません。

  2. これにより、WIP コミットがコミット履歴から削除され、作業ツリーはスナップショットを作成する直前の状態に設定されます。

  3. この時点では、インデックスファイルには、snapshot WIP としてコミットしたすべての WIP 変更がまだ残っています。これにより、インデックスが更新され、WIP ファイルが未コミットとして表示されます。

    git-stash[1] も参照してください。

インデックス内の単一ファイルのリセット

ファイルがインデックスに追加されているが、後でコミットに追加したくないと判断したとします。git reset を使用して、変更を保持したままインデックスからファイルを削除できます。

$ git reset -- frotz.c                      (1)
$ git commit -m "Commit files in index"     (2)
$ git add frotz.c                           (3)
  1. これにより、ファイルはインデックスから削除されますが、作業ディレクトリには保持されます。

  2. これにより、インデックス内の他のすべての変更がコミットされます。

  3. ファイルを再びインデックスに追加します。

作業ツリーの変更を保持しつつ、一部の以前のコミットを破棄する

何か作業をしてコミットし、さらに少し作業を続けたが、作業ツリーにあるものは以前にコミットしたものとは関係のない別のブランチに入れるべきだと考えた場合を想定します。新しいブランチを開始し、作業ツリーの変更を保持したままリセットすることができます。

$ git tag start
$ git switch -c branch1
$ edit
$ git commit ...                            (1)
$ edit
$ git switch -c branch2                     (2)
$ git reset --keep start                    (3)
  1. これにより、branch1 で最初の編集がコミットされます。

  2. 理想的な世界では、branch2 を作成して切り替えたときに、以前のコミットが新しいトピックに属さないことに気づけたはずです (つまり、git switch -c branch2 start)。しかし、誰もが完璧ではありません。

  3. しかし、branch2 に切り替えた後に、reset --keep を使用して不要なコミットを削除できます。

コミットを複数のコミットに分割する

多数の論理的に分離された変更を作成し、それらをまとめてコミットしたとします。その後、各論理チャンクを独自のコミットに関連付ける方が良いと判断しました。git reset を使用してローカルファイルの内容を変更せずに履歴を巻き戻し、その後、git add -p を連続して使用して、各コミットに含めるハンクを対話的に選択し、git commit -c を使用してコミットメッセージを事前に入力できます。

$ git reset -N HEAD^                        (1)
$ git add -p                                (2)
$ git diff --cached                         (3)
$ git commit -c HEAD@{1}                    (4)
...                                         (5)
$ git add ...                               (6)
$ git diff --cached                         (7)
$ git commit ...                            (8)
  1. まず、元のコミットを削除するように履歴を1つ前のコミットにリセットしますが、作業ツリーはすべての変更を保持したままにします。-N は、HEAD で追加された新しいファイルが引き続きマークされ、git add -p がそれらを見つけることを保証します。

  2. 次に、git add -p 機能を使用して、差分ハンクを対話的に選択して追加します。これにより、各差分ハンクについて順に尋ねられ、「はい、これを含める」、「いいえ、これを含めない」、あるいは非常に強力な「編集」機能などの簡単なコマンドを使用できます。

  3. 含めるハンクに満足したら、git diff --cached を使用して最初のコミットのために準備された内容を確認する必要があります。これにより、インデックスに移動され、コミットされようとしているすべての変更が表示されます。

  4. 次に、インデックスに保存されている変更をコミットします。-c オプションは、最初のコミットで開始した元のメッセージからコミットメッセージを事前に入力することを指定します。これは再入力を避けるのに役立ちます。HEAD@{1} は、元のリセットコミットの前の HEAD があったコミット (1つ前の変更) の特殊な表記です。詳細については、git-reflog[1] を参照してください。他の有効なコミット参照も使用できます。

  5. 元のコードを任意の数のコミットに分割するために、手順2-4を複数回繰り返すことができます。

  6. これで多くの変更が個別のコミットに分割されたため、残りの未コミットの変更をすべて選択するために、git add のパッチモードを使用しなくなるかもしれません。

  7. もう一度、含めたいものがすべて含まれていることを確認してください。また、git diff が後でコミットすべき変更を何も表示しないことを確認したい場合もあります。

  8. そして最後に、最終コミットを作成します。

考察

以下の表は、

git reset --option target

ファイルの状態に応じて異なるリセットオプションで HEAD を別のコミット (target) にリセットした場合に何が起こるかを示します。

これらの表では、ABCD はファイルの異なる状態です。たとえば、最初の表の最初の行は、作業ツリーでファイルが状態 A、インデックスで状態 BHEAD で状態 C、ターゲットで状態 D である場合、git reset --soft target はファイルを作業ツリーで状態 A、インデックスで状態 B のままにし、HEAD (現在のブランチにいる場合はその先端) をターゲット (ファイルが状態 D にある) にリセット (つまり移動) することを示します。

working index HEAD target         working index HEAD
----------------------------------------------------
 A       B     C    D     --soft   A       B     D
			  --mixed  A       D     D
			  --hard   D       D     D
			  --merge (disallowed)
			  --keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
 A       B     C    C     --soft   A       B     C
			  --mixed  A       C     C
			  --hard   C       C     C
			  --merge (disallowed)
			  --keep   A       C     C
working index HEAD target         working index HEAD
----------------------------------------------------
 B       B     C    D     --soft   B       B     D
			  --mixed  B       D     D
			  --hard   D       D     D
			  --merge  D       D     D
			  --keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
 B       B     C    C     --soft   B       B     C
			  --mixed  B       C     C
			  --hard   C       C     C
			  --merge  C       C     C
			  --keep   B       C     C
working index HEAD target         working index HEAD
----------------------------------------------------
 B       C     C    D     --soft   B       C     D
			  --mixed  B       D     D
			  --hard   D       D     D
			  --merge (disallowed)
			  --keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
 B       C     C    C     --soft   B       C     C
			  --mixed  B       C     C
			  --hard   C       C     C
			  --merge  B       C     C
			  --keep   B       C     C

reset --merge は、競合したマージからリセットする場合に使用されることを意図しています。任意のマージ操作は、マージに関与する作業ツリーファイルが開始前にインデックスに対してローカルな変更を持たず、結果を作業ツリーに書き出すことを保証します。したがって、インデックスとターゲットの間、およびインデックスと作業ツリーの間にいくつかの違いが見られる場合、それは競合で失敗した後、マージ操作が残した状態からリセットしていないことを意味します。そのため、この場合は --merge オプションを許可しません。

reset --keep は、現在のブランチの最後のコミットの一部を削除しながら、作業ツリーの変更を保持する場合に使用されることを意図しています。削除したいコミットの変更と保持したい作業ツリーの変更の間に競合が発生する可能性がある場合、リセットは許可されません。そのため、作業ツリーと HEAD の間、および HEAD とターゲットの間に変更がある場合は許可されません。安全のため、未マージのエントリがある場合も許可されません。

以下の表は、未マージのエントリがある場合に何が起こるかを示します

working index HEAD target         working index HEAD
----------------------------------------------------
 X       U     A    B     --soft  (disallowed)
			  --mixed  X       B     B
			  --hard   B       B     B
			  --merge  B       B     B
			  --keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
 X       U     A    A     --soft  (disallowed)
			  --mixed  X       A     A
			  --hard   A       A     A
			  --merge  A       A     A
			  --keep  (disallowed)

X は任意の状態を意味し、U は未マージのインデックスを意味します。

GIT

git[1] スイートの一部

scroll-to-top