連載
» 2022年07月25日 05時00分 公開

【 git reset 】コマンド(応用編その1)――インデックス登録やコミットをリセットするLinux基本コマンドTips(408)

本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回はファイルをインデックスから削除したり、特定のコミットの状態まで戻したりする「git reset」コマンド(応用編)です。

[西村めぐみ,@IT]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「Linux基本コマンドTips」のインデックス

Linux基本コマンドTips一覧

 本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回はファイルをインデックスから削除したり、特定のコミットの状態まで戻したりする「git reset」コマンド(応用編)です。「git reset」には、「インデックスへの登録」をリセットする用途と、「コミットをリセットする」という用途があります。今回は、コミットをリセットする操作について解説します。

git/git resetコマンドとは?

 「git」は「Git」という分散型バージョン管理システム用のコマンドです。GitはもともとLinuxカーネルのソースコードを管理するために作られた「バージョン管理システム」で、現在は多くのソフトウェアやWebサイトのソースコード、ドキュメントの管理などに用いられています。

 ソースコードを管理する際、最新版だけを保存するやり方はうまくいきません。開発中のさまざまなタイミングで状態を管理し、必要に応じて比較、参照したり、元に戻したりできるようにする仕組みが「バージョン管理システム」です。

 Gitでは、テスト版など複数に枝分かれした状態も管理できます。複数のメンバーによる開発を前提としており、開発中の各時点におけるコメントや、コメントへの返信なども管理できるようになっています。

gitのサブコマンドとGitの仕組み

 gitコマンドはほとんどの場合、「サブコマンド」と組み合わせて利用します(本連載ではgitとサブコマンドの組み合わせをコマンドとして紹介します)。

 今回紹介する「git reset」コマンドは、ファイルをインデックスから削除したり、特定のコミットの状態まで戻したりできます。

 gitコマンドでは「リポジトリ(repository)」を使ってバージョンを管理します。リポジトリにはソースコードや変更履歴、コメントなどを一括して保管します。リポジトリには、自分のPC上に作る「ローカルリポジトリ」と、「GitHub」などのWebサービス上に作る「リモートリポジトリ」があり、両者を連携させることで複数の開発者による開発を1本にまとめることができます。

 また、ローカルリポジトリのみで運用することも可能です。そのような運用をしているリポジトリにリモートリポジトリを追加したり、逆に、リモートリポジトリを削除したりするには、「git remote」コマンドを使用します。「git remote」コマンドには、さらに「add」や「remove」などのサブコマンドがあります。

 既存のリポジトリ(リモートリポジトリ)にあるソースコードなどを入手したい場合は、まず、「git clone」(連載第381回)でリポジトリを自分の環境に複製します(※1)。リモートリポジトリの内容がバージョンアップされたら「git pull」(連載第382回)で最新版を取得します。開発に参加するのではなく、単に最新版を取得したいという場合は、「git clone」と「git pull」を利用すればよいでしょう。

※1 特定のファイルだけが欲しい場合、例えばGitHub(github.com)にあるリポジトリであれば「Raw」というボタンで表示されるURLを使い、「wget」コマンドなどを使ってダウンロードできる。この他、プロジェクト全体をダウンロードするためのリンクも用意されている([Clone or download]ボタン→「Download ZIP」)。



 保管場所であるリポジトリに対し、ファイルの編集などを行う場所を「ワークツリー」「ワーキングエリア」「作業ツリー」などと呼びます。「git clone」や「git pull」で取得した最新版のファイルはワークツリーに配置されます。つまり「作業ディレクトリ」です。

 ワークツリーで編集した結果をリポジトリに反映する操作を「コミット」と呼びます。「git add」(連載第384回)コマンドでコミットしたいファイルを「インデックス」あるいは「ステージングエリア」と呼ばれる領域に追加します。インデックスにはファイルの変更箇所などが記録されます。

 インデックスの内容は「git commit」コマンドでローカルリポジトリにコミットされ、「git push」コマンドでローカルリポジトリの内容をリモートリポジトリに反映します。従って、「git add」や「git commit」などを行わなければ、自分の環境で編集した内容がリポジトリに影響を与えることはありません。自由に編集し、テストできます。なお、ワークツリーのファイルを過去の任意のコミット状態に戻すことも可能です。

 Gitには、この他、開発中のソースコードやドキュメントを、「テスト版」「○○版」……のように枝分かれさせたり、それらを合流させたりする機能もあります。枝分かれしたそれぞれのバージョンを「ブランチ」(branch)と呼び、ブランチを合流させることを「マージ」(merge)と呼びます。

 コミットには「タグ」と呼ばれる名前を付けることができます。その際には「git tag」コマンドを使います。



コマンドの書式

git [オプション] サブコマンド [サブコマンドごとのオプションや引数]

git reset [オプション] [対象] [ファイル……]

※ [ ]は省略可能な引数を示しています。対象にはコミットなどを指定します(デフォルトは「HEAD」)。




gitの主なオプション

短いオプション 長いオプション 意味
-C パス カレントディレクトリではなく指定したディレクトリで実行したものとする
--bare リポジトリを「bareリポジトリ」(ワーキングツリーが存在しない、管理だけを目的としたリポジトリ)として扱う
-c 設定=値 設定値を指定する(設定は「git config」で確認可能)
-p --paginate 全ての出力を「less」コマンドまたは環境変数PAGERで指定されたコマンドで表示する
-P --no-pager 「less」コマンドで表示しない(「-p」の指定を打ち消す)
--exec-path=パス gitの実行ファイルのパスを指定する(「--exec-path」のみの場合、実行ファイルのパスを表示する)
--html-path gitのHTML形式のドキュメントがインストールされたパスを表示する
--man-path gitのmanファイルのパスを表示する
--info-path gitのinfoファイルのパスを表示する

gitのサブコマンド

コマンド 実行内容
clone リポジトリのクローンを作成する
init リポジトリを新規作成する、または既存のリポジトリを初期化する
remote リモートリポジトリを関連付けする
fetch リモートリポジトリの内容を取得する
pull リモートリポジトリの内容を取得し、現在のブランチに取り込む(「fetch」と「merge」を行う)
push ローカルリポジトリの変更内容をリモートリポジトリに送信する
add ファイルをインデックスに追加する(コミットの対象にする)
rm ファイルをインデックスから削除する
mv ファイルやディレクトリの名前を変更する
reset ファイルをインデックスから削除し、特定のコミットの状態まで戻す
revert 指定したコミットを取り消すコミットを実行する
status ワークツリーにあるファイルの状態を表示する
show ファイルの内容やコミットの差分などを表示する
diff コミット同士やコミットとワークツリーの内容を比較する
commit インデックスに追加した変更をリポジトリに記録する
tag コミットにタグを付ける、削除する、一覧表示する
log コミット時のログを表示する
grep リポジトリで管理されているファイルをパターン検索する
branch ブランチを作成、削除、一覧表示する
checkout ワークツリーを異なるブランチに切り替える
merge 他のブランチやコミットの内容を現在のブランチに取り込む
rebase コミットを再適用する(ブランチの分岐点を変更したり、コミットの順番を入れ替えたりできる)
config 現在の設定を取得、変更する

git resetの主なオプション

短いオプション 長いオプション 意味
--mixed インデックスの登録を取り消す(デフォルトの動作、本文を参照
--mixed -N 「git add -N」による登録を取り消す(※2)
-p --patch パッチ形式でインデックスから取り除く。変更の一部だけ取り消したい場合に使用する(「git add -p」の逆の操作、※3)
--soft コミットだけを取り消す(ワークツリーとインデックスの状態を保持する)
--hard インデックスとワークツリーを指定したコミットの状態まで戻す。インデックスやワークツリーに変更があった場合は破棄する
--keep インデックスとワークツリーを指定したコミットの状態まで戻すが、インデックスやワークツリーに変更があった場合は処理を中止する
--merge インデックスとワークツリーを指定したコミットの状態まで戻す。インデックスに追加されていない更新がワークツリーにあった場合は処理を中止する(※4)
-q --quiet エラー以外のメッセージを表示しない

※2 「git add -N」は、後に追加する予定があることを記録するオプションだ。正式に追加するまで「git commit」の対象にはならないが、変更箇所を確認する「git diff」の対象になる。
※3 「-p」(--patch)は他のオプションと同時に指定できない。
※4 現在は、MERGE_HEADファイル(マージが中断されたり、競合するマージがあったりしたときなどに「.git」下に生成されるファイル)が存在するときに、処理を完了する際に使用する。「Error: You have not concluded your merge (MERGE_HEAD exists).」というメッセージが表示された際に「git reset --merge」を実行してリセットすることが多い。





最新のコミットを取り消す

 「git reset --soft コミット」で、指定したコミットの状態にリセットします。「--soft」の場合、リセットされるのはコミットのみで、インデックスやワークツリーの変更はそのまま保持されます。

 例えば、「git reset --soft HEAD^」のように指定することで、1つ前のコミットに戻します(※5)。直前のコミットをやり直したいような場合に便利です。

※5 「HEAD」は最新のコミットを指す。「^」を付けると、指定したコミットの1つ前という意味になるので、「HEAD^」で「最新の1つ前のコミット」を指定できる。

コマンド実行例

git reset --soft コミット

(コミットを取り消して、指定したコミットの状態にリセットする)

git reset --soft HEAD^

(最新の1つ前のコミットにリセットする。最新のコミットが取り消される)


 今回も、連載第407回と同じく、連載第396回で作成したローカルリポジトリとリモートリポジトリを使用しています。

 画面1では、あらためて「git add .」を実行しています。前回編集している「hello.txt」と「index.html」がインデックスに追加され、「git commit」(連載第385回)でコミットされました。「-m」はコミット時のメッセージを指定するオプションです。

 コミット後、「git log --oneline」(連載第386回)でコミットの履歴を確認しています。

 その後で、「git reset --soft HEAD^」を実行して最新のコミットを取り消しています。

画面1 画面1 最新のコミットを取り消したところ


最新のコミット以降の変更を取り消す

 「git reset --hard コミット」で、コミットを取り消して指定したコミットの状態にリセットします。「--hard」の場合、指定したコミット以降の変更を全て破棄します。従って、インデックスやワークツリーの変更を破棄します。

 「git reset --hard HEAD」で、最新のコミット以降の変更を全て破棄することができます(※6)。

※6 「HEAD」は「git reset」のデフォルトの対象のため、省略が可能。

コマンド実行例

git reset --hard コミット

(コミットを取り消して、指定したコミットの状態にリセットする)

git reset --hard HEAD

(最新のコミット以降の編集を全て破棄する。HEADはデフォルトなので省略可能)


 画面2では、現在のHEAD(45a76ee)以降の変更を全てリセットしています。インデックスとワークツリーもリセットされるため、画面1でファイルを変更する前の状態にリセットされている様子が分かります。

画面2 画面2 最新のコミット以降の変更を取り消したところ


コミットを“なかったこと”にする

 Gitの「現在の作業位置」は「HEAD」で示されています。「git reset」は「HEADを指定したコミットへ動かす」という操作を行っています。「--hard」を指定すると、ワークツリーも含めて指定したコミットにリセットされるため、それ以降のコミットが全て破棄されて、いわば“なかったこと”になります(※7)。

※7 操作の履歴としては残っており、「git reflog」で参照できる。画面6を参照。

 ローカルリポジトリのみで運用している場合、コミットを自分だけの判断で“なかったこと”にできますが、リモートリポジトリがある場合、通常であれば、「git revert」(連載第403回第405回第406回)でコミットを取り消すことが、順当な操作です。

 また、過去の状態を参照するのであれば、「git checkout」(連載第391回連載第392回)を使用します。「git checkout」の「-b」オプションを使うと、指定したコミットから枝分かれさせた新しいブランチを作成できます。



(参考)「--keep」と「--merge」

 「git reset --keep」は、「git reset --hard」同様、インデックスとワークツリーを指定したコミットの状態まで戻しますが、「--hard」と違い、インデックスあるいはワークツリーに変更があった場合は処理が中止されます。

 「git reset --hard」の実行に不安があるときは、まず、「git reset --keep」でインデックスやワークツリーに変更があるかどうかを確認する、という使い方ができます。どちらにも変更がない場合は、そのままコミットがリセットされます。

 「git reset --merge」の場合、ワークツリーの変更がインデックスに追加されている場合は破棄されます。---mergeは古いオプションで、現在は主に、「MERGE_HEAD」ファイルが存在するとき、処理を完了させる際に使用されています(※4)。

 具体的には、「Error: You have not concluded your merge (MERGE_HEAD exists).」というメッセージが表示された際に「git reset --merge」でリセットする、という使い方が多いでしょう。

 以下、参考として「--keep」と「--merge」の動作の様子を示します。

 まず、hello.txtを書き換えて、git commitを実行後、git pushでリモートリポジトリにも反映させます(画面3)。

画面3 画面3 --keepと--mergeの動作を確認するための準備を進めているところ

 その後、あらためてhello.txtを書き換えました。ワークツリーのファイルを変更した状態なので、「git reset --keep」や「git reset --merge」では1つ前のコミットにリセットすることができません(画面4)。

 実行後、念のためログなどを確認していますが、何も変更されていないことが分かります。

画面4 画面4 --keep、--mergeとも処理が中止される

 その一方で、「git reset --hard」であればリセットすることができます。リセットを実施すると、インデックスやワークツリーの変更が破棄されていることが分かります(画面5)。

画面5 画面5 --hardであればリセットが可能

 ここで、いったん画面3の状態に戻します(画面6)。コミットがリセットされているため「git log」では履歴を確認できませんが、「git reflog」であれば操作の履歴を参照できます。多数表示されるため、headコマンドで最初の3行だけ表示しました。reflogによると、画面3のコミットは「2516352」だと分かったので、「git reset --hard」で元に戻します。

画面6 画面6 画面3の状態に戻したところ

 あらためて、hello.txtを書き換えます。今度はインデックスにも登録した上で、1つ前のコミットへのリセットを試みます。

 「git reset --keep」の場合、インデックスが変更されている状態に相当するので、リセット処理は中止されます(画面7)。これは先ほどと同じ結果です。

画面7 画面7 --keepの処理を確認したところ

 一方、ワークツリーの変更がインデックスに登録されているため、「git reset --merge」は実行され、1つ前のコミットにリセットできています(画面8)。

 ワークツリーとインデックスの変更は破棄されるので、画面5の「git reset --hard」実行後と同じ状態になっていることが分かります。

画面8 画面8 --mergeの処理を確認したところ --keepとは違う結果になった


筆者紹介

西村 めぐみ(にしむら めぐみ)

元々はDOSユーザー。ソフトハウスに勤務し生産管理のパッケージソフトウェアの開発およびサポート業務を担当、その後ライターになる。著書に『図解でわかるLinux』『らぶらぶLinuxシリーズ』『[新版 zsh&bash対応] macOSコマンド入門』『Accessではじめるデータベース超入門[改訂2版]』など。地方自治体の在宅就業支援事業にてMicrosoft Officeの教材作成およびeラーニング指導を担当。会社等の"PCヘルパー"やピンポイント研修なども行っている。


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。