おもしろい投稿git reset
の微妙な点について説明しました。
残念ながら、私がそれについて読むほど、私はそれを完全に理解していないように見えます。私はSVNのバックグラウンドから来ました、そしてGitはまったく新しいパラダイムです。私は簡単にMercurialを手に入れましたが、Gitはもっと技術的です。
git reset
はhg revert
に近いと思いますが、違いがあるようです。
それでgit reset
は正確に何をしますか?詳細な説明を含めてください。
--hard
、--soft
および--merge
。HEAD^
やHEAD~1
などのHEAD
name__で使用している奇妙な表記法。HEAD
name__、そしてあなたのグローバルなストレスレベルへの影響。一般に、git reset
の機能は、現在のブランチを取得し、それをリセットして別の場所を指すようにし、インデックスと作業ツリーを一緒に持ってくることです。より具体的には、マスターブランチ(現在チェックアウトされている)が次のような場合:
- A - B - C (HEAD, master)
マスターがCではなくBを指すようにしたい場合は、git reset B
を使用してそこに移動します。
- A - B (HEAD, master) # - C is still here, but there's no branch pointing to it anymore
余談:これはチェックアウトとは異なります。 git checkout B
を実行すると、次のようになります:
- A - B (HEAD) - C (master)
分離されたHEAD状態になりました。 HEAD
、作業ツリー、インデックスはすべてB
に一致しますが、マスターブランチはC
に残されました。この時点で新しいコミットD
を作成すると、これが得られますが、これはおそらく望んでいないことです。
- A - B - C (master)
\
D (HEAD)
リセットはコミットを行わず、別のコミットを指すようにブランチ(コミットへのポインター)を更新するだけです。残りは、インデックスと作業ツリーに何が起こるかの詳細です。
次のセクションのさまざまなオプションの説明の中で、git reset
の主なユースケースの多くを取り上げます。本当にさまざまなことに使用できます。共通のスレッドは、それらのすべてが、特定のコミットを指す/一致するようにブランチ、インデックス、および/またはワークツリーをリセットすることを伴うということです。
--hard
は、作業を本当に失う可能性があります。作業ツリーを変更します。
git reset [options] commit
は、コミットを(ある種の)失う可能性があります。上記のおもちゃの例では、コミットC
を失いました。まだリポジトリにあり、git reflog show HEAD
またはgit reflog show master
を調べることで見つけることができますが、実際にはどのブランチからもアクセスできません。
Gitはそのようなコミットを30日後に永久に削除しますが、それまではブランチを再度ポイントすることでCを回復できます(git checkout C; git branch <new branch name>
)。
Manページを言い換えると、最も一般的な使用法はgit reset [<commit>] [paths...]
の形式で、指定されたコミットから指定されたパスをその状態にリセットします。パスが提供されない場合、ツリー全体がリセットされ、コミットが提供されない場合、HEAD(現在のコミット)と見なされます。これはgitコマンド全体で共通のパターン(例:checkout、diff、log、正確なセマンティクスは異なります)であるため、それほど驚くべきことではありません。
たとえば、git reset other-branch path/to/foo
はpath/to/fooのすべてを他のブランチの状態にリセットし、git reset -- .
は現在のディレクトリをHEADの状態にリセットし、簡単なgit reset
はすべてをHEADの状態。
リセット中に作業ツリーとインデックスに何が起こるかを制御する4つの主なオプションがあります。
インデックスはgitの「ステージングエリア」であることに注意してください。コミットの準備中にgit add
と言うと、そこが物事の行き先です。
--hard
は、すべてをリセットしたコミットに一致させます。これはおそらく最も理解しやすいでしょう。ローカルの変更はすべて上書きされます。主な用途の1つは、作業を吹き飛ばすことですが、コミットを切り替えることではありません:git reset --hard
はgit reset --hard HEAD
を意味します。もう1つは、ブランチをある場所から別の場所に移動し、インデックス/作業ツリーの同期を維持することです。 これは、作業ツリーを変更するため、実際に作業を失う可能性があります。作業の前にローカルの作業を破棄することを非常に確信してください。 reset --hard
を実行します。
--mixed
がデフォルトです。つまり、git reset
はgit reset --mixed
を意味します。インデックスはリセットされますが、作業ツリーはリセットされません。これは、すべてのファイルが無傷であることを意味しますが、元のコミットとリセットしたコミットの違いは、gitステータスでローカルの変更(または追跡されていないファイル)として表示されます。いくつかの悪いコミットをしたことに気付いたときにこれを使用しますが、それを修正して再コミットできるように、行ったすべての作業を保持したい場合に使用します。コミットするには、ファイルをインデックスに再度追加する必要があります(git add ...
)。
--soft
は、インデックスまたは作業ツリーに触れません。すべてのファイルは--mixed
と同様にそのままですが、すべての変更はgitステータスでchanges to be committed
として表示されます(つまり、コミットの準備でチェックインされます)。いくつかの悪いコミットをしたことに気付いたときにこれを使用しますが、作業はすべて良いです-あなたがする必要があるのは、それを別の方法で再コミットすることです。インデックスは変更されていないため、必要に応じてすぐにコミットできます。結果のコミットには、リセット前と同じ内容がすべて含まれます。
--merge
は最近追加されたもので、失敗したマージの中止を支援することを目的としています。これは、git merge
が実際には、変更によってマージの影響を受けないファイルにある限り、ダーティな作業ツリー(ローカルに変更されたもの)とのマージを試行できるためです。 git reset --merge
は、インデックスをリセットし(--mixed
-すべての変更がローカルの変更として表示される)、マージの影響を受けるファイルをリセットしますが、他のファイルはそのままにします。これにより、うまくいけば、すべてが不良マージ前の状態に復元されます。ブランチを実際に移動するのではなく、マージをリセットするだけでよいため、通常はgit reset --merge
(git reset --merge HEAD
を意味する)として使用します。 (HEAD
は、マージに失敗したため、まだ更新されていません)
具体的には、ファイルAとBを変更し、ファイルCとDを変更したブランチでマージしようとするとします。マージは何らかの理由で失敗し、中止することにしました。 git reset --merge
を使用します。 CとDをHEAD
の状態に戻しますが、AとBへの変更はマージの一部ではないため、変更はそのままにします。
man git reset
はこれに非常に適していると思います。おそらくgitが実際に沈み込むためにはgitの動作を少し理解する必要があります。特に、時間をかけて注意深く読んでいると、さまざまなオプションやケースのインデックスと作業ツリーにあるファイルの状態を詳細に示した表が非常に役立ちます。 (しかし、はい、それらは非常に高密度です-それらは非常に簡潔な形式で上記の情報の非常に多くを伝えています。)
あなたが言及する「奇妙な表記法」(HEAD^
およびHEAD~1
)は、3ebe3f6
のようなハッシュ名を使用することなく、コミットを指定するための単なる略記です。これは、git-rev-parseのmanページの "specifying revisions"セクション に完全に文書化されており、多くの例と関連構文があります。キャレットとチルダは、実際には 異なるもの を意味します。
HEAD~
はHEAD~1
の略で、コミットの最初の親を意味します。 HEAD~2
は、コミットの最初の親の最初の親を意味します。 HEAD~n
を「HEADの前にn回コミット」または「HEADの第n世代の祖先」と考えてください。HEAD^
(またはHEAD^1
)は、コミットの最初の親も意味します。 HEAD^2
は、コミットのsecond親を意味します。通常のマージコミットには2つの親があります。最初の親はマージ先コミットであり、2番目の親はマージされたコミットです。一般に、マージは実際に任意の数の親を持つことができます(タコマージ)。^
および~
演算子は、HEAD
、HEAD~3^2
、2番目の第3世代の祖先の2番目の親であるHEAD^^2
のように、一緒に連結できます。 HEAD
、またはHEAD^^^
と同等のHEAD~3
の最初の親の親。git
には以下のものがあります。
HEAD
ポインタ 、あなたが取り組んでいるコミットを教えてくれます詳細な説明を含めてください。
--hard
、--soft
および--merge
。
危険度の高いものから順に:
--soft
はHEAD
を動かしますが、ステージング領域や作業ツリーには触れません。--mixed
はHEAD
を移動してステージング領域を更新しますが、作業ツリーは更新しません。--merge
はHEAD
を移動し、ステージング領域をリセットし、そして作業ツリーのすべての変更を新しい作業ツリーに移動しようとします。--hard
はHEAD
と を移動してステージング領域と作業ツリーを新しいHEAD
に調整し、すべてを捨てます。具体的なユースケースとワークフロー
--soft
を使用してください。これが必要になることはほとんどありません。-
# git reset --soft example
touch foo // Add a file, make some changes.
git add foo //
git commit -m "bad commit message" // Commit... D'oh, that was a mistake!
git reset --soft HEAD^ // Go back one commit and fix things.
git commit -m "good commit" // There, now it's right.
-
別のコミットで状況を確認したいときに--mixed
(これがデフォルトです)を使用しますが、既に行った変更を失いたくはありません。
新しい場所に移動したいが、既に持っている変更を作業ツリーに取り込む場合は--merge
を使用してください。
--hard
を使用してすべてを消去し、新しいコミットで新しいスレートを開始します。
ブログ Pro Git の投稿 Reset Demystified は、git reset
とgit checkout
について非常に 非常に簡単な 説明を提供します。
この記事の冒頭にある有益な議論をすべて終えて、作者はルールを次の簡単な3つのステップにまとめました。
それは基本的にそれです。
reset
コマンドはこれら3つのツリーを特定の順序で上書きし、指示があると停止します。
- HEADが指すブランチを移動します(
--soft
の場合は停止します)。- それでは、インデックスをそのように見せてください(
--hard
以外はここで止めてください)- それで、作業ディレクトリをそのように見せてください
--merge
と--keep
オプションもありますが、今のところもっと単純にしておきたいと思います - それは別の記事のためになるでしょう。
Gitするために何かをコミットするとき、あなたは最初にあなたの変更をステージング(インデックスに追加)しなければなりません。これは、gitがそれらをコミットの一部と見なす前に、このコミットに含めたいすべてのファイルをgit addしなければならないことを意味します。まず、Gitリポジトリのイメージを見てみましょう。
だから、今は簡単です。作業ディレクトリで作業し、ファイル、ディレクトリ、その他すべてを作成する必要があります。これらの変更は追跡されない変更です。それらを追跡するには、git addコマンドを使用してそれらをgit indexに追加する必要があります。それらがgitインデックスに追加されたら。 gitリポジトリにプッシュしたい場合は、これらの変更をコミットできます。
しかし、突然、インデックスに追加した1つの追加ファイルがgitリポジトリにプッシュするのに必要ではないことをコミットしている間に知るようになりました。そのファイルをインデックスに入れたくないということです。さて、問題はgitインデックスからそのファイルを削除する方法です。インデックスにそれらを入れるためにgit addを使ったのでgit rmを使うのは論理的でしょうか?違う! git rmは単にファイルを削除し、その削除をインデックスに追加します。だから今何をすべきか:
つかいます:-
gITリセット
インデックスを消去し、作業ディレクトリをそのまま残します。 (単にすべてを展開する)。
それはそれと一緒にオプションの数で使用することができます。 git resetで使用する3つの主なオプションがあります:--hard、 - soft、 - mixed。これらは、リセットしたときのHEADポインタに加えて、リセットされる内容に影響します。
まず、--hardがすべてをリセットします。あなたの現在のディレクトリは、あなたがずっとそのブランチをたどっていた場合と全く同じでしょう。作業ディレクトリとインデックスがそのコミットに変更されます。これは私がよく使うバージョンです。 git reset --hardはsvn revertのようなものです。
次に、完全に反対の—softは、作業ツリーもインデックスもリセットしません。 HEADポインタを移動するだけです。これはあなたの現在の状態をあなたがあなたのディレクトリの中に配置しようとしているコミットとは異なる、そしてコミットのために「段階的に」変更されたままで残します。ローカルでコミットしてもgitサーバーにコミットをプッシュしていない場合は、前のコミットにリセットして、正しいコミットメッセージで再コミットできます。
最後に、--mixedはインデックスをリセットしますが、作業ツリーはリセットしません。そのため、変更はすべてまだ残っていますが、「ステージングされていない」ので、git add 'またはgit commit -aを実行する必要があります。 git commit -aで意図した以上にコミットした場合、これを使用することがあります。gitreset --mixedを使用してコミットを取り消し、コミットするものを追加してそれらをコミットするだけです。
git revertとgit resetの違い : -
簡単に言うと、git resetは"未修正の間違い"へのコマンド、git revertは"未確定の間違い"へのコマンドです。
ある変更に何らかのエラーを犯し、それをgit repoにコミットしてプッシュしたのであれば、git revertが解決策です。また、プッシュ/コミットする前に同じエラーが見つかった場合は、git resetを使用して問題を解決できます。
私はそれがあなたの混乱を取り除くのに役立つことを願っています。
TL、DR
git reset
はステージングを最後のコミットにリセットします。作業ディレクトリ内のファイルも最後のコミットにリセットするには、--hard
を使用します。
長期版
しかし、それは明らかに単純化されているので、かなり冗長な答えです。変更を元に戻すという文脈でgit reset
を読むことは私にとってより意味がありました。例えば。これを見てください。
Git revertが変更を元に戻すための「安全な」方法であれば、git resetを危険な方法と考えることができます。 git resetを使って元に戻す(そしてコミットがrefやreflogから参照されなくなった)場合、元のコピーを取り出す方法はありません - それは永久的な元に戻すことです。このツールは、作業を失う可能性がある唯一のGitコマンドなので、このツールを使用するときは注意が必要です。
https://www.atlassian.com/git/tutorials/undoing-changes/git-reset から
この
コミットレベルでは、リセットはブランチの先端を別のコミットに移動する方法です。これは現在のブランチからコミットを削除するために使用できます。
https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations から
注意してください、これはこの複雑な機能を理解しようとする最初のステップとして意図されている簡単な説明です。
次の各コマンドの実行後に自分のプロジェクトの状態がどのようになるかを視覚化したい視覚学習者には便利です。
カラーをオンにしてTerminalを使う人のために(git config --global color.ui auto):
git reset --soft A
すると、BとCのものが緑色で表示されます(段階的でコミットの準備ができています)
git reset --mixed A
(またはgit reset A
)をクリックすると、BとCのものが赤で表示されます(ステージングされず、ステージング(グリーン)、そしてコミットの準備ができています)
git reset --hard A
なら、あなたはもはやどこにもBとCの変化を見ないでしょう(まるでそれらが存在しなかったかのようになるでしょう)
または 'Tower'や 'SourceTree'のようなGUIプログラムを使う人のために
git reset --soft A
すると、BとCのものがコミットの準備が整った「staged files」領域に表示されます。
git reset --mixed A
(またはgit reset A
)なら、ステージングに移動してコミットする準備ができている「ステージングされていないファイル」領域にBとCのものが表示されます。
git reset --hard A
なら、あなたはもはやどこにもBとCの変化を見ないでしょう(まるでそれらが存在しなかったかのようになるでしょう)
チェックアウトはヘッドを特定のコミットに向けます。
リセットは特定のコミットで分岐を指します。 (分岐はコミットへのポインタです。)
ちなみに、あなたの頭がブランチによっても指示されているコミットを指していないのであれば、あなたは独立した頭を持っています。(間違っていることが判明しました。コメントを参照してください...)