Githubを使ったプロジェクトで頻繁に使用するコマンドの一つにgit pull(プル)があります。
Githubのリモートレポジトリのコミットをマージするんでしょ?という理解をされている方は多いと思います。その通りです。
ですが、実はgit pullはできることがたくさんあるとても便利なコマンドです。ただし、使い方には癖があり、理解が必要です。
ここではgit pullは具体的にどんな処理をしているのか?引数が無い場合の処理内容やFETCH_HEADとは何か?--rebase
オプションの使い方などについて実例を踏まえてまとめています。
git pullとは何か?
git pullとは、リモートレポジトリの更新内容を取得してきて、現在のブランチに統合(マージ)するコマンドです。
git fetchを使った場合、取得してきたコミットをマージするにはgit merge(あるいはgit rebase)を使います。この、git mergeのマージ作業もまとめて行うのがgit pullです。
なお、オプションに--rebase
をつけることで、git fetch + git rebaseの処理にすることもできます。
コミットのマージまで行うので、cloneやfetchと異なりコンフリクトが発生する可能性があります。
git pullの裏側の処理内容
git pullの処理はgit fetchとgit mergeを同時に実行しているのですが、具体的には以下のようになっています。
$ git pull <リモートレポジトリ名> <リモートのブランチ名>
↑↓ 同じ
$ git fetch <リモートレポジトリ名> <リモートのブランチ名>
$ git merge FETCH_HEAD
FETCH_HEADについは後述しています。
git pullの使い方
git pullの使い方は主に以下の3個です。
git pullの注意点
git pullを使う上で注意しておくべき点がいくつかあります。
どこのブランチで実行するかが重要
GitにはGithubのリモートレポジトリからコミットを取得してくるコマンドに、git pull以外にもgit cloneやgit fetchがあります。
git cloneやgit fetchはローカルレポジトリのどのブランチでコマンドを実行するかは関係ありません。
一方、git pullはgit fetchしてgit mergeまで行うのでどこのブランチで実行するかが大きく関係してきます。
git pullを実行する前に、自分がどのブランチにいるかを確認し、マージしたいブランチにいるかを確認しておくことが重要です。
コンフリクトが発生する可能性がある
git cloneやgit fetchのようにただ情報をとってくるだけではなく、マージ(あるいはリベース)まで行うので、コンフリクト(conflict)が発生することがあります。
コンフリクトが発生した場合は下記をご参考ください。
【Git】git merge(マージ)でconflict(コンフリクト)が発生したファイルの修正方法をわかりやすく解説
リモートレポジトリ名とブランチ名を指定してマージする方法
リモートレポジトリ名とブランチ名を指定したマージのやり方
git pullで現在のブランチで、リモートレポジトリ名とブランチ名を指定してマージするときは以下のようにします。
git pull <リモートレポジトリ名> <リモートのブランチ名>
実例
リモートレポジトリ名「origin」のブランチ「aa」をコミット履歴を、ローカルレポジトリのブランチ「test」にマージする場合例を紹介します。
まずは、マージを実行したいブランチに移動します。
#現在のブランチはmain
$ git branch
aa
* main
test
#マージを実行したいブランチに移動
$ git checkout test
Switched to branch 'test'
#きちんと移動できてるか確認
$ git branch
aa
main
* test
マージを実行したいブランチ(ここでは「test」)にいることが確認できたら、マージしたいコミットを持つリモートレポジトリ名「origin」のブランチ「aa」を指定して、git pullを実行します。
$ git pull origin aa
From https://github.com/xxxxx/rails-test
* branch aa -> FETCH_HEAD
Updating 61d3b4b..0701d9d
Fast-forward
app/controllers/api/pj1/clients_controller.rb | 8 +-
app/javascript/components/Modal.vue | 106 +++++++++++++++++++++++++
app/javascript/components/clients/Clients.vue | 42 +++++++++-
config/initializers/content_security_policy.rb | 12 +--
config/routes.rb | 2 +-
docker-compose.yml | 2 +
6 files changed, 162 insertions(+), 10 deletions(-)
create mode 100644 app/javascript/components/Modal.vue
上記のように表示され、マージが実行されます。
「61d3b4b」から「0701d9d」のコミットを取り込んだことがわかります。
コミットログを確認すると、指定したリモートレポジトリのブランチと対応するリモート追跡ブランチ「origin/aa」と、git pullを実行したtestブランチが同じコミットを指していることがわかります。
$ git log --oneline --graph
* 0701d9d (HEAD -> test, origin/aa, aa) [U]docker-compose add webpack port 3035
* a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
* 6adca49 [A]destroyメソッド & Modal追加
* 61d3b4b [A]ClientEdit.vue
* 00093d1 [F]ClientNewからClientForm.vueを切り出し
なお上記の例では、コミットが分岐していないので、コミットのポインタを前に進めただけ(Fast-forwardしただけ)ですが、コミットが分岐しているブランチでgit pullを行った場合、コミット履歴は以下のようになります。
$ git log --oneline --graph
* 7f02744 (HEAD -> master, origin/master) Merge branch 'ft2'
|\
| * d6eb207 add h3 again
* | 07ea837 add h4
|/
* 788f5b1 remove all marks
分岐したコミット履歴が、マージされ統合されたことがわかります。
リモート追跡ブランチとは何か?(git pullの処理内容)
リモート追跡ブランチとは何か?
Gitではローカルレポジトリのブランチとリモートレポジトリの同じ名前のブランチが直接連携しているわけではありません。
このため、例えば、ローカルレポジトリのブランチでgit pullしたときに、指定したリモートレポジトリのブランチを直接取り込んでいるわけではありません。
ローカルレポジトリには「リモート追跡ブランチ」というものがあり、これがリモートレポジトリの各ブランチと直接連動しているブランチです。
このため、まずリモートレポジトリの指定したブランチの内容をリモートレポジトリに同期し、それから、git pullしたブランチのコミット履歴に、リモート追跡ブランチのコミット履歴をマージするという処理が行われます。
リモート追跡ブランチは「remotes/リモートレポジトリ名/リモートブランチ名」という名前がついているブランチのことです。(例: remotes/origin/main)
リモート追跡ブランチは「git branch -a」で参照できます。-aはall(全てのブランチ)という意味です。
実例:リモート追跡ブランチの参照
$ git branch -a
* aa
main
vue-router
remotes/origin/HEAD -> origin/main
remotes/origin/aa
remotes/origin/main
上記の例だと、リモートレポジトリには「main」と「aa」の2つのブランチがあり、それぞれのリモート追跡ブランチは「remotes/origin/main」「remotes/origin/aa」となっていることがわかります。
なお、「remotes/origin/HEAD -> origin/main」は取得してきた時点で最新のリモートレポジトリのデフォルトブランチを指しています。
「remotes/origin/HEAD」を使うことはほぼないので、あまり気にする必要はありません。
「remotes/origin/ブランチ名」はよく使うので覚えておく必要があります。
FETCH_HEADとは何か?
git pullすると以下のように「FETCH_HEAD」という表示がでます。
$ git pull origin master
From github.com:xxx/git-test
* branch master -> FETCH_HEAD
* [new branch] master -> origin/master
これは、git mergeの前段階として、git fetchでコミット履歴を取得したブランチの最新のコミットがどこかを示す目印です。
FETCH_HEADというポインタをセットしましたという意味になります。
引数を省略してマージ(git pullのみ。上流ブランチの設定)
引数を省略したマージのやり方
git pullでは現在のローカルレポジトリのブランチに「上流ブランチ」を設定すると、引数を省略してgit pullを実行することができます。
git pull
とてもシンプルなコマンドになります。
上流ブランチとは何か?
上流ブランチとは、ローカルレポジトリのブランチ毎に設定するもので、ローカルレポジトリのブランチをリモートレポジトリのブランチと紐づけるものです。
上流ブランチを設定すると、git pushなどのコマンドの引数を省略することができます。
上流ブランチの設定方法
上流ブランチを設定するには、通常のgit pull <リモートレポジトリ名> <リモートのブランチ名>
のオプションとして「--set-upstream
」を付けて実行します。
git pull --set-upstream <リモートレポジトリ名> <リモートのブランチ名>
なお、upstreamは上流という意味です。
実例:上流ブランチの設定
現在のローカルレポジトリの「main」ブランチにおいて、リモートレポジトリ「origin」の「aa」ブランチを上流ブランチに設定する場合は以下のようになります。
$ git pull --set-upstream origin aa
From https://github.com/xxxxx/rails-test
* branch aa -> FETCH_HEAD
Merge made by the 'recursive' strategy.
app/controllers/api/pj1/clients_controller.rb | 10 ++-
app/javascript/app.vue | 8 +-
app/javascript/components/Modal.vue | 106 +++++++++++++++++++++++
8 files changed, 215 insertions(+), 12 deletions(-)
create mode 100644 app/javascript/components/Modal.vue
特に、上流ブランチを設定したという情報は表示されません。
上流ブランチの確認方法
上流ブランチが設定してあるかどうかはgit branch -vv
コマンドを実行することで確認できます。
#上流ブランチがない場合
$ git branch -vv
* main dc53f35 first commit
#上流ブランチある場合
$ git branch -vv
* main dc53f35 [origin/main] first commit
上流ブランチがある場合は、[リモートレポジトリ名/リモートレポジトリのブランチ名] が表示されます。
よって、このローカルレポジトリのmainブランチでgit push
を実行すると、リモートレポジトリ名originのmainブランチにpushを行います。
なお、ブランチ名の横の 数値(例 dc54f35)は最新のコミット番号、末尾の「first commit」はコミットメッセージです。
実例:引数なしのgit pull
まずは上流ブランチが設定されていることを確認します。
$ git branch -vv
aa 0701d9d [U]docker-compose add webpack port 3035
main 21f31d2 [origin/main] [A]test.html.rb
* test 718eea7 [origin/aa: behind 6] [A]Client.vue
「main」ブランチと「test」ブランチの2つに上流ブランチが設定されていることがわかります。
testブランチでgit pullを実行します。
$ git pull
From https://github.com/xxxx/rails-test
* [new tag] v2.0 -> v2.0
* [new tag] v3.0 -> v3.0
Updating 718eea7..0701d9d
Fast-forward
app/controllers/api/pj1/clients_controller.rb | 46 +++++++++-
app/javascript/app.vue | 16 +++-
app/javascript/components/Modal.vue | 106 +++++++++++++++++++++++
app/javascript/components/clients/Client.vue | 24 ++---
12 files changed, 412 insertions(+), 29 deletions(-)
create mode 100644 app/javascript/components/Modal.vue
create mode 100644 app/javascript/components/clients/ClientEdit.vue
引数なしでgit pullが実行できました。
上流ブランチが設定されていない場合はエラーになる
上流ブランチが設定されていない状態で引数なしのgit pullを実行すると「There is no tracking information for the current branch.」という以下のようなエラーが発生します。
(参考)エラー:上流ブランチが設定されていない場合
$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=<remote>/<branch> master
表示されている内容は次の2つです。
・git pull <remote> <branch>
通常通り、リモート名とブランチ名を指定する。または、
・git branch --set-upstream-to=<remote>/<branch> master
で上流ブランチをセットしてくださいとの示唆。
--rebaseオプションでリベースする
--rebaseオプションの使い方
git pullのオプションで「--rebase
」をつけると、git mergeの処理をgit rebaseに変えることができます。
git pull --rebase <リモートレポジトリ名> <リモートのブランチ名>
上流ブランチが設定してある場合は引数を省略できます。
git pull --rebase
git rebaseとは何か?
git rebaseは本質的にはマージではなく、コミット履歴を移動したり修正、削除したりするコマンドです。
マージ前にコミット履歴をきれいにする目的で使用するのが一般的です。(例えば、git rebaseをしてから、プルリクを出すなど)
ただし、他のブランチから分岐した以降のコミットをとってくることができるので、マージと似たような処理をすることになります。
git rebaseをすると、分岐している他のブランチのコミットを分岐した時点からとってきて、自分のコミットの前に付け足すことができます。
枝分かれの枝を根本からポッキっと追って、本流にくっつけるイメージです。
なお、rebaseとは土台を完全に移行するという意味です。このため元のブランチの内容を根っこから、指定したブランチのコミットログの先端にとりつける処理を指しています。
git pull –rebaseの裏側の処理内容
git pull --rebase
の処理はgit fetchとgit rebaseを同時に実行しているのですが、具体的には以下のようになっています。
$ git pull --rebase <リモートレポジトリ名> <リモートのブランチ名>
↑↓ 同じ
$ git fetch <リモートレポジトリ名> <リモートのブランチ名>
$ git rebase FETCH_HEAD
実例:git pull –rebase
現在のコミット履歴が以下のような状態とします。
例えば、現在のブランチが「test」ブランチで以下のようにリモートレポジトリ名「origin」ブランチ名「aa」という上流ブランチが設定されているとします。
$ git branch -vv
aa 0701d9d [U]docker-compose add webpack port 3035
main 21f31d2 [origin/main] [A]test.html.rb
* test 3aeb158 [origin/aa] [F]div-tag
testブランチのコミット履歴は以下のようになっているとします。一番最近のコミット「3aeb158」が上流ブランチ「aa」のコミットから分岐しているコミットです。
$ git log --oneline --graph
* 3aeb158 (HEAD -> test) [F]div-tag
* 00093d1 [F]ClientNewからClientForm.vueを切り出し
* a84cfc5 [A]ClientNew.vue
* 718eea7 [A]Client.vue
この状態でgit pull --rebase
を実行して、上流ブランチのコミット履歴をfetchしてrebaseします。
$ git pull --rebase
From https://github.com/shizen-shin/rails-aa
* [new tag] v2.1 -> v2.1
Successfully rebased and updated refs/heads/test.
「Successfully rebased and updated refs/heads/test.」と表示され、リベースが正しく行われたことがわかります。
コミット履歴を確認すると以下のようになっています。
$ git log --oneline
5095d55 (HEAD -> test) [F]div-tag
0701d9d (second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
a84cfc5 (tag: v2.1) [A]ClientNew.vue
718eea7 [A]Client.vue
もとのコミット履歴に対して、「61d3b4b」~「0701d9d」が追加されています。
また、「a84cfc5」にタグ「v2.1」も追加されています。
過去の履歴が変わったので、これまでのtestブランチの最新のコミット番号が「3aeb158」から「5095d55」に変化していることも注目してください。
(※コミット番号は過去のコミット履歴と紐づいているため、履歴が変わればコミット番号のハッシュ値も変わります)