こんにちは、平奥です!
これはTECHSCORE Advent Calendar 2015の23日目の記事です。
はじめに
開発でソースの管理をするのに、分散型バージョン管理システムのGitを使うようになりました。
はじめは慣れていないので、git addしてgit commitしてgit pushするというような基本的な操作しかせず、また慎重に行うので大きな失敗は少ないです。しかし慣れてくるとそうはいきません。
「えっ、何でこーなるの?」
「あっ、間違えちゃった…」
そういうこと多々ありませんか??
そういう場面に出くわした場合に、手助けできるような内容を書きたいと思います。
Gitの基本概念
まずは最低限の基本概念!!ソフトウェアの思想と振る舞いを知ることが一番の上達の近道だと思いますので押さえておくべきです!!
用語解説
はじめに用語を説明します。
リポジトリとは
ファイルやディレクトリの状態を記録する場所です。プロジェクト開始以来の、すべてのリビジョンを集めて記録しているデータベースをリポジトリと呼びます。
リビジョン、コミット
プロジェクトの状態を記録した一つの単位を、リビジョンとかコミットとか言う言葉で表します。リビジョンとコミットの違いはあるようですが、意味合いはリビジョン=コミットで考えていても問題ないと思います。
ブランチ
ブランチとは、コミットの履歴の流れを記録したものです。特定のコミットを指し示すことによって、そのブランチの現在の状態、プロジェクトの歴史の中における、ブランチの位置づけを記録したり、変更をした場合に次に作られるコミットがどこに作られるかを示すために使われます。
fix-Aとかfeature-Bとかの矢印がそれぞれの先頭の状態を指し示しています。これらの特定のコミットを指し示した矢印を、ブランチと呼びます。
インデックス
インデックスはコミットするための情報を準備するためのものになります。具体的には変更内容をgit addなどのコマンドでコミット対象にしますが、そのコミット対象を管理するものがインデックスになります。インデックスはステージという言い方もされます。
ワークツリー
リビジョンに記録されている内容を展開したディレクトリ領域をワークツリーと呼びます。git checkoutしてファイルがディレクトリ領域に保存されますが、そのディレクトリ領域のことを指しています。
tips集
それでは以下に例をあげて対処法を書いていきます。
・ログメッセージに変なことを書いてしまった。コミットに追加するファイルを忘れていた
ログメッセージを何も書かずにcommitしたり、恥ずかしいことを書いたりすることたまにありますよね。
そんな時は、
1 |
git commit --amend |
でログを書き換えてしまいましょう。
ちなみにこのコマンドはコミットをやり直すコマンドですので、例えばコミットに追加するファイルを忘れていた場合
1 2 |
git add hoge.js git commit --amend |
でコミット内容を修正することができます。
・一気にいろんな修正をしてしまったが、それぞれ別々にコミットにして履歴を残したい
1 |
git add -p |
を使ってインデックスを更新する。
このコマンドを使うと、変更箇所を対話形式で確認してくれて、インデックスに追加するかの選択をすることができます。
1つのファイルであっても部分的にインデックスに追加することができます。
ですので今回のコミットではAの修正分だけをインデックスに反映して、次回のコミットではBの修正分をインデックスに反映してコミットするというようなことができます。
また、たとえばテストコードなどを実装していて、テストコードはコミットしたくないとか、間違った修正をコミットしないように修正内容を確認しながらインデックスに追加したいなどの用途にも使えます。
・ローカルで編集したファイルを元に戻したい
1 |
git checkout <fileName> |
ファイルを修正していて、やっぱり元に戻したい場合に使います。またブランチを切り替えるときにファイルを変更していた時は切り替えられないのですが、git stashで退避するまでもない修正や要らない修正の場合はこのコマンドを使って変更を取り消し、ブランチを切り替えたりします。
またgit addでインデックスを更新してしまっている場合は以下のコマンドでインデックスの変更内容も直前のコミットの状態に戻すことができます。
1 |
git reset --hard HEAD |
そもそもgit resetにはオプションで--soft、--mixed(オプションなし)、--hardとあるのですが、どう違うのか。
--softは今までのおこなってきたコミットのみをなかったことにします
--mixed(オプションなし)はコミットとインデックスに対しておこなった変更をなかったことにします。
--hardはコミット、インデックスの変更、ワークツリーに対して行った変更をなかったことにします。
用途に応じて使い分けてもらえばと思います。
ワークツリーの要らないファイルやディレクトリをすっきり削除したい場合は以下のコマンドを使用します。
1 2 |
git clean -xfd git clean -xf |
-xオプションはgitで通常は無視されるファイルも削除します。
-fオプションはファイルを削除するときに指定します。--forceの略です。
-dオプションは追跡されていないファイルに加え、追跡されていないディレクトリも削除します。
・必要なブランチ、コミットを消してしまった場合
上記で紹介したgit resetで間違えて必要なコミットを消してしまった場合、以下のコマンドで元に戻せます。
1 |
git reflog |
でまずコミット一覧を確認、そして
1 |
git reset --hard <commitID> |
を実行します。
git reflogは過去のあらゆるコミットを参照することができます。
ブランチを消してしまったときは
1 |
git reset --hard <branchName><commitID> |
で元に戻せます。
・ブランチ間の比較をしたい
AブランチにはBブランチから派生してC修正を加えているはずなので、差分はC修正のはずだけど、確認する方法はないかなと思うことがあります。そういう時は
1 |
git diff 'Aブランチ' 'Bブランチ' |
で確認することができます。
これは正確にはコミット間の比較になります。
ですので、以下のコマンドでコミット間の比較も行うことができます。
1 |
git log |
コミットIDの取得をします。
1 |
git diff <commitID-A> <commitID-B> |
・修正していたブランチが目的のブランチと違っていた
ワークツリーやインデックスにした変更は間違いではなく、ブランチを切り替えるのを忘れていて、目的のブランチとは違うブランチに修正を行っていたってことよくあります。そういう場合の解決方法は、まずこのコマンドを試します。
1 |
git checkout <branch> |
branchは目的のブランチを指定してください。
もしこのコマンドが成功して、目的のブランチをcheckoutできたら、何も悩むことはありません。
間違ったブランチに行った内容は目的のブランチに正しくコミットすることができます。
Gitではワークツリーとインデックスにした変更は、どのブランチにも属しているものではないので、目的のブランチとは違うブランチにおこなった変更が、こうやって目的のブランチに移ってコミットしてしまうことができるんです。
しかし以下のようなエラーが出る場合、修正したファイルが間違ったブランチと目的のブランチとで異なっている可能性があります。
1 2 |
git checkout <branch> error: You have local changes to 'ファイル名'; cannot switch branches. |
こういう場合の対処法はいくつかあります。
トピックブランチを始めてマージする
間違ったと判断した時点でトピックブランチを始めてしてマージします。
1 2 3 4 |
git checkout -b 'トピックブランチ名' git commit git checkout '目的のブランチ名' git merge 'トピックブランチ名' |
トピックブランチを始めてチェリーピックする
先ほどのトピックブランチを始めてマージするの最後の手順でマージの代わりにチェリーピックを行います。
チェリーピックとはブランチの登録されたコミットを別のブランチに適用することをいいます。
1 2 3 |
git checkout -b 'トピックブランチ名' git commit git log |
git logでコミットIDを取得します。
1 2 |
git checkout '目的のブランチ名' git cherry-pick <commitID> |
git stashで退避する
git stashを使って今の変更を退避させ、目的のブランチをチェックアウトして変更を反映します。
1 |
git stash save |
まずは変更を退避。
1 |
git checkout '目的のブランチ' |
目的のブランチをチェックアウト。
1 |
git stash pop |
変更内容を反映します。
・マージを取り消したい
マージしたけど、そのマージを取り消したい。そんな時はgit revertを使います。
1 2 |
git merge <branch> git commit |
マージを行いましたが、この時点でやっぱり元に戻したい。。
1 |
git log |
ログでマージのコミットIDを取得
1 |
git revert -m 1or2 <commitID> |
-mオプションは「1がマージされた側のブランチ」「2がマージする側のブランチ」となります。多くの場合はマージされた側のブランチを基に戻すと場合が多いので1を指定することが多いと思います。
まとめ
今まで書いてきた内容である程度は、問題を解決することが出来ると思います。しかしGitは複数人で使った場合にさらに複雑な問題も出てきます。また、ブランチをどのように使うかなどのブランチモデル、git-flowとかも有名ですが、そういう運用も考えたりとかGitの奥は深く、使いこなすと、私たちに素晴らしい恩恵を与えてくれるものであると実感しました。
参考文献
入門Git 濱野 純(Junio C Hamano)