[Git] checkout徹底解説|ブランチを切り替えるだけじゃないよ。

Gitに欠かせないコマンドのひとつである checkout

一番使用頻度が高いのはブランチを切り替えるときですが,実は checkoutはブランチを切り替えるためのコマンドではありません.

この記事が,勘違いを正すきっかけになれば幸いです.

SPONSORED LINK

ブランチを切り替えるだけじゃない

ぼくの思い込みかもしれないですが,「checkout はブランチを切り替えるもの」と勘違いされていることが多い気がします.

Qiitaでも,ちゃんと紹介されずに使われているのをよく見ます.

では,checkout の本来の役割とは何なのでしょうか?

それは,「特定のコミットの状態に移動すること」です.

なので,実はブランチだけでなくコミットを指定することもできます.

$ git checkout <branch>
$ git checkout <commit>

ブランチを勘違いしている人も多い

ここで補足です.

ブランチを勘違いしている人は,ここまで読んでも「?」だと思うので補足します.

ブランチをコミット列だと思っている人が少なからずいると思うんですが,実はブランチはあるコミットのエイリアス (別名) です.

1つのコミットを指しているだけなんですね.

詳しい説明は省きますが,ここでは「ブランチはあるコミットの別名」ということだけ覚えておいてください.

SPONSORED LINK

使用例を見てみよう

checkout は「特定のコミットに移動するコマンド」であることがわかったところで,checkout の使い方を見ていきましょう.

ブランチを移動する

$  git checkout <branch>

ワーキングツリーを,指定したブランチのワーキングツリーに切り替えます.つまり,ブランチの切り替えです.

もっともメジャーな checkout の使い方ですね.

ワーキングツリーやインデックスにコミット前の変更が残っていて,移動先のブランチと競合する場合は,チェックアウトできません (コミットしたことがないファイルなら関係ない).

通常ならコミットまたはスタッシュして変更を退避しますが,-m をつけてマージすることもできます.

$ git checkout -m <branch>

ちなみに,<branch>- (dash) を指定すると,1つ前にいたブランチに移動できます.

ちょっと作業して,また戻ってくるときに便利ですね.

$ git checkout master
$ 作業
$ git checkout feature
$ 作業
$ git checkout - # masterにcheckout 

参考:前のブランチをチェックアウトするのに git checkout の引数としてダッシュを使えるよ – yu8mada

HEADからブランチを切る

$ git checkout -b <new_branch>

現在のコミットから新しくブランチを切るコマンドです.ちょうど,以下の操作に相当します.

$ git branch <new_branch>
$ git checkout <new_branch>

-b-B に置き換えた場合は,同じ名前のブランチが存在しても,それを上書きしてブランチを切ります.

$ git branch -f <new_branch> # -f | --force
$ git checkout <new_branch>

いらないブランチを削除する手間が省けますが,危険なコマンドなので使い方には注意です.

ここで,git checkout <branch> の仕様について少しだけ.

<branch> が,ローカルには存在しないけど追跡ブランチには存在する場合,かつリモートが1つの場合は,git checkout <branch> は次のコマンドと同じ挙動になります.

$ git co -b <branch> --track <remote>/<branch>

つまり,追跡ブランチに同じ名前のブランチがあれば,自動的に結びつけてくれるというわけです.

HEAD以外からブランチを切る

現在のコミットではなく,1つ前のコミットでブランチを切りたいときもありますよね.

そんなときは,新しいブランチの次に,出発点となるコミットを指定すればOKです.

$ git checkout -b <new_branch> HEAD~

特定のコミットの状態にする

$ git checkout <commit>

直接,コミットを指定すればOKです.

が,気をつけないといけないポイントがあります.それは,DETACHED HEADになってしまうこと.

本来,HEADは特定のブランチを指定するものなので,コミットを直接指定することはないんですね.

HEADがブランチを参照していれば「通常のHEAD」,コミットを参照していれば「DETACHED HEAD」というわけです.

ちょうど,次のような感じ.

本来は
HEAD->branch->commit # HEAD
でも
HEAD->commit # DETACHED HEAD

この状態で作業すると,他のブランチに切り替えたら最期.帰ってくることができません.さらに,Gitのガベージコレクションによって削除されてしまいます.

$ git checkout --detach <branch>

参考:Git の ‘detached HEAD’ 状態とその注意点 – yu8mada

参考:detached HEAD から脱出する方法を git の内部構造から探る – Qiita

 

特定のファイルだけ特定のコミットの状態にする

$ git checkout <commit> <filepath>

「あのコミットの,あのファイルがほしい」というときは,コミットの後ろにファイルパスを指定します.

実は,これをうまく使うと削除してしまったファイルを復元することもできます.

間違って削除したファイルを復元する

$ rm -f hello.c
$ git checkout hello.c

これで,間違って消してしまった hello.c を復元できます.

ただし,コミットしたことがない場合は復元できません,少なくともステージしている必要があります.

ちなみに,git checkout hello.cgit checkout HEAD hello.c の省略形です.

ワーキングツリーの変更を削除する

さらに,checkoutを使ってファイルの変更をなかったことにもできます.

$ git checkout HEAD file.txt
または
$ git checkout file.txt

変更前のファイルの状態,つまり,HEADからファイルを持ってきて上書きすることで,変更をなかったことにするんですね.

ブランチ名とファイル名が同じ場合はブランチ名が優先されてしまうので,次のように書く必要があります.

 

$ git checkout -- file

コンフリクトを解消する

$ git checkout --ours <filepath>
または
$ git checkout --theirs <filepath>

--ours はカレントブランチの変更を保持する場合に,--theirs はマージさせたブランチの変更を保持する場合にそれぞれ使います.

実際のコードで説明すると,======= の上が --ours で,下が --theirs です.

<<<<<<< HEAD:file
    aiueo   # --ours
=======
    ouiea   # --thiers
>>>>>>> 34bfe3...:file

コンフリクトしたすべてのファイルに同じ操作をしたいときは,<filepath>. をいれます.git add <filepath>git add . と書くのと同じだと思えばわかりやすいと思います.

$ git checkout --ours .
または
$ git checkout --theirs .

チェックアウトした後は,通常のコンフリクト解消と同じく git add する必要があることに注意です.

最後に

ここまで読んでいただければ,checkoutが「ブランチを切るためのコマンド」という勘違いは解消できたのではないでしょうか?

ここまで理解しなくてもGitは使えますが,いざというときにボロが出ます.自信を持って人に説明できるくらい勉強しておきましょう!

Reference

日本語で,かつマイナーな使い方まで網羅されていてとても参考になります.

→ transitive.info – git checkout 使い方

やはり,一番確実なのは「ドキュメントを読むこと」ですね.

Git – git-checkout Documentation

この方の解説記事にも大変お世話になりました.

yu8mada.comのcheckout関連の記事一覧


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です