アルパカログ

プログラミングとマネジメントがメインです。時々エモいのも書きます。

【初心者向け】図解でわかるGitのブランチとコミット

最近エンジニアでない人にGitを教える機会がありました。

意欲のある人に教えるのは楽しいもので、とりあえず動かせた方が学ぶのも楽しいだろうと必要最低限のコマンドだけ教えたんですね。

けれどもどうやら、全体像を把握しないままGitを使うのは難しそうです。

思えば私も、初めてGitを触ったとき何が何やらでわからなかったのを思い出しました。

そういうわけで、自戒を込めて、図を使ってできるだけわかりやすくGitのブランチとコミットについて説明したいと思います。

よく見るあの図のわかりにくさ

Gitについて調べると、よく次のような図を目にします。

f:id:otoyo0122:20190714102011p:plain
Gitでよく見る図

これは私がGitを教えるにあたって作った図ですが、Gitのわかりにくさの1つはこの図にあると私は思っています。

なぜかと言うと、Gitのリポジトリはリモートとローカルの2種類あることが暗黙の了解になっているからです。

ちなみにGitリポジトリは、ファイルの変更をひとまとめに管理したいプロジェクトだと思ってください。人によってはレポジトリと言ったりもします。

ローカルとリモート

f:id:otoyo0122:20190714105108p:plain:h300
ローカルとリモート

私たちが作業しているPCの環境を、手元にあるという意味でローカルと言い、ローカルにあるリポジトリのことをローカルリポジトリと言います。

ローカルリポジトリと言うとややこしく聞こえますが、要は作業用のフォルダです。

一方GitHubの方は、インターネットを介してアクセスするという意味でリモートリポジトリもしくは単にリモートと言います。*1

チェックアウトのイメージ

Gitを使って開発を始めるとき、最初にブランチをチェックアウトします。「ブランチを切る」と言ったりします。

リモートのmasterブランチからブランチを切るのは、次のようなイメージです。

f:id:otoyo0122:20190714102759p:plain
チェックアウトのイメージ

ここで origin/master というブランチが出てきました。

Gitではリモートとローカルで同じ名前のブランチが存在することがあります。

そんなとき混乱しないように、「リモートの」という意味でブランチ名の前に「origin/」をつけます。*2

コミットのイメージ

コミットする」とは、あなたの変更を履歴に加えることです。コミットすると、あなたの変更の履歴が「コミット」として記録されます。

f:id:otoyo0122:20190714160706p:plain
コミットのイメージ

しかし、追加されたコミットは、この時点ではまだあなたのローカルにしかありません。

コミット、つまり変更履歴を他の人が見えるようにするためには、リモートへアップロードする必要があるわけです。

Pushのイメージ

ローカルのコミットをリモートへアップロードするには、ブランチごとアップロードする必要があります。

ブランチをリモートへアップロードすることを、Gitでは「Pushする」と言います。

f:id:otoyo0122:20190714110333p:plain
Pushのイメージ

ブランチをPushすると、同名のブランチがリモートにも存在することになります。リモートブランチは origin/ を付けて区別します。

変更をリモートに反映したことでPull Requestの準備ができました。次はPull Requestの仕組みを見ていきましょう。

Pull Requestのイメージ

あなたの変更はリモートに反映されましたが、まだ正式に採用されてはいません。

なぜなら、リモートのmasterブランチ(origin/master)に反映されて初めて正式採用となるからです。*3

当然、誰でも好き勝手に正式採用できると、バグが潜んでいたり、品質の低いコードが採用されてしまうかもしれません。

そうならないために、「正式採用してくれませんか?(しませんか?)」というコミュニケーションの仕組みを提供するのがPull Requestです。

f:id:otoyo0122:20190714110848p:plain
Pull Requestのイメージ

Pull Requestは頭文字を取ってPRと呼ばれたり、人によってはプルリクと呼ばれたりします。

コードレビューにパスしてPRがマージされると、あなたの変更がリモートのmasterブランチに反映され、晴れて正式採用となるわけです。


ここまでGitとGitHubの基本的な仕組みを説明しました。

次はもう少し踏み込んで、コミットが変わるとはどういうことかを見ていきます。

コミットが変わることを理解すれば、Pushしたときになぜエラーが起こるのかがわかるようになります。

コミット固有のID

f:id:otoyo0122:20190714112441p:plain
コミットには固有のIDがある

コミットには、コミット固有のIDが自動で割り振られます。

この固有のIDは、40桁のランダムな英数字で、そのパターンは世界中の砂つぶの数よりも多いと言われています。

Gitはこの固有のIDによってコミットが同じかどうかを識別します。

f:id:otoyo0122:20190714113509p:plain
固有のIDでコミットを見分けている

コミットのIDは変わるケースがある

コミットのIDは、そのコミット固有のものと説明しました。というのも、コミットし直したり、並べ替えたりすると変わるからです。

f:id:otoyo0122:20190714113102p:plain
コミットのIDは変わるパターンがある

PushしたコミットのIDを変えてしまうとどうなる?

Gitはコミットを固有のIDによって見分けていると言いました。

すでにリモートにPushしたコミットを、ローカルで変更し、再度Pushするとどうなるでしょうか?

f:id:otoyo0122:20190714113716p:plain
PushしたコミットのIDを変えるとエラーになる

リモートではcccの次はdddになっているのに、ローカルではcccの次はddd'になっています。辻褄が合いません。

このとき error: failed to push some refs というようなエラーになってしまいます。

Gitを使うにあたって大事なことは、「一度PushしたコミットのIDを変えない」ということです。

慣れないうちは、修正があるならコミットを修正するのではなく、修正を新しいコミットとして追加しましょう。

コミットの修正は、上手く使えばコミットを整理しレビューしやすくするための強力な機能ですが、Gitの使い方に慣れるまでは使わなくても良いでしょう。

Gitを使うときは、リモートとローカルの差を頭に入れておき、辻褄が合うように意識するということが大事です。

おわりに

エンジニアでない人にとって難しいGitのブランチとコミットについて説明しました。

もし参考になりましたらGitで悩んでいる人にこの記事を共有いただければ幸いです。

*1:もちろんGitHub以外にもリモートリポジトリを提供するサービスはあります。

*2:最初にcloneしたリポジトリが自動的にoriginになります。他の名前を付けることもできます。

*3:運用によってmasterという名前ではないこともあります。