【 git revert 】コマンド(応用編その1)――コミットを取り消すLinux基本コマンドTips(405)

本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回はコミットを取り消す「git revert」コマンドの応用編です。

» 2020年07月01日 05時00分 公開
[西村めぐみ@IT]

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

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

Linux基本コマンドTips一覧

 本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回はコミットを取り消す「git revert」コマンドの応用編です。連載第403回に引き続き、削除したファイルを元に戻す場合を例に挙げて説明しました。「git revert」コマンドだけではうまくいかない場合に使う「git stash」コマンドについても触れます。

 なお、「git revert」コマンドを使わずに削除したファイルを元に戻す方法については、「git rm」コマンドの応用編を参照ください。

git/git revertコマンドとは?

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

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

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

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

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

 今回紹介する「git revert」コマンドは、「コミットの内容を取り消すためのコミット」を行うコマンドです。コミットの記録を残した上で、ファイルの変更を元に戻すことができます。

 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 revert [オプション] コミット……

※ [ ]は省略可能な引数を示しています。




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 ファイルをインデックスから削除し、特定のコミットの状態まで戻す
status ワークツリーにあるファイルの状態を表示する
show ファイルの内容やコミットの差分などを表示する
diff コミット同士やコミットとワークツリーの内容を比較する
commit インデックスに追加した変更をリポジトリに記録する
tag コミットにタグを付ける、削除する、一覧表示する
log コミット時のログを表示する
grep リポジトリで管理されているファイルをパターン検索する
branch ブランチを作成、削除、一覧表示する
checkout ワークツリーを異なるブランチに切り替える
merge 他のブランチやコミットの内容を現在のブランチに取り込む
rebase コミットを再適用する(ブランチの分岐点を変更したり、コミットの順番を入れ替えたりできる)
config 現在の設定を取得、変更する

git revertの主なオプション

短いオプション 長いオプション 意味
--no-edit コミットメッセージを編集しない(本文を参照
-e --edit コミットメッセージを編集する(デフォルト動作)
-m 番号 --mainline 番号 マージコミットの場合、どのコミットに戻すのかを指定する
-n --no-commit ワークツリーを元に戻すための変更だけを行い、コミットは実行しない


「コミットの内容を取り消す」コミットを実行する

 「git revert コミット」で、指定したコミットの内容を取り消すためのコミットを実行します。ここではタグやコミットのハッシュ値を使ってコミットを指定します。例えば、「git revert HEAD」で先頭のコミット、つまり、直近に行った最後のコミットを取り消すことができます。

 コマンドを実行するとエディタが開くので、コミットメッセージを入力します。「Revert "元のコミットメッセージ"」というメッセージが自動で用意されているため、そのままでよい場合は何もせず、保存して終了します。「--no-edit」オプションを付けて実行することでエディタを開かずに、自動生成されたメッセージを付けてコミットできます。

 なお、チームで開発している場合、コミットの内容を取り消すための運用ルールが設けられている場合があります。必ず確認してください。

コマンド実行例

git revert コミット

(指定したコミットの内容を取り消す、メッセージを入力するためにエディタが起動する)

git revert --no-edit コミット

(指定したコミットの内容を取り消す、デフォルトのメッセージ「Revert "元のコミットメッセージ"」を使用する)

git revert --no-edit HEAD

(直近のコミットの内容を取り消す)


 画面1〜6では連載第396回で作成したローカルリポジトリとリモートリポジトリを使用しています。連載第398回で「hello.txt」の変更を「git commit」「git push」した後の状態から始めています。

 まず、「git revert」を実行する前の状態を用意します(画面1)。「hello.txt」を編集後、「deleted my memo」というコメントを付けて「git commit」を実行しました。次に「git push」でリモートリポジトリに反映させました。最後に、「git log」でコミットの履歴(最新の5件分)を確認しています。

画面1 画面1 「hello.txt」ファイルを編集後、コミットしたところ

 続いて、画面1のコミットを取り消すために「git revert」を実行します(画面2)。

画面2 画面2 直前のコミットを取り消したところ

 最初に「hello.txt」の内容を確認し、ワークツリーの現在の状態を確認しています。「git commit」と「git push」を実行した直後なので、変更はありません(nothing to commit, working tree clean)。

 ここで「HEAD」を指定して直近のコミットを取り消します。自動で生成される「Revert "元のコミットメッセージ"」というメッセージをここでは使用したいので「git revert --no-edit HEAD」としました。「--no-edit」を付けなかった場合はエディタ(vi)が開きます。適宜編集してください。

 「hello.txt」の内容を確認すると、「HEAD」のコミットが取り消されて(revertされて)その前の状態に戻っていることが分かります。「git status」ではコミットが終わっている状態で、「git push」をまだ実行していないことが分かります。最後にコミットログを確認しました。



ワークツリーが変更されていた場合にコミットの内容を取り消す

 「git revert」を実行しようとした時点で、ワークツリーが変更されていた場合、そのままでは「git revert」を実行できません。現在の変更内容をどのようにするのかを先に決める必要があります。

 実際に試してみましょう。画面3では、画面1と同じ操作をしていますが(※2)、「git push」の後に、「hello.txt」の内容を変更しています。つまり、ワークツリーのファイルを更新しています。

※2 テスト環境を事前に「git reset」で元に戻している。「git push」を実行した後で、その前の状態にリセットする方法については連載第403回「リモートリポジトリの変更内容も『なかったこと』にする(git reset)」参照。



画面3 画面3 「hello.txt」ファイルを編集後、コミットしたところ 「git push」後に「hello.txt」を編集している

 この状態で「git revert」を実行すると、エラーになります(画面4)。

画面4 画面4 画面3の後に直前のコミットを取り消したところ エラーが起こった

 最後にあるエラーメッセージでは、「git revert」を実行する前に、現在の変更内容をコミットするか、「git stash」でいったん退避するように促しています。ここでは「git stash」で、現在の変更内容を退避してみましょう(画面5)。無事「git revert」に成功し、1つ前の「hello.txt」に戻りました。

画面5 画面5 現在の変更内容をいったん退避した後、直前のコミットを取り消したところ

 それでは、「git stash」で退避していた「hello.txt」の内容を戻してみましょう(画面6)。

画面6 画面6 退避した内容をワークツリーに戻したところ コンフリクトが起こった

 まず、「git stash list」で退避リストを確認します(※3)。先ほどの「git stash」の結果が入っているのは「stash@{0}」です。

※3 「git stash」は何度でも退避でき、退避リストを利用して退避した内容を選択できる。



 この「stash@{0}」をワークツリーに戻すには「git stash apply stash@{0}」のように指定します。

 今回の場合は、同じ「hello.txt」を編集しているのでコンフリクト(衝突)が発生しました。どこが衝突しているか確認し、適宜「hello.txt」の内容を編集してください。



筆者紹介

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

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


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

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

メールマガジン登録

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