続続・イラストでわかるgit入門の入門:checkoutをする

みなさん、こんにちは!
最近ルーンファクトリー4を始めました志田です。
私にとって牧場物語系はキラーソフトです。

さて今回は、git checkoutについて見ていきたいと思います。
なんだか長くなっちゃってすみません。

前回までのあらすじ

・コミットの流れを分岐させる「ブランチ」機能
・大きな改修はブランチを切って行おう
・ブランチ同士のマージができる

前回までで、基本的なgitの考え方と、ブランチを切って開発するやり方についてご紹介しました。

・・・今までファイルを編集してコミットをする、というところまでしかやっていませんでしたが、
あのときコミットした状態まで戻りたいということ、ありますよね?
バージョンを管理しているんだから、戻れて当然だろ!と思いますよね?

どうやって今の状態から、あのコミットまでこのファイルを戻すんだろう・・・
そんなときに使うのが、この「チェックアウト」という機能です。

チェックアウトとは

普段使っている「チェックアウト」という言葉は、ホテルで退出手続きをとることですね。
では、gitではどうでしょうか。
gitでもやはり、今のファイルの状態やコミットから出て、なにかの手続きをすることだと言えると思います。

実は、ブランチの回でもcheckoutを使っていました。
git checkout <ブランチ名>
ブランチ名を入力することで、そのブランチに切り替えていましたね。

つまり、今いる場所(masterブランチの最新コミット)から出て、作ったブランチの先頭に移動する。
そして、あなたが今いるディレクトリのファイルたちを、
移動先のブランチに保存されているファイルの状態にする、ということを行なっていました。
(ブランチを切り替えると、ファイルの編集状態もそのブランチにあわせて変わりましたね)

そういうわけで、今回もすくえにまさとしさんの仕事ぶりから、
チェックアウトの動きと概念図についてご説明していきたいと思います。

まさとし ホームページを管理する

前回までで、まさとしさんが管理しているウェブサイトのディレクトリはこのような状態になっていました。


$ ls -al
total 8
drwxr-xr-x   4 masa  masa   136  7 26 09:44 .
drwxr-xr-x+ 54 masa  masa  1836  7 26 09:44 ..
drwxr-xr-x  13 masa  masa   442  7 26 09:44 .git
-rw-r--r--   1 masa  masa   104  7 26 09:44 index.html

index.htmlの内容は、ごらんの通り。


ようこそ!アシアルのホームページへ!
PHPでの開発ならまかせてください!

index.htmlを作ったあと、PHPの開発ならまかせろというコピー文を追加したところまで
コミットログに残っています。


$ git log
commit b47f86d31aa5fb14760bf9b23e0d17af1cb8b197
Author: すくえに まさとし <masa@localdomain.com>
Date:   Wed Jul 25 20:00:42 2012 +0900
    コピー文を追加しました
commit cec4380c59d99a31a1977065478672efdecfe4ee
Author: すくえに まさとし <masa@localdomain.com>
Date:   Wed Jul 25 19:59:55 2012 +0900
    index.htmlをつくりました

 

まさとし ファイルを修正する

index.htmlにMonacaの宣伝文を追加するよう頼まれたまさとし。
現在の状況を図で確認しましょう。

図のように、今作業しているディレクトリにあるのは、最新のコミットの内容と同じです。

この今作業しているディレクトリを、「ワーキングツリー」といいます。
では、ワーキングツリー(=今のディレクトリ)にあるindex.htmlを編集していきましょう。


ようこそ!アシアルのホームページへ!
PHPでの開発ならまかせてください!
<Monacaもやってます>

うーん、あんまり冴えないコピーだけど…ひとまず保存です。
まだコミットしていないない状態ですが、ひとまずgit statusの様子を確認してみます。


$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified:   index.html
#
no changes added to commit (use "git add" and/or "git commit -a")

not staged for commit ということで、「前のコミットから状態が変わっているが、まだステージされていない状態」になっています。
では、次の図をみてください。

最新のコミット状態のワーキングツリーから、index.htmlを編集しました。
「ステージされていない状態」というのはつまり、ワーキングツリーでは編集があったものの、
「インデックス」に登録されていない状態だということです。

このインデックスというのは、これからコミットする内容を記録したものです。
インデックスに登録することで、登録したファイルをコミットすることができます。

ではまず、git addコマンドでindex.htmlをインデックスに登録して、status を見ます。


$ git add index.html
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   index.html
#

Changes to be committedという状態になりました。
つまり、index.htmlはインデックスに登録されて、「これからコミットする内容リスト」に登録されたのです。

では、この内容でコミットを行って、コミットログを見てみます。


$ git log
commit 4d2c6649b4ba6f0dba89c0be346ac4025a9fccf9
Author: すくえに まさとし <masa@localdomain.com>
Date:   Thu Jul 26 11:41:50 2012 +0900
    index.htmlにMonacaのコピーを追加
commit b47f86d31aa5fb14760bf9b23e0d17af1cb8b197
Author: すくえに まさとし <masa@localdomain.com>
Date:   Wed Jul 25 20:00:42 2012 +0900
    コピー文を追加しました
commit cec4380c59d99a31a1977065478672efdecfe4ee
Author: すくえに まさとし <masa@localdomain.com>
Date:   Wed Jul 25 19:59:55 2012 +0900
    index.htmlをつくりました

git logに「Monacaのコピー文を追加」というコミットが追加されました。無事にコミットできました!

もう一度、さきほどの図を見てください。

先ほど、インデックスに登録した内容がコミットされ、コミットログには「新しいコミット」として
Monacaのコピー文を追加したものが記録されました。
すると、「最新のコミット」は「Monacaのコピーを追加したコミット」ということになります。

つまり、「最新のコミット」を示すカーソルが移動したということになりますね。
gitでは、最新のコミットを示すカーソルのことを「HEAD」と呼んでいます。
コミットをすることによって、HEADが移動し、最新のコミットを指すようになったということです。

・・・そんな折、まさとしさんの先輩が言いました。
「Monacaもやってますうーだなんてそんなコピー、全然ダメダメ!もっとキャッチーなのないの?」
うう、先輩、いいとこ突くなぁ。やっぱりダメか…
まさとしさんは考えました。そうだ、じゃあこうしよう。


ようこそ!アシアルのホームページへ!
PHPでの開発ならまかせてください!
<Monacaも頑張ってます!>

git statusを確認します。


$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified:   index.html
#
no changes added to commit (use "git add" and/or "git commit -a")

Monacaも頑張ってます!に変更し、保存しました。
今の状態だと、ワーキングツリーのindex.htmlが編集され、インデックスに登録されていない状態ですね。

まさとし checkout

先輩がまさとしさんのディスプレイを覗き、言いました。
「うわあ、、、それはないわ。前のがよかったわー!一旦戻してくれる?」

まさとしさんは、ガックリ。せっかく考えたのに…。index.htmlをもとに戻します。


$ git checkout HEAD index.html
$ git status
# On branch master
nothing to commit (working directory clean)
$ cat index.html
ようこそ!アシアルのホームページへ!
PHPでの開発ならまかせてください!
<Monacaもやってます!>

git checkout を行うことで、ファイルの内容がもとに戻りました。
いったいどういう状態だったのでしょうか?

最新コミットHEADの状態だった、ワーキングツリーのindex.htmlを編集しました。
ですが、この編集をなかったことにし、HEADの状態に戻したかったので、git checkout を行いました。

「gitさん、checkoutしてください。HEADの状態に、index.htmlを戻してください!」
先ほどのcheckoutコマンドは、こういう意味でした。
このコマンドにより、index.htmlはHEADの状態に戻りました。

HEADというのは特殊な状態で、gitのいろいろなコマンドで、
「特に指定がなければHEADを指定したものとみなす」といった動きになっています。
先ほどのcheckoutコマンドも、「git checkout index.html」と打っても同じ意味になります。

まさとしの挑戦

まさとしさんは、小さな疑問をいだきました。
「…HEADより前の状態に戻ることはできるだろうか」
まさとしさんは、git logを見てみました。


$ git log
commit 4d2c6649b4ba6f0dba89c0be346ac4025a9fccf9
Author: すくえに まさとし <masa@localdomain.com>
Date:   Thu Jul 26 11:41:50 2012 +0900
    index.htmlにMonacaのコピーを追加
commit b47f86d31aa5fb14760bf9b23e0d17af1cb8b197
Author: すくえに まさとし <masa@localdomain>
Date:   Wed Jul 25 20:00:42 2012 +0900
    コピー文を追加しました
commit cec4380c59d99a31a1977065478672efdecfe4ee
Author: すくえに まさとし <masa@localdomain>
Date:   Wed Jul 25 19:59:55 2012 +0900
    index.htmlをつくりました

実はこの、commit:の後ろに続くわけのわからん文字列が、それぞれのコミットへついた一意なIDなのです。
gitでは、このようにSHA1ハッシュをIDとしています。

さっきは、git checkout HEAD index.htmlとしていたけれど、一番古いコミットIDを指定してindex.htmlをcheckoutしたらどうなるんだろう?
まさとしチャレンジです。


$ git checkout cec4380c59d99a31a1977065478672efdecfe4ee index.html
$ cat index.html
ようこそ!アシアルのホームページへ!

index.htmlの中身が、指定したコミット番号である一番古い状態に戻りました。
最新の状態に戻してみます。


$ git checkout HEAD index.html
$ cat index.html
ようこそ!アシアルのホームページへ!
PHPでの開発ならまかせてください!
<Monacaもやってます!>

見事、コミット間を行き来することができるようになりました。
git checkoutは、あるコミットの状態から、ファイルをワーキングツリーへ取り出していることだと言えます。

git checkout <ブランチ名> のときは、現在のワーキングツリーへブランチのファイルの状態を取り出していましたし、
git checkout <コミットID> <ファイル名>のときは、指定コミットIDのファイルの状態を取り出していました。
ブランチの移動もcheckoutだったけど?移動と元に戻すコマンドで、一緒では?と疑問に思うかもしれませんが、
よく考えてみると、確かに同じcheckoutコマンドでいいね!となりますね。

まさとし そして改修へ・・・

まさとしさんは新しいコピーをひらめきました。
先輩に見せてもナウいじゃんと好評価。すぐにindex.htmlを編集し、コミットしました。


$ vim index.html
ようこそ!アシアルのホームページへ!
PHPでの開発ならまかせてください!
<Monacaもイケイケゴーゴーです!>
$ git add index.html
$ git comit
$ git log
commit 464b3d82399f29a38ed5f172c3e5c60c85b05ed4
Author: すくえに まさとし <masa@localdomain.com>
Date:   Thu Jul 26 13:42:07 2012 +0900
    Monacaコピー再修正
commit 4d2c6649b4ba6f0dba89c0be346ac4025a9fccf9
Author: すくえに まさとし <masa@localdomain.com>
Date:   Thu Jul 26 11:41:50 2012 +0900
    index.htmlにMonacaのコピーを追加
commit b47f86d31aa5fb14760bf9b23e0d17af1cb8b197
Author: すくえに まさとし <masa@localdomain.com>
Date:   Wed Jul 25 20:00:42 2012 +0900
    コピー文を追加しました
commit cec4380c59d99a31a1977065478672efdecfe4ee
Author: すくえに まさとし <masa@localdomain.com>
Date:   Wed Jul 25 19:59:55 2012 +0900
    index.htmlをつくりました

これで、図の編集・インデックスへ追加・コミット、全ての動作が完了したことになります。

git checkoutは、あるコミットからワーキングツリーへファイルを取得してくるコマンドです。
よく使うコマンドですので、その動きをイメージしながら使ってみてください。
イメージがつけば、他のgitを解説しているサイトや書籍でも、意味が掴みやすくなってくるかと思います。