WHAT'S NEW?
Loading...

Gitをおさらいしよう【4/6】

さて、前回はGitでの作業の基本的な部分をおさらいした。
今回は少し作業をすすめてみて、いくつかのバージョンを作った上で、そのバージョンに関する操作を中心に進めていこうと思う。

■ファイルを更新しよう

さっそくindex.htmlを開き、内容を少し変更してみよう。
前回の内容はhtmlタグとbodyタグのみだったが、間にheadタグを挟んでみる。


文字コードをcharsetアトリビュートでutf-8に設定し、その後titleタグでページ名を指定している。この場合は『mygit』だ。ブラウザのタブに表示されてるはず。



この状態でGit Bashを見てみよう。まずはgit statusだ。


赤い文字で『modified』、そして『index.html』と表示されたはずだ。
これは前回index.htmlをバージョン管理下に置いてない場合に表示された『index.html』とは意味が若干違う。冒頭の『modified』を見てもらえればわかるが、この場合はすでにバージョン管理下に含まれているけど、最新バージョンと内容が違ってる、つまり編集された状態ということを表している。事実headタグを追記したので、編集された状態というのは正しい。

そしてここでよくある間違いが、『以前バージョン管理下にファイル追加したから、編集後は追加しないでいい』という勘違いだ。追加は、ステージングにあるインデックスというメモに、必ず追加しなければいけないが、普通の作業でもそれは同じだ。何しろGitはこのインデックスを常に見ているのだから。

というわけで、この編集作業をステージングに反映させよう。前回と同じく、『git add index.html』でOKだ。


次にコミットする前に、ファイルを追加してみよう。
フォルダ内で右クリックし、新規作成でテキストファイルを作ろう。ファイル名は『home.html』で。
内容はindex.htmlの中身をコピーして貼り付け、4行目を編集、8行目を追記だ。


ブラウザで見てみるとこんなふうになる。


さて、早速Git Bashでステータスを確認しよう。git statusだ。


index.htmlを追加した時と同じように、ただファイル名だけが赤く表示されている。
このhome.htmlをバージョン管理下に置くには、何度も叩いている『git add』コマンドで行うわけだ。この場合は『git add home.html』になる。
早速追加してみよう。追加したら『git status』でステータスを確認してみよう。以下のようになったはずだ。



そう、もう気づいたと思うけど、ステージングされてるファイルは、緑で表示されるんだ。
つまりこの緑のファイル名は、コミット待ちということになる。
ここで『git commit -m 』でコミットすると、バージョンが作られることになるんだけど、前回と同じことをしてもしょうがないので、別の作業をして見ようと思う。

■ステージング後に作業を取り消す

実はこのhome.html、コミット前に必要がなかったということがわかった。その場合、ステージングにあるインデックスの中から、このnew fileであるhome.htmlを消しておかないと、不要なファイルがコミットされてしまうことになる。
まぁ後から消して、消した状態をコミットすればいい話なので、それほど神経使うような話でもないんだけどね。

とは言え、もしかしたら仕事に関係ない内容が記述してある、例えば個人情報のファイルとか、そういうものを間違えて『git add -A』などで全部ステージングしてしまった場合など、他の人に知られてしまうとまずい場合など、痕跡自体を消したくなるはずだ。

というわけで、ステージングにある作業内容を操作してみよう。コマンドは『git reset home.html』だ。
この場合、『home.html』だけがステージングする前に戻る。もちろんステージングする前は追加すらされてなかったので、『git status』で見てみると、以下のように、赤い文字で表示される。つまり追加前に戻ってくれるわけだ。


さて、これで現状、編集したindex.htmlだけがステージングされていることになる。

ここではhome.htmlは内容を編集し、個人情報などの余計な記述を削除した状態に訂正したと仮定しておこう。
再度、『git add home.html』で追加しておこう。

■コミット後に作業を取り消す

現状、『git status』で見てみると、以下のようになってるはずだ。


さて、この状態でローカルリポジトリにコミットしてみようと思う。
『git commit -m "edit index.html, add home.html"』的なメッセージを入れてコミットしよう。

こんな画面になったはずだ。


『[master 8c47ald]』と書いてある部分に注目しよう。

『master』というのは、現在自分が作業しているGitの領域名と思って構わない。後述するが、この作業領域はいくつでも持つことができるんだ。例えば『ちょっと試したい事があるんだけど、ためした後にソースコード元に戻すのが面倒だから、別領域つくってそっちででやるか』なんて事が気軽にできてしまう。もちろんうまく行けば、もとの作業領域とくっつけることも可能だ。この作業領域を『ブランチ』と呼ぶ。初回の作業領域はGitが自動的に『master』と名づけてくれる。

そして実はGitはバージョンと言っても、1、2、3の様に、数字の連番でバージョンをつけているわけじゃない。

これは、例えばAさんBさんがそれぞれ自分のローカルリポジトリで作業してる時、最終的にAさんBさん全員のバージョンを1つにマージする場合、Aさんのバージョン2とBさんのバージョン2が同じであるとは限らないためだ(と言うよりほとんど違うであろうね)。つまりバージョン番号自体が衝突してしまう、というのを避けるために、連番にはなっていない。

連番になってない場合、どうなっているのかというと、ハッシュ値になってる。可能な限り世界中でぶつかり合わないであろう、衝突しないであろう『ハッシュ値』というのを使ってるわけだ。そのハッシュ値は例えば40桁だったりする。※ハッシュ化にはsha1というアルゴリズムが使われている

40桁をいちいち画面に表示してたら極めて見難い。というわけで、Gitでは先頭の8文字だったらそれほどぶつからないでしょ、というわけで、先頭8文字だけを表示している。この『8c47ald』というのがその8文字だ。

さて、このハッシュ値でできたバージョン番号がわかれば、好きなバージョンへ戻すのが楽になる。
実際に今まで行ってきたコミットの履歴を見てみよう。Gitにはログを観る機能がある。『git log』だ。

以下の様な情報が表示されると思う。上にいくほど新しい情報だ。現在は2個の履歴がある。


ドスのきいた黄色で表示されてる『commit』行がコミット名、つまりバージョン番号。
その下の『Author』行には『git config』で付けたuser.nameとuser.emailが表示される。
更に下の『Date』行には、コミットされた年月日時分秒が表示される。GMT表記なので、日本だと+9時間して表示するのは言うまでもないかな。
で、その下に、コミットした時のメッセージが表示されるわけだ。
つまりここに表示される内容が、『今日はここまで』とか『幾つか追加』、『あとよろしく』みたいな内容だと、確実におかしいことになるよね。
2013年8月23日の1時に『あとよろしく』ってなんだよ?ってなる。メッセージには、行った作業内容を『正確』に、そして『簡潔』に残すように心がけよう。


というわけで、『git log』でバージョン番号を確認することができた。

ちなみにこのバージョン番号、具体的な呼び方だけど、個人的にはコミット名やハッシュ値でいいと思う。

さて、『git log』では現在2個の履歴がある。これを最初の履歴だけにしてみようと思う。
つまり、index.htmlの編集内容、追加したhome.htmlがなくなり、もとのindex.htmlだけになるということだ。

実はコミット後のリセットにはいろいろな方法があるが、その前に覚えておかないといけない用語がある。
  • HEAD
  • インデックス
  • 作業ツリー
の3つと、
  • --soft
  • --hard
オプションだ。
これらが現在どこの位置にいるのかによって、いろいろなresetの仕方がある。

説明が少々ややこしいので、全部覚える必要はないが、ここでは今後のGitライフに大きく影響する部分でもあるので、しっかりおさらいしておこうと思う。

HEAD最後のコミットの状態
インデックスgit addされた時点の状態
作業ツリー今自分が作業してる状態

HEAD、インデックス、作業ツリーの意味は大体こういうふうに覚えてもらって問題無いだろう。
HEADというのはとにかくコミットしたら最新のものになる、という感じで覚えてればOK。
インデックスは何度もやってるけど、『git add』した時の状態だ。
そして作業ツリーというのはこの場合『mygitフォルダの中身』という感じ。つまり、Gitで管理してようがしてまいが、現在のファイルの状態ってことだ。
これは例えば、『git add』した直後にそのファイルを編集すれば、作業ツリーは別の位置になるという意味になる。

文章だとわかりにくいので、具体的に図にして見た。

例えば3回コミットしたとして、その後何も作業をしていなければ、以下の様な図になる。

この状態で何かファイルを作業したとすると、以下のようになる。


そしてその作業内容を『git add』すると、以下の様な図になる。


そしてこの状態からコミットすると、以下のようになるわけだ。


これでだいたいわかってもらえたと思う。

仮に、2回めのコミットの時点で、一度作業したものをステージングし、そのあと別の作業をしている途中の状態だと、以下のようになる。


さて、このHEAD、インデックス、作業ツリーの3つ存在する状態をもとに戻すのがresetオプションなんだけど、それぞれ何をしたらどれが戻るのか、をまとめてみたいと思う。

--hard 指定なし --soft
HEAD
インデックス
作業ツリー


●を付けたものが戻る、と理解してもらいたい。

さて、戻る、というよりむしろ戻す作業が『reset』なんだけど、ここでは『どこまで戻すのか?』をまだ指定してない。

指定するにはコミット番号を指定して戻す方法があるんだけど、代名詞を使うことも可能だ。
例えば『HEAD』に対して『^』や『^^』をつけることにより、それぞれ『1つ前』、『2つ前まで』と指定が可能になる。
注意するべきところは、『^^』は『2つ前』ではなく、『2つまえまで』という意味になる。つまり『1つ前』も含んでいるということだ。

例えばコミット後、何かファイルを編集して『git add』したとしよう。この場合、以下のようになってるはずだ。

この作業を取り消すためには、インデックスと作業ツリーを対象としなければいけない。
つまり上記の表で言うと、インデックスと作業ツリーを含んでいるオプションとしては、『--hard』になる。そして1個まえに戻すわけなので、対象は『HEAD^』となるわけだ。

コマンドはこのようになる。


上記図の状態で上記コマンドを叩くと、以下の様な図になるわけだ。


さて、現状『mygit』フォルダには、

  • index.html
  • home.html

が存在し、特に編集してない。そして2回コミットを行っているので、以下のようになっているはずだ。

これを(1)に戻すには、『git reset --hard HEAD^』とやってもいいんだけど、何もいじってないので作業ツリーは移動してない。つまり、オプションを付けないで『git reset HEAD^』でもOKだ。

しかし今回はせっかくHEAD、インデックス、作業ツリーの3つの状態を説明したので、作業ツリーを進めてみようじゃないか。

index.htmlを開いて、h1タグを編集してみよう。

7行目を『mygit』から『MyGIT』に編集してみた。
この編集を保存すると、状態は以下になる。

さて、今編集したindx.htmlは閉じて、作業ツリーを取り消してみよう。
『git status』で状態を確認だ。


なんだか編集されたっぽいぜー!とGit様が申しておられる。

作業ツリーだけ取り消す方法は無いので、コマンドは『git reset --hard HEAD^』になる.
そして今回、コミットは2回しかしてなく、2回めのコミットも含めて取り消すことになるので、home.htmlは消えることになる。

早速叩いてみよう。


『HEADがe985811の”add index.html”に戻ったYO!』と言われた。
まず『mygit』フォルダを見てみよう。home.htmlはすでに無いはずだ。そしてテキストエディタでindex.htmlを見てみよう。


今回はhome.htmlも消えてindex.htmlも最初に戻ると説明してからresetしたけど、ここが入門者が『うわー!!』ってなるところなんだよね。

『なんだかresetしたらスゲー前に戻った!!うわー!!』と。

もう一度いうけど、『作業ツリーだけを戻すコマンドは無い』のですよ。はい。

戻せるのは、
  • HEAD
  • HEAD+インデックス
  • HEAD+インデックス+作業ツリー
の3パターン。

補足(2013.08.24):上記例に限った話ではあるけど、戻す位置を『HEAD^』としたので(1)に戻ったけど、単純に『HEAD』とだけしておけば、HEADもインデックスも作業ツリーも(2)の位置にリセットすることが可能だ。つまりコマンドとしては『git reset --hard HEAD』となる。この説明を先にすれば良かったとちょっと後悔。すまぬ。

■まとめ

ごちゃごちゃと細かく説明したけど、大体こんな時にはこうすればいいよ、的な一覧も作っておいたので、参考にされたし。

目的 対象 オプション
コミットだけ取り消す HEAD --soft
addも含めて取り消す HEAD、インデックス 指定なし
作業自体を取り消す HEAD、インデックス、作業ツリー --hard

作業の流れとしては、上記表の下から順になると思うので、戻りたい過去への時間適距離が遠ければ遠いほど、戻る内容も多くなる、という感じ。

というわけで、ややこしい話で色々面倒くさいかもしれないけど、いろいろ試してみて、3つの状態は頭のなかで常に意識できるような感じにしておくと、あなたのGitライフが素晴らしい物になると思う。

次回はリモートリポジトリをやろうと思う。