git-reset

コマンド名

「git-reset」:現在のヘッドを指定された状態にリセットします

概要

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>はすべての形式でデフォルトでヘッドとなります。

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> のツリーにリセット)とワークツリーを更新する可能性があります。<mode>を省略すると、デフォルトで--mixedになります。<mode> は次のいずれかである必要があります。

--soft

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

--mixed

インデックスをリセットしますが、ワークツリーはリセットしません(つまり、変更されたファイルは保持されますが、コミットのマークは付けられません)。更新されていないものをレポートします。これがデフォルトのアクションとなります。

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

--hard

インデックスとワークツリーをリセットします。<commit> 以降のワークツリー内の追跡ファイルへの変更はすべて破棄されます。

--merge

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

言い換えると、--mergegit read-tree -u -m <commit>のようなことをしますが、マージされていないインデックスエントリを転送します。

--keep

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

--[no-]recurse-submodules

ワークツリーが更新されると、「-recurse-submodules」を使用し、スーパープロジェクトに記録されたコミットに従い、すべてのアクティブなサブモジュールのワークツリーが再帰的にリセットされ、そのコミット時にサブモジュールのヘッドが切り離されるように設定されます。
3つのコマンドの違いについてはgit[1] の「リセット、復元、および元に戻す」を参照してください。

オプション

-q
--quiet
--no-quiet

クワイエットにし、エラーのみをレポートします。デフォルトの動作はreset.quiet 構成オプションによって設定されます。–quiet と–no-quiet はデフォルトの動作を上書きします。

--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」エントリを参照してください。

追加を元に戻す

$ 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. これはコミットした内容が不完全であることを思い出した場合、コミットメッセージのスペルを間違えた場合、またはその両方の場合に最もよく行われます。「reset」前の状態でワークツリーを残します。
  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つのコミットを取り除きます。
  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. このコミットはブロウンされるので、使い捨てのログメッセージでOKです。
  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 --cacheを使用して、最初のコミット用に何が準備されているかを確認する必要があります。 これは、インデックスに移動され、コミットされようとしているすべての変更を示しています。
  4. 次にインデックスに保存されている変更をコミットします。-cオプションは最初のコミットで開始したオリジナルメッセージからコミットメッセージを事前入力することを指定します。これは再入力を避けるのに役立ちます。HEAD@{1} はオリジナルリセットコミット(1つの変更前)の前にHEADが使用されていたコミットの特別な表記です。詳細については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(つまり、現在のブランチの末端にある場合はその末端)をtarget(Dにファイルがある場合)にリセットします。

working index HEAD target         working index HEAD
----------------------------------------------------
ABCD     --soft   ABD
--mixed  A       D     D
--hard   D       D     D
--merge (disallowed)
--keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
ABCC     --soft   ABC
--mixed  A       C     C
--hard   C       C     C
--merge (disallowed)
--keep   A       C     C
working index HEAD target         working index HEAD
----------------------------------------------------
BBCD     --soft   BBD
--mixed  B       D     D
--hard   D       D     D
--merge  D       D     D
--keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
BBCC     --soft BBC
--mixed  B       C     C
--hard   C       C     C
--merge  C       C     C
--keep   B       C     C
working index HEAD target         working index HEAD
----------------------------------------------------
BCC D     --soft   BCD
--mixed  B       D     D
--hard   D       D     D
--merge (disallowed)
--keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
BCCC     --soft   BCC
--mixed  B       C     C
--hard   C       C     C
--merge  B       C     C
--keep   B       C     C

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

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

次の表はマージされていないエントリがある場合に何が起こるかを示しています。

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

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

GIT

git[1]パッケージソフトの一部

git公式ドキュメント

reset