コミットや変更を元に戻す
このセクションでは、Git の「元に戻す」戦略とコマンドについて説明します。まず重要なことは、Git には、ワープロアプリケーションに備わっているような従来型の「元に戻す」システムがないということです。Git での操作は、従来の「元に戻す」という考え方に当てはめないことをお勧めします。また、Git には話し合いで活用すべき、独自の「元に戻す」操作用語があります。この用語には、リセット、打ち消し、チェックアウト、クリーンアップなどがあります。
Git をタイムライン管理ユーティリティとして考えると、面白いかもしれません。コミットは、プロジェクト履歴のタイムラインに沿った、ある時点または関心のあるポイントのスナップショットです。さらに、ブランチを使えば複数のタイムラインを管理できます。Git で「元に戻す」場合、通常は過去の状態か、間違いが起こらなかった別のタイムラインに移動します。
このチュートリアルでは、ソフトウェア開発プロジェクトの過去のバージョンに関する操作を行う際に必要になるすべてのスキルを学習します。最初に、過去のコミットを調べる方法を示し、次に公開リポジトリに公開済みのコミットの打ち消しとローカルマシーン上での未公開のコミットの取り消しの違いを説明します。
間違えた場所を探す:古いコミットを確認する
すべてのバージョン管理システムの背景となっている考え方は、プロジェクトの状態を「安全に」コピーし、コード・ベースに回復不可能な破損が生じる可能性をなくすということにあります。一旦コミットのプロジェクト履歴が作成されると、履歴にあるあらゆるコミットのレビューや検討ができるようになります。Git リポジトリの履歴のレビューに最も適したユーティリティの 1 つは、git log
コマンドです。次の例では、git log を使用し、人気のオープンソース・グラフィック・ライブラリのために最新コミットのリストを取得します。
git log --oneline
e2f9a78fe Replaced FlyControls with OrbitControls
d35ce0178 Editor: Shortcuts panel Safari support.
9dbe8d0cf Editor: Sidebar.Controls to Sidebar.Settings.Shortcuts. Clean up.
05c5288fc Merge pull request #12612 from TyLindberg/editor-controls-panel
0d8b6e74b Merge pull request #12805 from harto/patch-1
23b20c22e Merge pull request #12801 from gam0022/improve-raymarching-example-v2
fe78029f1 Fix typo in documentation
7ce43c448 Merge pull request #12794 from WestLangley/dev-x
17452bb93 Merge pull request #12778 from OndrejSpanel/unitTestFixes
b5c1b5c70 Merge pull request #12799 from dhritzkiv/patch-21
1b48ff4d2 Updated builds.
88adbcdf6 WebVRManager: Clean up.
2720fbb08 Merge pull request #12803 from dmarcos/parentPoseObject
9ed629301 Check parent of poseObject instead of camera
219f3eb13 Update GLTFLoader.js
15f13bb3c Update GLTFLoader.js
6d9c22a3b Update uniforms only when onWindowResize
881b25b58 Update ProjectionMatrix on change aspect
関連資料
Git チートシート
ソリューションを見る
Bitbucket Cloud での Git の使用方法についてのチュートリアルです。
コミットごとに一意の SHA-1 識別ハッシュがあります。この ID を使用して、コミットされたタイムラインを移動したり、コミットを再確認したりします。既定では、git log
は現在選択されているブランチのコミットのみを表示します。表示したいコミットが別のブランチのものであることは十分に考えられます。git log --branches=*
を実行すると、すべてのブランチのすべてのコミットが表示されます。git branch コマンドは、他のブランチの表示や移動に使用します。git branch -a
コマンドを実行すると、既知の全ブランチ名が一覧表示されます。表示されたブランチ名のいずれかを git log
で使用すると、そのブランチのログを作成できます。
確認する履歴にコミット参照がある場合は、git checkout
コマンドを利用してそのコミットを確認できます。git checkout
によって、開発マシン上に保存されたスナップショットを簡単に「ロード」できます。一般的な開発プロジェクトの進行中に HEAD
は通常 main
ブランチまたはその他のローカル ブランチを指しますが、過去のコミットをチェック アウトすると HEAD
はブランチではなくコミットを直接指すようになります。この状態を「detached HEAD
」状態と呼びます。図で説明すると次のようになります。
古いファイルを確認しても、HEAD
ポインターは移動しません。同じブランチで同じコミットに留まり、「detached HEAD」の状態になるのを防ぎます。この過去バージョンのファイルは、他の変更と一緒に新しいスナップショットにコミットできます。したがって、ファイルの git checkout
コマンドは実質的に個々のファイルの変更を打ち消して元に戻す働きをします。2 つのモードの詳細については、git checkout ページを確認してください。
過去のバージョンの閲覧
この例では、常識外れの開発を実験的に開始したものの、それを保存しておくべきか否かについて判断ができないものと仮定します。そしてこの判断の参考とするため、実験的開発を開始する前のプロジェクトの状態を確認するとします。最初に、確認するバージョンのIDを知る必要があります。
git log --oneline
次のようなプロジェクト履歴が表示されたとします:
b7119f2 Continue doing crazy things
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import
ここで、git checkout
コマンドを使用して、コミットメッセージが「Make some import changes to hello.txt」であるコミットの内容を確認します。
git checkout a1e8fb5
このコマンドを実行すると、作業ディレクトリはコミット a1e8fb5
と全く同じ状態になります。この状態で、プロジェクトの現在の状態に影響を与えることなく、ファイルの閲覧、プロジェクトのコンパイル、テストラン、さらにはファイルの編集さえも可能となります。この状態で行われた操作はリポジトリには一切保存されません。開発を続けるには、プロジェクトの「現在の」状態に戻る必要があります。
git checkout main
ここでは、既定である main
ブランチで開発作業をしているものと仮定しています。main
ブランチに戻った後、git revert または git reset を使用して、不要と判断した変更を元に戻せます。
コミットしたスナップショットを元に戻す
コミットを「元に戻す」には、いくつかの異なる技術戦略があります。以下のサンプルでは、次のようなコミット履歴があると仮定します。
git log --oneline
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import
コミット 872fa7e Try something crazy
の取り消しに焦点を当てます。少しおかしなことが起きるかもしれません。
git checkout を使用してコミットを元に戻す方法
git checkout
コマンドを使用すると、以前のコミット a1e8fb5,
をチェックアウトして、コミットがおかしくなる前の状態にリポジトリを戻せます。特定のコミットをチェックアウトすると、リポジトリは「detached HEAD」状態になります。これはブランチ上で作業していないことを意味します。detached 状態で作成されたコミットは、ブランチを確立されたブランチに戻すよう変更したときにどれも孤立した状態になります。孤立したコミットは、Git のガベージ コレクションの削除対象となります。ガベージ コレクションは設定されたインターバルで実行され、孤立したコミットは完全に削除されます。孤立したコミットがガベージ コレクションの対象となることを防ぐには、必ずブランチで作業する必要があります。
detached HEAD 状態から、git checkout -b new_branch_without_crazy_commit
を実行できます。この操作により、new_branch_without_crazy_commit
というブランチが新規作成され、この状態に切り替わります。リポジトリは新しい履歴タイムライン上にあります。このタイムラインにコミット 872fa7e
は存在しません。この時点で、「元に戻し終わった」と見なし、コミット 872fa7e
がないこの新しいブランチで作業を継続できます。残念ながら、以前のブランチ、おそらくは main
ブランチが必要な場合、この元に戻す戦略は適していません。他の「元に戻す」戦略を見ていきましょう。詳しい情報や例については、git checkout の説明を参照してください。
git revert を使用して、パブリックのコミットを元に戻す方法
元のコミット履歴のサンプルに戻ったと仮定しましょう。872fa7e
コミットを含む履歴。今回は打ち消す「元に戻す」を試してみましょう。git revert HEAD
を実行すると、Git は最後のコミットの逆で新しいコミットを作成します。これにより、現在のブランチ履歴に新しいコミットが追加され、次のようになります。
git log --oneline
e2f9a78 Revert "Try something crazy"
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import
この時点で、872fa7e
コミットを再び「元に戻し」た形になります。872fa7e
は履歴内にまだ存在するのですが、新しい e2f9a78
が 872fa7e
による変更を打ち消すような全く逆のコミットになります。前述のチェックアウトによる方法とは異なり、同じブランチを使い続けることができます。この解決策は満足のいくものであり、共有パブリックリポジトリで作業するときの理想的な「元に戻す」ための方法です。ただし、選別された最小限の Git 履歴を保持する必要がある場合、この方法は適さない場合があります。
git reset を使用してコミットを元に戻す方法
この元に戻す戦略で、作業サンプルを続けます。git reset は複数の用途と機能を備えた汎用性のあるコマンドです。git reset --hard a1e8fb5
を呼び出すと、コミット履歴はその指定されたコミットにリセットされます。git log
でコミット履歴を調べると、次のようになります。
git log --oneline
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import
ログ出力から、コミット e2f9a78
と 872fa7e
がコミット履歴からなくなったことがわかります。ここで、「crazy」なコミットは一切なかったかのように新しいコミットを作成し、作業を続けることができます。変更を元に戻すこの方法には、履歴をクリーンアップする効果があります。ローカルの変更に対してはリセットの実行が優れていますが、共有リモートリポジトリで作業している場合はより複雑になります。共有リモートリポジトリにコミット 872fa7e
をプッシュしている場合、履歴をリセットしたブランチの git push
を実行すると、Git がこれを検知してエラーにします。足りないコミットがあるため、プッシュしようとしているブランチは最新ではないと、Git は判断します。このような場合は、git revert
を使用して元に戻すことをお勧めします。
直前のコミットを元に戻す
前のセクションでは、コミットを元に戻すためのさまざまな戦略について説明しました。これらの戦略はすべて、直前に実行したコミットにも適用できます。ただし、場合によっては、直前のコミットの削除やリセットが不要なこともあります。おそらくコミットが不完全だっただけです。そのような場合は、直前のコミットを修正します。作業ディレクトリでさらに変更を加え、git add
を使用してコミットのためにステージングしたら、git commit --amend を実行します。こうすることで、Git は設定されたシステム・エディターを起動し、直前のコミット・メッセージを変更できるようにします。新しい変更は、修正されたコミットに追加されます。
コミットされていない変更を元に戻す
リポジトリの履歴にコミットするまで、変更はステージング・インデックスと作業ディレクトリに保管されています。これら 2 つのエリア内の変更は元に戻せます。ステージング・インデックスと作業ディレクトリは、内部的な Git の状態管理メカニズムです。これら 2 つのメカニズムの仕組みについては、git reset ページにある詳しい説明を参照してください。
作業ディレクトリ
通常、作業ディレクトリはローカルのファイル・システムと同期しています。作業ディレクトリの変更を元に戻すには、使い慣れたエディターでふだん行うようにファイルを編集します。Git には、作業ディレクトリの管理に役立つユーティリティがいくつか用意されています。git clean コマンドは、作業ディレクトリへの変更を元に戻すのに便利なユーティリティです。さらに、git reset
を --mixed
または --hard
オプションを使用して実行すると、作業ディレクトリにリセットが適用されます。
ステージングインデックス
git add コマンドは、ステージング・インデックスに変更を追加するために使用します。ステージング・インデックスの変更を元に戻すには、主に git reset
を使用します。--mixed
を使用したリセットは、保留中の変更をステージング・インデックスから作業ディレクトリに戻します。
パブリックな変更の取り消し
リモートリポジトリを使ってチームで作業する場合、変更を元に戻すときにはさらに考慮が必要です。通常、git reset
は「ローカル」で元に戻す方法と見なすべきです。プライベートブランチへの変更を元に戻す場合は、リセットの使用をお勧めします。リセットでは、他の開発者が作業している可能性がある他のブランチからコミットの取り消しを切り離すことができます。共有ブランチでリセットを実行した後、git push
を使ってそのブランチをリモートでプッシュすると、問題が生じます。この場合、足りないコミットがあるため、プッシュしようとしているブランチはリモートブランチより古いとして、Git はプッシュをブロックします。
共有履歴を元に戻す方法としては、git revert
をお勧めします。打ち消しは共有履歴からコミットを削除することは一切ないため、リセットよりも安全です。打ち消しは元に戻すコミットを保持したまま、削除するコミットを逆向きにする新しいコミットを作成します。離れた場所にいる開発者がブランチをプルして、やり直すコミットを元に戻した新しい打ち消しコミットを受け取れるため、この方法は共有リモート コラボレーションにより安全な選択肢です。
要約
ここでは、Git で行った作業を取り消すための戦略の概要を取り上げました。Git プロジェクトを「元に戻す」方法は 1 つではないことを覚えておいてください。このページで説明したトピックのほとんどは、関連する Git コマンド別のページでさらに詳しく説明されています。最もよく使われている「元に戻す」ツールは、git checkout、git revert、git reset です。重要なポイントは以下のとおりです。
- 変更がコミットされると、通常は永続的です
git checkout
を使用してコミット履歴を移動し確認します。git revert
は、共有のパブリックな変更を元に戻すときに使用するのが最適git reset
は、ローカルのプライベートな変更を元に戻すときに使用するのが最適
主な元に戻すコマンドに加えて、他の Git ユーティリティを見てみましょう。失われたコミットを探すための git log、コミットされていない変更を元に戻すための git clean、ステージング・インデックスを修正するための git add があります。
これらのコマンドは、それぞれ詳しく説明されています。ここで説明した個々のコマンドの詳細については、対応するリンク先を参照してください。
この記事を共有する
次のトピック
おすすめコンテンツ
次のリソースをブックマークして、DevOps チームのタイプに関する詳細や、アトラシアンの DevOps についての継続的な更新をご覧ください。