Skip to content
This page has been auto-translated and may contain errors.View in English

Gitでの変更とコミットの取り消し

docs.scrimba.com

取り消す必要があることは常に発生します。意図しない編集を入力した、修正しようとしたら状況が悪化した、または誰にも見られたくないコミットをしてしまった。Gitはあなたのプロジェクトの過去のほぼすべてのバージョンの記録を保持しているため、ほとんどのミスは一瞬感じるより復旧可能です。どのツールを使うかは、ミスがどこまで進んでいるかによります: まだエディタに入ったままか、既にステージングされているか、既にコミットされているか、または既にチームメイトが見られる場所にプッシュされているか。この章ではそれぞれの段階を順番に説明します。

pushとpullとは?

Pushはあなたのコミットをチームメイトも作業する共有リポジトリのコピーに送信し、Pullは彼らのコミットをあなたに持ってきます。Remotes and GitHubは両方について完全に説明しています。この章では「プッシュされた」はコミットがあなたのマシンを離れたことを意味します。

コミット前に変更を破棄する

コミットループweather-appstyles.cssのスタイルを変更中だとします。新しいレイアウトは悪い考えだとわかりました。ステージングしていない、コミットもしていない。ファイルを前の状態に戻すだけ。その仕事にはgit restoreがちょうど良いです:

bash
$ git status
On branch main
Changes not staged for commit:
        modified:   styles.css

$ git restore styles.css

$ git status
On branch main
nothing to commit, working tree clean

git restore <file>はコミットされていない編集を破棄し、ファイルを最後に保存された状態に戻します。git addでファイルを既にステージングしていた場合、ファイルがワーキングディレクトリではなくステージング領域に座っているため、素のgit restoreは変更しません。まずアンステージングしてからリストアします:

bash
$ git add styles.css
$ git restore --staged styles.css
$ git restore styles.css

2つの領域に対して2つの分離した移動: git restore --stagedはファイルをステージング領域から戻し、単独のgit restoreはワーキングディレクトリの編集を破棄します。git restoreはコミットされていない作業のみを対象にします。変更がコミットされると、別のコマンドが引き継ぎます。

古いチュートリアルやスクリプトはgit restore <file>が今日やる正確な仕事にgit checkout -- <file>を使用します。checkoutは「ブランチを切り替える」と「ファイルをリストアする」の両方として機能し、これが混乱を招いたため、Gitはこの2つをswitchrestoreに分割しました。古い資料でcheckoutに出会うでしょう。認識していますが、今あなたが書くものではrestoreを使用してください。現在のフォルダのすべてのアンステージング変更をgit restore .で一度に破棄することもできます。これは1つで全ファイルをクリアするため、実行前に考える価値があります。

restoreの取り消しはワーキングディレクトリで確実に停止します。コミットは保存されたオブジェクトでGitのガベージコレクションがそれを削除するまで残ります。そのため悪いコミットはハードリセット後でもreflogを通じて復旧可能です。コミットされていない編集はそもそも保存オブジェクトに変わりませんでした: restoreはファイルの最後に保存されたバージョンを編集の上にチェックアウトし、掘り返すために保存された前の状態はどこにもありません。これは早期かつ頻繁にコミットする実用的なケースです。乱雑なコミットはほぼ常に修正可能です。保存しなかった編集は通常そうではありません。

Junoコミットされていない変更を破棄するgit restore <file>はコミットしていない編集を破棄し、ファイルを前の状態に戻します。既にgit addで変更をステージングしていた場合、まずgit restore --staged <file>でアンステージングし、編集を消したければファイルをリストアしてください。これはコミットしていない変更でのみ機能するため、既に保存したミスを救うことはできません。
Junoコミットされていない変更を破棄するgit restore <file>git checkout -- <file>の現代的な置き換えで、git restore .はフォルダ内のすべてのアンステージング変更をクリアするため、まずgit statusを実行してそれが何に触れるかを確認してください。古い答えやスクリプトでこの仕事をするcheckoutを見ますが、古い、より多機能な名前の下で同じ考えです。
Junoコミットされていない変更を破棄するrestoreがコミットを作成したり触ったりすることはありません。ワーキングディレクトリをファイルの最後に保存されたバージョンで上書きするだけです。つまり、誤った場合の復旧の跡を残しません。この章の他のほぼすべてとは異なります。小さくコミットし、頻繁にコミットしてください。そして「おっと」はresetやreflogが修正できる問題に変わり、保存ポイントに戻ることができない変更ではなくなります。

git stashで作業を一時保存する

時々修正するミスではなく、ただタイミングが悪いだけです。styles.cssの編集途中で、チームメイトがあなたに別のブランチで必要とし、半完成のスタイルをコミットする準備ができていません。git stashは変更を一時保存し、きれいなワーキングディレクトリをあなたに与えるため、離れて後で戻ることができます。

bash
$ git status
On branch main
Changes not staged for commit:
        modified:   styles.css

$ git stash
Saved working directory and index state WIP on main: 8f3c7d1 Cache forecast responses in localStorage

$ git status
On branch main
nothing to commit, working tree clean

styles.cssへの編集は消えていません。駐車されています。準備ができたらgit stash popで戻してください:

bash
$ git stash pop
On branch main
Changes not staged for commit:
        modified:   styles.css

git stashはまだコミットする準備ができていない変更用で、ブランチにコミットを作成することはありません。

単一のstashが実際の1週間の作業をカバーすることはめったにないため、残りのツールキットを知ってください。git stash listは一時保存したすべてを表示します。最新が最初です。git stash popは上部のエントリを再適用して一覧から削除します。git stash applyは再適用しますが一覧に残します。同じ一時保存された変更を2つのブランチで使いたい場合に便利です。入力時にstashに名前を付けると、後でlistがラベルのないエントリの壁にならないようにできます:

bash
$ git stash push -m "wip: forecast card layout"
$ git stash list
stash@{0}: On main: wip: forecast card layout

stashはGitの他のすべてと同じコミット機械から構築されています。git stashはステージングされた状態とアンステージング状態をコミットオブジェクトのカップルとして保存し、特別なリファレンスrefs/stashでそれらを指します。そのためgit stash listは独自の小さな履歴のように読みます。reflogは同じトリックに依存しており、ブランチが到達しないコミットを指すリファレンスです。

Juno作業を一時保存するgit stashはコミットする準備ができていない変更を一時保存するため、ワーキングディレクトリがきれいになり、git stash popで後で戻ります。何もコミットされず、何も失われません。編集は少し駐車されています。チームメイトが今別のブランチで必要な場合、私はこれを常に使用します。
Juno作業を一時保存するgit stash listはすべての一時保存された変更を表示し、popは最新のものを再適用して削除し、applyは削除なしに再適用します。push -mで入力時にstashに名前を付けてください。ラベルのないエントリでいっぱいのstash一覧は1週間後、誰の役にも立ちません。あなたも含めて。
Juno作業を一時保存する stashはコミットオブジェクトのカップルでrefs/stashが指しており、ブランチから外れています。そのため独自の小さな履歴のように動作します。それをそのように見ると、stashは魔法のように感じるのをやめます。同じオブジェクトモデルが同じトリックを再び実行しています。

git revertでコミットを取り消す

コミット8f3c7d1「Cache forecast responses in localStorage」が間違っていることが判明し、既にプッシュされているため、チームメイトも引っ張られています。そのコミットを書き直すことは、既に誰か他の人が持っているものを変更することを意味し、彼らのコピーと次にプルするときにあなたのコピーが一致しません。git revertは既に存在するものを何も変更せずにこれを解決します。反対の変更を適用する全く新しいコミットを作成します。

bash
$ git revert 8f3c7d1
[main 2b6f0d1] Revert "Cache forecast responses in localStorage"

履歴は元のコミットとそれを取り消すコミットの両方を順番に示しています。誰のコピーも再形成する必要がなく、チームメイトは他のコミット同様新しいrevertコミットをプルします。

git revertは他の人が既に持つコミットで常に安全です。古いコミットを変更する代わりに新しいコミットを追加するからです。

後の分け目とコンフリクトを起こす可能性がある変更をリバートすると、マージとコンフリクトがカバーする同じ種類のコンフリクトになります: Gitはファイルを<<<<<<<=======>>>>>>>でマークし、あなたが解決するのを待ちます。マーカーを修正し、git addファイルを行い、次にgit revert --continueで完了します。

bash
$ git revert 8f3c7d1
Auto-merging src/index.js
CONFLICT (content): Merge conflict in src/index.js

$ git add src/index.js
$ git revert --continue

共有履歴でのRevertの安全性は、公開ブランチをリベースしない裏にある同じ考え方に基づいています: 他の人が既にプルしたコミットを書き直すことは、彼らのコピーとあなたのコピーを同期から外し、その後誰かが不一致を通じて強制的に進む必要があります。Revertはすべての既存コミットを正確にそのままにします。新しいコミットのみを追加するため、誰のコピーもあなたのコピーと調整する必要がありません。

Junorevertでコミットを取り消すgit revert <commit>は元のコミットを消すのではなく、それを逆転させる全く新しいコミットを追加することでコミットを取り消します。そのためコミットが既に他の誰かと共有されている場合、既に保存されたものが何も変更されないため、安全な選択肢です。履歴はミスと修正の両方の記録を保持します。Gitを長く使うほどそれを好みます。
Junorevertでコミットを取り消す Revertはマージと同じようにコンフリクトが発生する可能性があります。後のコミットが同じ行に触れた場合、同じ方法で解決します: マーカーを修正し、ファイルをaddし、その後git revert --continueを実行します。既にプッシュされているか共有されているコミットを取り消す場合はこれを使用してください。
Junorevertでコミットを取り消す Revertは既存のコミットに一切触れません。そのため、他の人が既にプルしているブランチでうまく機能する取り消しツールです。悪いコミットがあなた自身のマシンを離れた瞬間からそれをデフォルトのままにしてください。

git resetで巻き戻す: soft、mixed、hard

コミットを取り消す3番目の方法があります。git resetです。revertとは異なる動作をします: 古いコミットを取り消す新しいコミットを追加する代わりに、resetは現在のブランチを完全に異なるコミットを指すように移動します。それは誰も見ていないコミットに対して高速でクリーンにし、見た人のコミットに対しては危険です。

Resetはrestoreとrevertの上の、より広く使用して誤った従兄弟です。ほとんどの日々の修正ではそれを必要としません。restore、revert、stashは「助けて、これを取り消して」という瞬間の大部分をカバーします。他の人の説明にはそれを見るでしょう。そのため見出しは: resetはあなたのブランチを履歴を遡ります。そのモード1つ--hardはコミットされていない作業を確認なしで破棄します。あなたがどこかからコピーするコマンドにgit reset --hardが含まれる場合、実行する前に何をするかを読んでください。

git reset <commit>はあなたのブランチをそのコミットを指すように移動し、--soft--mixed--hardは他にどのくらい移動するかを決めます。3つのレイヤーを想像してください: あなたのコミット履歴。ブランチポインタが座っている場所。ステージング領域。コミットするために並んでいるもの。そしてワーキングディレクトリ。ディスク上のファイル。以下のショートハッシュはHistorygit log --onelineでカバーするのと同じように読みます。ターゲットHEAD~1は「HEADの1つ前のコミット」を意味し、「最新のコミットを取り消す」という標準的な方法です。

bash
$ git log --oneline
8f3c7d1 Cache forecast responses in localStorage
4f2a1c9 Fix temperature conversion in Celsius-to-Fahrenheit formula
5f3d8b2 Add five-day forecast to the dashboard
1a2b3c4 Set up project structure

$ git reset --soft HEAD~1

--softはブランチポインタを1つ前のコミットに移動して停止します。ステージング領域とワーキングディレクトリは変更されていないため、取り消されたコミットからのすべてがステージングされ、あなたが好むように再度コミットする準備ができています。

bash
$ git reset --mixed HEAD~1

--mixedはフラグを省略した場合に実行されるものです。ブランチポインタを戻し、ステージング領域もクリアするため、取り消されたコミットの変更がワーキングディレクトリでアンステージング編集として戻ります。何も失われません。コミット前に再びgit addを実行してください。

bash
$ git reset --hard HEAD~1

git reset --hardはブランチポインタを戻し、マッチするようにワーキングディレクトリを上書きするため、コミットされていない作業と取り消されたコミットの両方が1ステップで表示されなくなり、確認を求めるプロンプトはありません。これは実行する前に一時停止するモードです。

これはまたコミットループgit commit --amendが何をしていたかも説明しています: amendはreset --soft HEAD~1に非常に近い後すぐに新しいコミットが続き、そのため最後のコミットを置き換えるのではなく、その上に積み重ねる理由です。

Resetはポインタのみを移動し、--hardモードではワーキングディレクトリがマッチするようにします。コミットオブジェクトを直接削除することはありません。reset --hardが後ろに残したコミットはGitのオブジェクトデータベースに座っており、ブランチから到達不可能です。これは重要です。なぜなら再びそれに到達する方法があるからです: git reflog。次上。共有ブランチですが、ブランチを後方に移動してプッシュすることは、チームメイトが既に持つコミットを強制的にプッシュすることを意味するため、彼らの次のプルは再構成された履歴と一致しません。これはresetが共有ブランチ上で他の人が既にプルしているブランチをカバーしながらrevertが留まる実用的な理由です。

Junogit resetで巻き戻す ほとんどの日々の修正でgit resetに到達しません。restore、revert、stashはほぼすべてをカバーします。それでも、それの形を知ってください: resetはブランチを前のコミットに戻し、--hardモードはワーキングディレクトリを確認なしで消去します。reset --hardを含むコマンドをコピーして実行する場合は、実際の注意で扱ってください。
Junogit resetで巻き戻すgit reset --softは取り消されたコミットの変更をステージングしたままにし、--mixed(デフォルト)はアンステージングしますが編集を保持し、--hardは完全に編集を捨てます。commit --amendは基本的にソフトリセットの直後に新しいコミットが続き、最後のコミットを上に積み重ねるのではなく置き換える理由です。
Junogit resetで巻き戻す Resetはポインタのみを移動し、--hardではワーキングディレクトリがマッチするようにします。コミットオブジェクト自体を削除しません。そのオブジェクトはreflogとガベージコレクションがそれに追いつくまでデータベースに留まります。再構成されたブランチをプッシュし、チームメイトの次のプルは彼らのものと一致しないこともはやの履歴と衝突します。

revertまたはresetを選択する

どのツールに到達するかを決める規則: 他の誰かが既にこのコミットをプルしていますか?はい、revert。コミットがあなたのマシンにのみ存在し、プッシュしなかったブランチ上の場合、resetは良好であり、しばしばより整然とした修正です。確実でない場合、revertは常に安全な選択肢です。両方のケースで機能するからです。

「他の誰かがプルしたか」は本当に「このコミットが他の誰かが持つリファレンスから到達可能であるか」を意味し、実際にはプッシュされたブランチ上に座っているかどうかを意味します。5分前に発明した局所的なブランチのコミットとプッシュしないものは、resetのためのオープンテリトリーです。プッシュした瞬間、それを共有として扱います: 局所的にリセットして強制的にプッシュすることは、別のブランチに触れる他の誰かと最初に調整することを意味します。

Junorevertまたはresetを選択する コミットを取り消す前に1つの質問をしてください: 他の誰かが既にそれをプルしていますか?はい、revertに到達してください。常に安全です。コミットがあなたのマシン上にのみ存在する場合、resetも問題なく機能します。どちらが当てはまるか確実でない場合、revertはより安全なデフォルトです。
Junorevertまたはresetを選択する コミットがプッシュされて他の誰かが持つ可能性がある場合、revertに到達してください。純粋にあなたに局所的でまだプッシュされていない間、resetは良好であり、しばしば整然とした修正です。ログに「revertのrevert」エントリが座っていないからです。
Junorevertまたはresetを選択する 実際のテストは到達可能性です: このコミットは他の誰かがプルしたリファレンスに座っていますか?ローカルでプッシュなし、resetはオープンテリトリーです。プッシュされて共有される、resetする場合は最初に調整する何かとして扱います。後の強制的なプッシュはチームメイトが既に持つものと衝突するからです。

git reflogで失われたコミットを復旧する

reset --hard、乱雑なリベース、またはブランチを誤って削除することは、コミットが完全に消えたように見える可能性があります。通常消えていません。GitはHEADとブランチが指した場所のローカル記録を保持し、**reflog**と呼ばれ、そのログはほぼ常に失われたように見えるコミットに戻るあなたの方法です。

あなたはこれをしばしば必要とするのは間違いありませんが、ここは見出しです: Gitは消えたように見えた瞬間にほぼ何も破棄しません。reset --hardを実行してその後そのコミットが必要であることに気づいた場合、非常に可能性の高い方法があります。もっと深く掘り下げる準備ができています。ここでカバーされています。

短いバージョン: git reflogHEADの最近の位置を列挙し、各々短いハッシュ付きで、あなたはそれらのハッシュのいずれかにgit reset --hardすることができます。正確な状態に戻ります。それは第2の履歴のように読みます: HEADが立った場所のすべて。HEADが指していないコミットを含む。

ブランチ、タグ、またはHEADがそれをもはや指していないコミットは**到達不可能**: リポジトリで放置、まだ保存、それに戻るラベルを欠けている。reflowはあなたのマシン上でHEADが移動した場所の時系列リストです。このブランチスイッチ。そのリセット。そのリベース。各エントリはHEAD@{2}のような短いリファレンスでキー付けされました。ミスの直前のエントリを見つけてブランチをそれに指し直してください:

bash
$ git reflog
2b6f0d1 HEAD@{0}: reset: moving to HEAD~1
8f3c7d1 HEAD@{1}: commit: Cache forecast responses in localStorage
4f2a1c9 HEAD@{2}: commit: Fix temperature conversion in Celsius-to-Fahrenheit formula

$ git reset --hard 8f3c7d1

これはreset --hardがリストアしたように見えるコミットです。知っておく価値のある2つの制限。まず、reflogはあなた自身のマシンにのみ存在し、プッシュとプルはそれに一切触れません。そのためチームメイトが彼らのマシンで失ったコミットを復旧することはできません。第2に、それは永遠に続きません: Gitは最終的にガベージコレクションを実行し、数週間のデフォルトカットオフを過ぎて経過した到達不可能なコミットをクリアするため、安全ネットは最近のミスをプロジェクトの全生活ではなくカバーします。

Juno失われたコミットを復旧するreset --hardを実行して後でそのコミットが必要であることに気づいた場合、それはおそらくまだ復旧可能です。Gitはあなたの作業が指した場所のローカル記録を保持し、reflowと呼ばれ、それはおそらく消えたように見えるコミットを戻すのに十分です。これはディープダイブテリトリーですが、それがそこにあることを知っていることは心強いです。
Juno失われたコミットを復旧するgit reflogHEADの最近の位置を短いハッシュで列挙し、あなはそれにreset --hardできるため、進みすぎたリセットは通常復旧可能です。あなた自身のマシンにのみ存在しますが、チームメイトが彼らのマシンで失ったコミットを救うことはできません。
Juno失われたコミットを復旧する reflowはHEADが立った場所のローカル、時系列記録であり、reset --hardがリストアしたコミットが正しいHEAD@{n}エントリでブランチを指してリストアされる方法です。最終的にガベージコレクションでエイジアウトします。それはあなた自身の最近のミスのみをカバーします。チームメイトがコミットを失った場合、彼ら自身のマシンで同じ復旧を実行する必要があります。