開発者は常に Git を使用しています。 「うーん、私は今何をしたの?」と言ったことがありますか? この投稿は、自信を持って歴史を書き換えるためのツールを提供します。 始める前の注意事項 また、この投稿の内容をカバーするライブ トークも行いました。ビデオが好きな場合 (または読書と一緒に見たい場合) — あなたはそれを見つけることができます . ここ Git に関する本を執筆中です。初期バージョンを読んでフィードバックを提供することに興味がありますか?メールを送ってください: gitting.things@gmail.com Git での変更の記録 Git で 方法を理解する前に、Git で変更を 方法を理解する必要があります。すでにすべての用語を知っている場合は、この部分をスキップしてください。 元に戻す 記録する Git をファイルシステムのスナップショットを適時に記録するためのシステムと考えると非常に便利です。 Git リポジトリを考えると、3 つの「状態」または「ツリー」があります。 通常、ソース コードで作業するときは、 から作業します。 (または ) は、 関連付けられているファイル システム内の任意のディレクトリです。 作業ディレクトリ 作業ディレクトリ (ectrory) 作業ツリー リポジトリが プロジェクトのフォルダーとファイル、および というディレクトリが含まれています。 フォルダーの内容については、 . .git .git 以前の投稿 いくつかの変更を行った後、それらを に記録することをお勧めします。 (要するに: ) は のコレクションであり、それぞれのコミットは、自分のマシン上か他の誰かのマシン上にあるかにかかわらず、過去の日付でプロジェクトの どのように見えたかのアーカイブです。 リポジトリ リポジトリ repo commits 作業ツリーが には、 やブランチなど、コード ファイル以外のものも含まれます。 リポジトリ HEAD その間に、 または あります。これら 2 つの用語は交換可能です。ブランチ と、Git は、 に最後にチェックアウトされたすべてのファイルの内容と、それらが最初にチェックアウトされたときの様子を に入力します。 インデックス ステージング領域が checkout 作業ディレクトリ インデックス を使用すると、 の状態に基づいて が作成されます。 git commit インデックス コミット したがって、 または 次のコミットの遊び場です。 で好きなことをしたり、ファイルを追加したり、何かを削除したりできます。準備ができたら、先に進んでリポジトリにコミットします。 インデックス、 ステージング領域は、 index ハンズオンの時間です🙌🏻 使用して、新しいリポジトリを初期化します。 というファイルにテキストを書き込みます。 git init 1.txt 上記の 3 つのツリー状態のうち、 現在どこにありますか? 1.txt まだインデックスに導入されていないため、作業ツリー内。 してインデックスに追加するには、 を使用します。 ステージング git add 1.txt これで、 使用して変更をリポジトリにコミットできます。 git commit 作業ツリー全体を記述するツリーへのポインターを含む、新しいコミット オブジェクトを作成しました。この場合、ルート フォルダー内の のみになります。ツリーへのポインターに加えて、コミット オブジェクトには、タイムスタンプや作成者情報などのメタデータが含まれます。 1.txt Git のオブジェクト (コミットやツリーなど) の詳細については、 . 私の以前の投稿をチェックしてください (はい、「チェックアウト」、しゃれが意図されています😇) Git は、このコミット オブジェクトの SHA-1 値も教えてくれます。私の場合、それは でした (スペースを節約するために、SHA-1 値の最初の 7 文字のみです)。 c49f4ba このコマンドを自分のマシンで実行すると、別の作成者であるため、別の SHA-1 値が取得されます。また、別のタイムスタンプでコミットを作成します。 リポジトリを初期化すると、Git は新しいブランチ (デフォルトでは という名前) を作成します。と .したがって、デフォルトでは ブランチしかありません。複数のブランチがある場合はどうなりますか? Git はどのブランチがアクティブなブランチであるかをどのように認識しますか? main Git のブランチは、コミットへの名前付き参照にすぎません main Git には と呼ばれる別のポインターがあります。これは (通常) ブランチを指し、次にコミットを指します。ところで、 ブランチの名前といくつかのプレフィックスが含まれています。 HEAD フードの下、 HEAD は単なるファイルです。 リポジトリにさらに変更を加える時が来ました! さて、もう一つ作りたいと思います。それでは、新しいファイルを作成して、前と同じようにインデックスに追加しましょう。 では、 を使用します。重要なことに、 のことを行います。 git commit git commit 次の 2 つ まず、コミット オブジェクトが作成されるため、Git の内部オブジェクト データベース内に、対応する SHA-1 値を持つオブジェクトが存在します。この新しいコミット オブジェクトは、親コミットも指します。これは、 コマンドを作成したときに が指していたコミットです。 git commit HEAD 2 番目に、 アクティブ ブランチの を移動します。 git commit main 変更の取り消し 履歴を書き換えるには、コミットを導入するプロセスを元に戻すことから始めましょう。そのために、非常に強力なツールであるコマンド について学びます。 git reset git reset --soft つまり、前に行った最後のステップは でした。これは実際には 2 つのことを意味します。つまり、Git は commit オブジェクトを作成し、アクティブなブランチである を移動しました。このステップを元に戻すには、コマンド を使用します。 git commit main git reset --soft HEAD~1 構文 の最初の親を参照します。コミットグラフに複数のコミットがある場合は、「コミット 2」を指している「コミット 3」と言います。これは、「コミット 1」を指しています。 HEAD~1 HEAD そして、 「Commit 3」を指していたとします。 を使用して「コミット 2」を参照し、 を「コミット 1」を参照することができます。 HEAD HEAD~1 HEAD~2 それでは、コマンドに戻ります: git reset --soft HEAD~1 このコマンドは、Git に が指すものを変更するように要求します。 (注: 以下の図では、「 が指しているすべてのもの」に を使用しています)。この例では、 は を指しています。したがって、Git は のポインターを を指すように変更するだけです。つまり、 「コミット 1」を指します。 HEAD HEAD *HEAD HEAD main main HEAD~1 main ただし、このコマンドは、インデックスまたは作業ツリーの状態には影響しませ た。したがって、 を使用すると を実行する前と同じように、 がステージングされていることがわかります。 んでし git status git commit 2.txt から始まり、 に進み、次に「Commit 1」に進みます。これは、履歴から「Commit 2」に到達できなくなったことを意味することに注意してください。 git log? HEAD main 「コミット2」のコミットオブジェクトが削除されたということでしょうか? 🤔 いいえ、削除されません。オブジェクトの Git の内部オブジェクト データベース内にまだ存在します。 を使用して現在の履歴をプッシュすると、Git は「コミット 2」をリモート サーバーにプッシュしませんが、コミット オブジェクトはリポジトリのローカル コピーにまだ存在します。 git push ここで、再度コミットします。「コミット 2.1」のコミット メッセージを使用して、この新しいオブジェクトを元の「コミット 2」と区別します。 「Commit 2」と「Commit 2.1」はなぜ違うのですか?同じコミットメッセージを使用したとしても、それらが指しているとしても ( と で構成されるルート フォルダーの)、作成された時期が異なるため、タイムスタンプは異なります。 同じ木のオブジェクト 1.txt 2.txt 上の図では、Git の内部オブジェクト データベースにまだ存在していることを思い出させるために、「コミット 2」を残しました。 「Commit 2」と「Commit 2.1」の両方が「Commit 1」を指すようになりましたが、「Commit 2.1」のみが から到達可能です。 HEAD Git リセット -- 混合 さかのぼってさらに元に戻す時が来ました。今回は、 を使用します (注: は のデフォルト スイッチです)。 git reset --mixed HEAD~1 --mixed git reset このコマンドは と同じように開始されます。つまり、 が現在指しているポインタ ブランチ) を取得し、それを に設定します。この例では、「コミット 1」です。 git reset --soft HEAD~1 HEAD main HEAD~1 次に、Git はさらに進んで、インデックスに加えた変更を効果的に元に戻します。つまり、最初のステップで設定した後の新しい ある現在の と一致するようにインデックスを変更します。 HEAD HEAD を実行すると、 が (「コミット 1」) に設定され、Git はインデックスを「コミット 1」の状態に一致させることを意味します。 インデックスに含まれなくなることを意味します。 git reset --mixed HEAD~1 HEAD HEAD~1 2.txt 元の「コミット 2」の状態で新しいコミットを作成します。今回は、作成する前に 再度ステージングする必要があります。 2.txt Git リセット -- ハード 続けて、さらに元に戻してください! を実行してください。 git reset --hard HEAD~1 繰り返しになりますが、Git は ステージから開始し、 が指すもの ( ) を (「コミット 1」) に設定します。 --soft HEAD main HEAD~1 ここまでは順調ですね。 次に、 ステージに進み、インデックスを と一致させます。つまり、Git は のステージングを取り消します。 --mixed HEAD 2.txt Git がさらに進んで、作業ディレクトリをインデックスのステージと一致させる ステップの時間です。この場合、作業ディレクトリからも を削除することを意味します。 --hard 2.txt (**注: この特定のケースでは、ファイルは ファイル システムから削除されません。ただし、 を理解するためにはそれほど重要ではありません)。 追跡されていないため、 git reset Git に変更を加えるには、3 つのステップがあります。作業ディレクトリ、インデックス、またはステージング領域を変更し、それらの変更で新しいスナップショットをコミットします。これらの変更を には: 元に戻す を使用すると、コミット ステップが取り消されます。 git reset --soft を使用すると、ステージング ステップも元に戻します。 git reset --mixed を使用すると、作業ディレクトリへの変更が元に戻されます。 git reset --hard リアルなシナリオ! シナリオ #1 したがって、実際のシナリオでは、「私は Git が大好きです」とファイル ( ) に書き込みます。先に進み、これもステージングしてコミットします。 love.txt おっと! 実は、私はあなたにそれをコミットして欲しくありませんでした。 私が実際にあなたにして欲しかったのは、このファイルをコミットする前に、このファイルにいくつかの愛の言葉を書くことです. あなたは何ができますか? これを克服する 1 つの方法は、 を使用して、実行したコミットとステージングの両方のアクションを効果的に元に戻すことです。 git reset --mixed HEAD~1 したがって 再び「コミット 1」を指し示し、 もはやインデックスの一部ではありません。ただし、ファイルは作業ディレクトリに残ります。先に進み、さらにコンテンツを追加できます。 main love.txt ファイルをステージングしてコミットします。 よくやった👏🏻 「Commit 1」を指している「Commit 2.4」のこの明確で素晴らしい歴史があります。 ツールボックスに新しいツール 追加されました 💪🏻 git reset このツールは非常に便利で、ほとんど何でも実現できます。常に最も便利なツールというわけではありませんが、慎重に使用すれば、ほぼすべての書き換え履歴シナリオを解決できます。 初心者の場合、Git で元に戻したいときはほぼいつでも のみを使用することをお勧めします。慣れてきたら、他のツールに移ります。 git reset シナリオ 2 別のケースを考えてみましょう。 という新しいファイルを作成します。ステージングとコミット: new.txt おっと。実はそれは間違いです。あなたは にいましたが、機能ブランチでこのコミットを作成してほしいと思いました。私の悪い😇 main この記事からあなたに取ってもらいたい最も重要なツールが 2 つあります。 です。最初の、そしてはるかに重要なことは、現在の状態とあなたがなりたい状態を です. 2 つ目は git reset ホワイトボードに載せること このシナリオでは、現在の状態と目的の状態は次のようになります。 次の 3 つの変更点に気付くでしょう。 現在の状態では「コミット 3」(青い部分) を指していますが、目的の状態では「コミット 2.4」を指しています。 main ブランチは現在の状態には存在しませんが、存在し、目的の状態の「コミット 3」を指しています。 feature 現在の状態では を指し、目的の状態では を指します。 HEAD main feature これを描くことができ、 使い方を知っていれば、間違いなくこの状況から抜け出すことができます。 git reset 繰り返しますが、 は、息を吸ってこれを引き出すことです. 最も重要なこと 上の図を見て、どうすれば現在の状態から望ましい状態になるのでしょうか? もちろん、いくつかの異なる方法がありますが、シナリオごとに 1 つのオプションのみを提示します。他のオプションも自由に試してみてください。 を使用して開始できます。これにより、 前のコミット「Commit 2.4」を指すように設定されます。 git reset --soft HEAD~1 main current-vs-desired の図をもう一度見てみると、新しいブランチが必要であることがわかりますよね? または (同じことを行います) を使用できます。 git switch -c feature git checkout -b feature このコマンドは、新しいブランチを指すように も更新します。 HEAD を使用したため、インデックスを変更していないため、現在、コミットしたい状態とまったく同じです — なんと便利なことでしょう! ブランチにコミットするだけです。 git reset --soft feature そして、あなたは望ましい状態になりました🎉 シナリオ #3 あなたの知識を他のケースに適用する準備はできましたか? にいくつかの変更を加え、さらに という名前の新しいファイルを作成します。それらをステージングしてコミットします。 love.txt cool.txt ああ、おっと、実際には、変更ごとに 1 つずつ、2 つの別々のコミットを作成してほしかった 🤦🏻 これを自分で試してみませんか? コミットとステージングの手順を元に戻すことができます。 このコマンドを実行すると、インデックスにはこれら 2 つの変更が含まれなくなりますが、両方ともファイル システムに残ります。したがって、 のみをステージングする場合は、個別にコミットしてから、 に対して同じことを行うことができます。 love.txt cool.txt ナイス😎 シナリオ #4 テキストを含む新しいファイル ( ) を作成し、テキストを に追加します。両方の変更をステージングし、コミットします。 new_file.txt love.txt おっと🙈🙈 今回は、 ブランチではなく、既存のブランチではなく、別のブランチに配置したいと考えました。 新しい それで、あなたは何ができますか? ヒントをあげます。答えは本当に短く、とても簡単です。最初に何をしますか? いいえ、 しません。私たちは描く。他のすべてがずっと簡単になるので、それが最初に行うことです。これが現在の状態です。 reset そして望ましい状態? 現在の状態から目的の状態にどのように移行しますか? 最も簡単なのはどれですか? 先ほどのように を使うのもひとつの方法ですが、別の方法も試していただきたいです。 git reset まず、 ブランチを指すように を移動します。 existing HEAD 直観的に、青いコミットで導入された変更を取得し、これらの変更を ブランチの上に適用 (「コピー アンド ペースト」) する必要があります。 Git にはそのためのツールがあります。 existing このコミットとその親コミットの間に導入された変更を取得し、これらの変更をアクティブなブランチに適用するように Git に依頼するには、 を使用できます。このコマンドは、指定されたリビジョンで導入された変更を取得し、アクティブなコミットに適用します。 git cherry-pick また、新しいコミット オブジェクトを作成し、この新しいオブジェクトを指すようにアクティブ ブランチを更新します。 上記の例では、作成されたコミットの SHA-1 識別子を指定しましたが、変更を適用するコミットが が指しているコミットであるため、 を使用することもできます。 main git cherry-pick main しかし、これらの変更が ブランチに存在することは望ましくありません。 変更を ブランチにのみ適用しました。それらを からどのように削除できますか? main git cherry-pick existing main 1 つの方法は、 に から を使用することです。 main switch git reset --hard HEAD~1 できたね! 💪🏻 実際には、指定されたコミットとその親の違いを計算し、それらをアクティブなコミットに適用することに注意してください。これは、競合が発生する可能性があるため、Git がこれらの変更を適用できない場合があることを意味しますが、それは別の投稿のトピックです。 git cherry-pick また、ブランチによって参照されるコミットだけでなく、任意のコミットで導入された変更を ように Git に依頼できることに注意してください。 cherry-pick 新しいツールを取得したので、 と を使用しています。 git reset git cherry-pick シナリオ 5 さて、別の日、別のレポ、別の問題。 コミットを作成します。 そして、それをリモート サーバーに 。 push うーん、おっと😓… 私はちょうど何かに気づきました。そこにタイプミスがあります。 This is と書きました。おっと。では、今の大きな問題は何ですか?私は ed 。つまり、他の誰かがすでにそれらの変更を ed している可能性があります。 This is more text This is more tezt push pull これまで行ってきたように、 を使用してこれらの変更をオーバーライドすると、異なる履歴が作成され、すべてが崩壊する可能性があります。 するまでは、自分のレポのコピーを好きなだけ書き換えることができます。 git reset push 変更を したら、履歴を書き換える場合は、他の誰もそれらの変更を取得していないことを 確認する必要があります。 push 十分に または、 という別のツールを使用することもできます。このコマンドは、提供しているコミットを取得し、 と同様に、その親コミットから Diff を計算しますが、今回は逆の変更を計算します。 git revert git cherry-pick したがって、指定されたコミットで行を追加した場合、その逆は行を削除し、その逆も同様です。 新しいコミット オブジェクトを作成しました。これは、履歴への追加であることを意味します。 使用することで、履歴を書き換えませんでした。あなたは過去の過ちを認めました。このコミットは、あなたが間違いを犯し、それを修正したことを認めたものです。 git revert git revert それがより成熟した方法だと言う人もいます。 を使用して以前のコミットを書き換えた場合に得られるほどクリーンな履歴ではないと言う人もいます。しかし、これは歴史の書き換えを避ける方法です。 git reset タイプミスを修正して、再度コミットできます。 あなたのツールボックスには、新しい輝かしいツール がロードされました: revert シナリオ #6 作業を完了し、コードを記述して、 に追加します。この変更をステージングし、コミットします。 love.txt 私は自分のマシンで同じことを行い、キーボードの上 キーを使用して前のコマンドにスクロールして戻り、 を押して…うわー。 Up Enter おっと。 を使用しただけですか? 😨 git reset --hard 何が起こったのですか? Git はポインターを に移動したため、現在の履歴からは、すべての 作業を含む最後のコミットに到達できません。また、Git はステージング領域からすべての変更をステージング解除し、作業ディレクトリをステージング領域の状態に一致させました。 実際に HEAD~1 貴重な つまり、私の仕事がなくなったこの状態にすべてが一致します。 フリークアウトタイム。怖がら。 しかし、本当に、びっくりする理由はありますか?そうではありません… 私たちはリラックスした人々です。私たちは何をしますか?さて、直観的に、コミットは本当に、 なくなったのでしょうか?いいえ、なぜですか? Git の内部データベース内にまだ存在します。 本当に その場所さえわかれば、このコミットを識別する SHA-1 値がわかるので、復元できます。元に戻す操作を元に戻し、このコミットに こともできました。 reset したがって、ここで本当に必要なのは、「削除された」コミットの SHA-1 だけです。 問題は、どうすればそれを見つけることができるかということです。 役に立ちますか? git log まあ、そうではありません。 、探しているコミットの親コミットを指す を指す に移動します。次に、 親チェーンをトレースバックしますが、これには私の貴重な作業のコミットは含まれません。 git log main HEAD git log ありがたいことに、Git を作成した非常に頭の良い人たちは、バックアップ プランも作成してくれました。これは と呼ばれます。 reflog Git で作業しているときに、 使用して行うことができる でなく、 や などの他のコマンドを変更するたびに、Git はエントリを に追加します。 git reset HEAD git switch git checkout reflog コミットが見つかりました! で始まるものです。 0fb929e という「ニックネーム」で関連付けることもできます。 Git は を使用して の最初の親を取得し、 を使用して の 2 番目の親を参照するなど、Git は を使用して の最初の reflog 親を参照します。前の手順で 指していた場所。 HEAD@{1} HEAD~1 HEAD HEAD~2 HEAD HEAD@{1} HEAD HEAD にその値を表示するように依頼することもできます。 git rev-parse を表示する別の方法は、 を使用することです。これは、 に実際に を考慮するように要求します。 reflog git log -g git log reflog 上記のように、 と同様に、「コミット 2」を指す を指しています。しかし、 内のそのエントリの親は「コミット 3」を指しています。 reflog HEAD main reflog したがって、「コミット 3」に戻るには、 (または「コミット 3」の SHA-1 値) を使用できます。 git reset --hard HEAD@{1} そして今、 実行すると: git log 私たちはその日を救いました! 🎉👏🏻 このコマンドをもう一度使用するとどうなりますか? を実行しましたか? Git は を、最後の 前に が指していた場所、つまり「コミット 2」に設定します。私たちは一日中続けることができます: git commit --reset HEAD@{1} HEAD reset HEAD ツールボックスを見ると、Git でうまくいかない多くのケースを解決するのに役立つツールが満載です。 これらのツールを使用すると、Git の仕組みをよりよく理解できるようになります。具体的には など、履歴を書き換えることができるツールは他にもありますが、この投稿ですでに多くのことを学んでいます。今後の投稿では、 についても掘り下げます。 git rebase git rebase このツールボックスにリストされている 5 つのツールよりもさらに重要な最も重要なツールは、現在の状況と望ましい状況をホワイトボードで示すことです。これで私を信じてください、それはすべての状況を困難に思わせず、解決策をより明確にします. Git の詳細 また、この投稿の内容をカバーするライブ トークも行いました。ビデオが好きな場合 (または読書と一緒に見たい場合) — あなたはそれを見つけることができます . ここ 一般に、 Git とその内部の多くの側面をカバーしています。あなたは歓迎されています (しゃれのつもり😇) 私の YouTube チャンネル 見てみな 著者について のCTO兼共同創設者です。 は、開発者とそのチームが最新の内部ドキュメントを使用してコードベースに関する知識を管理するのに役立つ開発ツールです。 Omer は、Check Point Security Academy の創設者であり、ITC でサイバー セキュリティ リードを務めていました。ITC は、有能な専門家を訓練してテクノロジのキャリアを開発する教育機関です。 オマー・ローゼンバウム スイム Omer は、テルアビブ大学で言語学の修士号を取得しており、 . 簡単な YouTube チャンネル 初公開 ここで