私は見た興味深い投稿についての微妙な点を説明しますgit reset
。
残念ながら、これについて読めば読むほど、完全に理解していないように思えます。私は SVN のバックグラウンドを持っており、Git はまったく新しいパラダイムです。Mercurial は簡単に理解できましたが、Git ははるかに技術的です。
git reset
は に近いと思いますhg revert
が、違いもあるようです。
では、具体的には何をしgit reset
ますか? 以下の点について詳細な説明を記載してください。
- オプション
--hard
、--soft
および--merge
; - や
HEAD
などの奇妙な表記法は、HEAD^
HEAD~1
- 具体的なユースケースとワークフロー。
- 作業コピー、
HEAD
および全体的なストレス レベルへの影響。
ベストアンサー1
一般的に、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 内のすべてを other-branch 内の状態にリセットし、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
が、すべての変更はchanges to be committed
git status の場合と同様に表示されます (つまり、コミットの準備としてチェックインされています)。これは、いくつかのコミットに問題があったが、作業はすべて正常である場合に使用します。必要なのは、別の方法で再コミットすることだけです。インデックスは変更されないため、必要に応じてすぐにコミットできます。結果のコミットには、リセットする前と同じ内容がすべて含まれます。--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 の動作を少し理解しておく必要があるかもしれません。特に、時間をかけて注意深く読むと、さまざまなオプションやケースについて、インデックスと作業ツリー内のファイルの状態を詳細に示す表が非常に役立ちます。(ただし、非常に密度が高く、上記の情報の膨大な量を非常に簡潔な形式で伝えています。)
奇妙な表記
あなたが言及した「奇妙な表記法」(HEAD^
およびHEAD~1
)は、 のようなハッシュ名を使用せずにコミットを指定するための単なる省略形です3ebe3f6
。これは、「リビジョンの指定」セクションgit-rev-parseのマニュアルページには、多くの例と関連する構文が記載されています。キャレットとチルダは実際には異なるもの:
HEAD~
は の略でHEAD~1
、コミットの最初の親を意味します。HEAD~2
はコミットの最初の親の最初の親を意味します。 「HEAD の前の n 個のコミット」または「HEAD の n 世代目の祖先」と考えてくださいHEAD~n
。HEAD^
(またはHEAD^1
) はコミットの最初の親も意味します。 はHEAD^2
コミットの2 番目の親を意味します。通常のマージ コミットには 2 つの親があることを覚えておいてください。最初の親はマージ先のコミットで、2 番目の親はマージされたコミットです。一般に、マージには任意の数の親 (タコ マージ) が存在する場合があります。^
および演算子は、 、 の 3 世代目の祖先の 2 番目の親、、 の最初の親の 2 番目の親、または のよう~
に連結できます。これは と同等です。HEAD~3^2
HEAD
HEAD^^2
HEAD
HEAD^^^
HEAD~3