Gitのリモートブランチと追跡ブランチは違うよ

Tsukasa OISHI

いまさら感がひしひしと伝わってくる今日この頃ですが、ようやくまともにGitを触りはじめています。
Gitについては Learn Git Branching という素晴らしいサイトがあって基本的なところはだいたい理解できました。
ところでGitの本を読んでいると大きくわけて三つのブランチが出て来ます。

* ローカルブランチ
* 追跡ブランチ
* リモートブランチ

最初の頃、追跡ブランチとはローカルブランチが追従しているリモートブランチのことで、つまりはリモートブランチと追跡ブランチは同じものを指していると思っていました。両方ともリモートリポジトリ上にあるものだと勘違いしていて、ここでちょっと混乱していました。
実のところ、追跡ブランチ自体はローカルリポジトリ上にあります。これに気づくといろいろとGitの理解が深まりました。ちゃんとこのことを書いている本ってあまりないですよね。

以下のような状態を考えてみます。

$ git branch
 * master

ローカルブランチにはmasterだけがあります。

git branch コマンドに -a オプションを渡すと、ものの本にはリモートブランチの一覧も表示されるとよく書いてあります。

$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

remotes/origin/masterがリモートブランチのすべてであるように見えます。
このとき、サンフランシスコの同僚がリモートリポジトリに新しいブランチをpushしていたとします。このことをいつ知ることができるのでしょうか。
git branch -a コマンドを何回叩いても、現れるのは上記の通りorigin/masterのみです。

つまり、git branch -a コマンドはリモートブランチの一覧を表示しているわけではないのです。
では何を表示しているのかというと、リモートブランチの情報を持っているローカルリポジトリの中の特殊なブランチの一覧を表示しているのです。この特殊なブランチ、つまり追跡ブランチですが、これ自体はローカルに存在しているのでローカルブランチの一種であるといえます。そのため、最新のリモートリポジトリの状態なんて知らないのです。

最新のリモートリポジトリの情報を取得して追跡ブランチに反映するためには git fetch コマンドを使います

$ git fetch
From github.com:tsukasaoishi/sample
 * [new branch]      redwood_city -> origin/redwood_city

new branch redwood_city -> origin/redwood_city という表記は、リモートリポジトリにある redwood_city というブランチから、ローカルリポジトリにorigin/redwood_cityという追跡ブランチを作ったよ、ということを表しています。

$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/redwood_city

さっきまでなかった、remotes/origin/redwood_cityが表示されました。これがサンフランシスコの同僚が作ったブランチです。このブランチで作業をしたいときは、この追跡ブランチからローカルブランチを作る必要があります。
ブランチを作るコマンドはgit branchですね。

$ git branch redwood_city origin/redwood_city
Branch redwood_city set up to track remote branch redwood_city from origin.

$ git branch -a
* master
  redwood_city
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/redwood_city

git branch redwood_city origin/redwood_city というコマンドは、ローカルブランチredwood_city を、追跡ブランチである origin/redwood_city から作ってね、という意味になります。

git fetchはリモートブランチの最新状態をローカルの追跡ブランチに反映するコマンドです。なので、リモートブランチmasterの最新状態も、ローカルのorigin/masterに反映されています。ここで

$ git checkout master
$ git merge origin/master

というようにしてあげると、最新状態に更新された追跡ブランチorigin/masterを現在作業中のブランチ(上記だとローカルのmasterブランチ)にマージします。結果的に、ローカルのmasterブランチにリモートリポジトリのmasterブランチの最新状態をマージしたことになります。
これってつまり、git pull コマンドが中でやっていることです。