Githubを使ったプロジェクトで頻繁に使用するコマンドの一つにgit fetch(フェッチ)があります。
Githubのリモートレポジトリの情報をとってくるんでしょ?という理解をされている方は多いと思います。
ですが、git fetchは実はできることがたくさんあるとても便利なコマンドです。ただし、使い方には癖があり、理解が必要です。
ここではgit fetchでできることについて実例を踏まえてまとめています。
git fetchとは何か?
git fetch(フェッチ)はリモートレポジトリのコミット履歴をとってきて、ローカルレポジトリにコピーするコマンドです。
ローカルレポジトリのリモート追跡ブランチのコミット履歴をアップデートするだけで、マージは行いません。
なお、「fetch」は取ってくると言う意味です。
リモート追跡ブランチとは何か?
Gitではローカルレポジトリのブランチとリモートレポジトリの同じ名前のブランチが直接連携しているわけではありません。
このため、例えば、ローカルレポジトリのmainブランチで作業しているときに、git fetchを行うと、コミット履歴が更新されるのはローカルレポジトリのmainブランチではありません。
リモートレポジトリの各ブランチと直接連動しているブランチは他にあり、そのブランチのことを「リモート追跡ブランチ」と呼びます。
リモート追跡ブランチは「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/ブランチ名」はよく使うので覚えておく必要があります。
git fetchの使いどころやメリット
他のプロジェクトメンバーが作業進めたかな?メインのブランチ(mainやmasterなど)でアップデートがあったかな?といったことを調べたいときに使います。
git fetchした後は、ローカルレポジトリのコミット履歴がアップデートされるので、必要に応じてgit mergeやgit rebaseを実行してコミットを取り込むことができます。
強制的にマージまで実行してしまうgit pullと違って、自分でマージのタイミングを選べるのもgit fetchのポイントです。
リモートレポジトリのブランチの状態をそのまま再現できる。
git fetchの使い方
git fetchは主に次の5つの使い方があります。
それぞれのコマンドは以下のようになります。
コマンド | コマンド例 | 内容 |
---|---|---|
git fetch(引数無し) | git fetch | 取得対象のリモートレポジトリの優先度: 「上流ブランチ」 > 「origin」 |
git fetch <リモートレポジトリ名> | git fetch second | 指定したリモートレポジトリの最新のコミット履歴をまるごと取得 |
git fetch <リモートレポジトリ名> <ブランチ名> | git fetch origin main | 指定したリモートレポジトリの指定したブランチのみの最新のコミット履歴を取得 |
git fetch <リモートレポジトリ名> <ブランチ名>:<生成するブランチ名> | git fetch origin main:test | 指定したリモートレポジトリの指定したブランチのみの最新のコミット履歴を取得して、新たなローカルブランチを作成 |
git fetch --all | git fetch --all | 全てのリモートレポジトリの全てのブランチの最新のコミット履歴をとってくる |
引数なしのgit fetch
引数なしのgit fetchの使い方
git fetchを引数なしで実行した場合は、実行したブランチに上流ブランチが設定してある場合は、上流ブランチの最新のコミット履歴を取得してきます。
上流ブランチが設定されておらず、リモートレポジトリ名に「origin」が登録されている場合は、「origin」というリモートレポジトリのすべてのブランチの最新のコミットをまるごととってきます。
git fetch
上流ブランチとは、ローカルレポジトリのブランチ毎に設定するもので、ローカルブランチをリモートレポジトリのブランチと紐づけるものです。
上流ブランチを設定すると、git pushなどのコマンドの引数を省略することができます。
実例
まずはローカルレポジトリに登録してあるリモートレポジトリ名確認します。上流ブランチの有無も含めて確認する場合は git remote -vv
を実行します。
$ git remote -vv
origin https://github.com/xxxx/rails-test.git (fetch)
origin https://github.com/xxxx/rails-test.git (push)
上流ブランチはなしでリモートレポジトリ「origin」があります。このためgit fetchを実行すると「origin」の全てのブランチの最新のコミット履歴をとってくる処理になります。
git fetch
remote: Enumerating objects: 89, done.
remote: Counting objects: 100% (89/89), done.
remote: Compressing objects: 100% (40/40), done.
remote: Total 89 (delta 46), reused 80 (delta 38), pack-reused 0
Unpacking objects: 100% (89/89), done.
From github.com:xxx/git-test
8fccfd033..2a06b79c8 hotfix -> origin/hotfix
6115dc29d..1f60a6761 develop -> origin/develop
* [new branch] ft1 -> origin/ft1
* [new branch] test -> origin/test
大きく2つの記述があることがわかります。(なお更新内容が無い場合は上記のような表示はありません)
既存のリモート追跡ブランチのコミット履歴を更新
1つ目はコミット番号とブランチ名が表示されているものです。
8fccfd033..2a06b79c8 hotfix -> origin/hotfix
これは以下のような記述になります。
<更新していない最初のコミット番号>..<最新のコミット番号> <リモートレポジトリのブランチ名> -> <リモート追跡ブランチ名>
つまり、既にリモート追跡ブランチとして存在している「hotfix」ブランチにコミット番号「8fccfd033」から「2a06b79c8」までが追加されたという内容です。
新たなリモート追跡ブランチを作成
2つ目は[new branch]と記載があるものです。
* [new branch] ft1 -> origin/ft1
これは以下のような記述になります。
* [new branch] <リモートレポジトリのブランチ名> -> <リモート追跡ブランチ名>
つまり、リモートレポジトリに新たに「ft1」というブランチが追加されているので、「origin/ft1」というリモート追跡ブランチを新たに生成しましたという意味です。
git fetchで上流ブランチを設定する方法
git fetchで上流ブランチを設定するには、通常のgit fetch <リモートレポジトリ名> <リモートのブランチ名>
に「--set-upstream
」オプションを付けて実行します。
git fetch --set-upstream <リモートレポジトリ名> <リモートのブランチ名>
なお、upstreamは上流という意味です。
初回の実行で上流ブランチをセットすれば、後は引数無しのgit fetchコマンドが使えます。
リモートレポジトリ名を指定したgit fetch
リモートレポジトリ名を指定したgit fetchの使い方
リモートレポジトリが複数登録してある場合など、対象のリモートレポジトリを指定してgit fetchすることもできます。
その場合は第2引数でリモートレポジトリ名を指定します。
git fetch <リモートレポジトリ名>
実例
まずはローカルレポジトリに登録してあるリモートレポジトリ名確認します。
$ git remote -v
origin https://github.com/xxxx/rails-test.git (fetch)
origin https://github.com/xxxx/rails-test.git (push)
second https://github.com/yyyy/git-test.git (fetch)
second https://github.com/yyyy/git-test.git (push)
「origin」と「second」という名前のリモートレポジトリが2つ登録されていることがわかります。
git fetchで「second」のリモートレポジトリの全てのブランチの最新のコミット履歴をとってきます。
git fetch second
remote: Enumerating objects: 89, done.
remote: Counting objects: 100% (89/89), done.
remote: Compressing objects: 100% (40/40), done.
remote: Total 89 (delta 46), reused 80 (delta 38), pack-reused 0
Unpacking objects: 100% (89/89), done.
From github.com:yyyy/git-test
* [new branch] aa -> second/aa
* [new branch] main -> second/main
上記の場合は、git fetchの結果、新しいリモート追跡ブランチ「second/aa」と「second/main」が追加されたことがわかります。
実際にブランチを確認すると以下のようにリモート追跡ブランチが生成されているのがわかります。
$ git branch -a
* aa
main
vue-router
remotes/origin/HEAD -> origin/main
remotes/origin/aa
remotes/origin/main
remotes/second/aa
remotes/second/main
リモートレポジトリが存在しない場合はエラーになる
指定したリモートレポジトリが存在しない場合は「fatal: ‘リモートレポジトリ名’ does not appear to be a git repository」「fatal: Could not read from remote repository.」というエラーが表示されます。
$ git fetch no
fatal: 'no' does not appear to be a git repository
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
リモートレポジトリ名とブランチ名を指定してgit fetchする
リモートレポジトリ名とブランチ名を指定したgit fetchの使い方
「全てのブランチの情報ではなく、特定のブランチの更新履歴だけが欲しい」という場合は、リモートレポジトリのブランチ名を指定することもできます。
git fetch <リモートレポジトリ名> <リモートレポジトリのブランチ名>
実例
まずはローカルレポジトリに登録してあるリモート追跡ブランチを確認します。
$ git branch -a
* aa
main
vue-router
remotes/origin/HEAD -> origin/main
remotes/origin/aa
remotes/origin/main
remotes/second/aa
remotes/second/main
この中で、リモート追跡ブランチ「remotes/origin/main」すなわちリモートレポジトリ名「origin」ブランチ名「main」のみを更新したい場合は、git fetchの第1引数に「origin」、第2引数に「main」を指定します。
$ git fetch origin main
From https://github.com/xxxxx/rails-test
* branch main -> FETCH_HEAD
上記のように表示され、指定したリモート追跡ブランチの情報のみが更新されていることがわかります。
ブランチが存在しない場合はエラーになる
指定したブランチが存在しない場合は「fatal: couldn’t find remote ref ブランチ名」というエラーが表示されます。
$ git fetch second no
fatal: couldn't find remote ref no
リモートレポジトリ名とブランチ名と、生成するブランチ名を指定してgit fetchする
リモートレポジトリ名とブランチ名と、生成するブランチ名を指定したgit fetchの使い方
リモートレポジトリ名とブランチ名と、生成するブランチ名を指定してgit fetchすると、指定したリモートレポジトリのブランチの内容で、新たなブランチをローカルに生成することができます。
git fetch <リモートレポジトリ名> <リモートのブランチ名>:<ローカルに生成するブランチ名>
なお、ローカルに指定したリモートの追跡ブランチが存在する場合は、指定したローカルブランチのみを生成します。
どちらもない場合は、(1)追跡ブランチと(2)指定したローカルブランチの2つを作成します。
実例
リモートレポジトリ名「origin」のブランチ「master」を、ローカルレポジトリに「test」というブランチ名として生成する場合は以下のようになります。
$ git fetch origin master:test
remote: Enumerating objects: 70, done.
remote: Counting objects: 100% (70/70), done.
remote: Compressing objects: 100% (31/31), done.
remote: Total 70 (delta 41), reused 62 (delta 34), pack-reused 0
Unpacking objects: 100% (70/70), done.
From github.com:xxxx/rails-test
* [new branch] master -> test
* [new branch] master -> origin/master
新たにローカルブランチ「test」と、リモートブランチ「origin/master」が生成されたことがわかります。
ブランチの状態を確認すると、それぞれのブランチが生成されていることがわかります。
$ git branch -a
test
remotes/origin/master
全てのリモートレポジトリの全てのブランチの情報をとってくるオプション(--all)
--allオプションの使い方
引数無しのgit fetchを実行した場合、上流ブランチがある場合や、リモートレポジトリ名「origin」がある場合といった制約がつきます。
細かいことは抜きにして登録してある全てのリモートレポジトリの全てのブランチの最新のコミットを取ってきたい場合は「--all
」オプションを使用します。
git fetch --all
実例
ローカルレポジトリに登録してあるリモートレポジトリ名が以下のような状態だとします。
$ git remote -v
origin https://github.com/xxxx/rails-test.git (fetch)
origin https://github.com/xxxx/rails-test.git (push)
second https://github.com/yyyy/git-test.git (fetch)
second https://github.com/yyyy/git-test.git (push)
登録してあるリモートレポジトリは「origin」と「second」の2つです。この状態で–allオプションをつけたgit fetchを実行すると次のようになります。
$ git fetch --all
Fetching origin
Fetching second
このように、「origin」と「second」の両方とものリモートレポジトリをfetchしていることがわかります。
サブモジュールがある場合のgit fetch
サブモジュールがある場合のgit fetchの処理内容
リモートレポジトリにサブモジュールが存在するときに「git fetch」を実行すると、メインのリモートレポジトリの内容だけでなく、サブモジュールの内容も取得することができます。
実例
$ git fetch
remote: Enumerating objects: 193, done.
remote: Counting objects: 100% (193/193), done.
remote: Compressing objects: 100% (79/79), done.
remote: Total 193 (delta 135), reused 156 (delta 114), pack-reused 0
Receiving objects: 100% (193/193), 82.78 KiB | 657.00 KiB/s, done.
Resolving deltas: 100% (135/135), completed with 48 local objects.
From github.com:xxxx/rails-pj
8fccfd033..2a06b79c8 test -> origin/test
* [new branch] apc -> origin/apc
888570c19..0ce7f2188 master -> origin/master
+ 98ab8117f...c3d2ff203 test_ga -> origin/test_ga (forced update)
Fetching submodule rails-user
From github.com:xxxx/rails-user
f5bd9fa..e0af519 master -> origin/master
6439adb..e63c502 test -> origin/test
Fetching submodule rails-api
From github.com:xxxx/rails-api
d2ab8a8..865f236 master -> origin/master
+ 302d6ae...8aa0c0e ft -> origin/ft (forced update)
* [new branch] pr -> origin/pr
上記の場合、実行したのはgit fetch
のみですが、メインのリモートレポジトリ「rails-pj」以外に、サブモジュール「rails-user」と「rails-api」の全てのブランチの最新のコミットも取得していることがわかります。
git fetchした内容をマージやリベースする方法
git fetchをした後に、取得してきた内容を作業中のローカルレポジトリのブランチにマージすることが頻繁に発生します。
その場合は更新されたリモート追跡ブランチをマージ、あるいはリベース対象として指定します。
git mergeする方法
git mergeで現在作業中のブランチに、コミットを取得してきたリモート追跡ブランチの内容を取り込みたい場合は以下のようにします。
git merge remote/<リモートレポジトリ名>/<リモートのブランチ名>
実例
例えば、以下のようなブランチの状態だとします。
$ git branch -a
aa
main
* test
remotes/origin/HEAD -> origin/main
remotes/origin/aa
remotes/origin/main
このときに、現在のtestブランチにおいて、リモート追跡ブランチ「remotes/origin/aa」の内容をマージで取り込みたい場合は以下のようにします。
$ git merge origin/aa
Updating a90d4ef..0701d9d
Fast-forward
docker-compose.yml | 2 ++
1 file changed, 2 insertions(+)
すると、git fetchしてきた最新のリモートレポジトリ「origin」の「aa」ブランチの情報をマージすることができます。
git rebaseする方法
git rebaseで現在作業中のブランチに、コミットを取得してきたリモート追跡ブランチの内容を取り込みたい場合は以下のようにします。
git rebase remote/<リモートレポジトリ名>/<リモートのブランチ名>
実例
例えば、以下のようなブランチの状態だとします。
$ git branch -a
aa
main
* test
remotes/origin/HEAD -> origin/main
remotes/origin/aa
remotes/origin/main
このときに、現在のtestブランチにおいて、リモート追跡ブランチ「remotes/origin/aa」の内容をリベースで取り込みたい場合は以下のようにします。
$ git rebase origin/aa
Successfully rebased and updated refs/heads/test.
すると、git fetchしてきた最新のリモートレポジトリ「origin」の「aa」ブランチのコミット履歴を取り込むことができます。