これが "TL;DR" バージョンです (多くの特殊なケースを詳しく説明しています):git fetch
常にFETCH_HEAD
updateで、さまざまなケースで複数の行があります。フルネームが で始まる参照である「リモートブランチ」を更新することがありrefs/remotes/
ます。git fetch
残りは主に「時々」に関するもので、 に与えられた引数の数と git バージョンの両方に基づいて異なります。
これをテストする機会がありました。3 つのケースを区別してみましょう。これらはすべて、または などgit fetch
の追加オプションなしで実行することを前提としています。また、URL を直接使用したり、エントリや または にリストされたファイルを使用したりするなど、の変種も除外しましょう。(推測にすぎないことは認めますが、これらはエントリが git の構成ファイルに入る前の日の残り物だと思います。編集、2019: それは正しいことが判明しました。)-a
--all
git fetch
insteadOf
.git/remotes
.git/branches
[remote "name"]
git fetch
、その他の引数はありません。
Git は現在のブランチを決定します (通常の方法では、 を読み取りHEAD
ますが、もちろん、 または でそれが何であるかを確認できますgit branch
) git status
。次に、そのブランチの config エントリを探し、そのremote
. たとえば、あなたがブランチdummy
に.git/config
いて、(他のエントリの中で) 持っているとします:
[branch "dummy"]
remote = remote-X
この場合git fetch
は と同等git fetch remote-X
です。その後は、ケース 2 と同じです。つまり、次のようになります。
git fetch remote
(そしてそれ以上の議論はありません)。
今回は、Git は現在のブランチを参照しません。使用するリモートは、コマンド ラインで指定されたものです。指定されたリモートの構成セクションを探します。を使用しているとしましょうremote-X
: この場合、以下を探します:
[remote "remote-X"]
url = ...
そのセクションが存在しない場合、またはurl =
エントリがない場合は、次のエラーが発生しますfatal: 'remote-X' does not appear to be a git repository
。1 それ以外の場合、URL が提供git fetch
され、そこへの接続が試行されます。接続できると仮定して...
通常、少なくとも 1 つの構成エントリもあり、場合によってはさらに多くの読み取りがあります。
fetch = +refs/heads/*:refs/remotes/remote-X/*
(リモートの名前はここにハードコーディングされています)。あると仮定して...
次に、git fetch
リモートにどの参照があるかを尋ねます (すべての参照を取得することはできますが、ほとんどはブランチとタグですが、ほとんどの人はブランチとタグだけを気にします)。でこれと同じことを自分で行うことができます。これによりgit ls-remote remote-X
、次のようなものが流出します。
676699a0e0cdfd97521f3524c763222f1c30a094 HEAD
222c4dd303570d096f0346c3cd1dff6ea2c84f83 refs/heads/branch
676699a0e0cdfd97521f3524c763222f1c30a094 refs/heads/master
refの扱いはHEAD
完全に一貫しているわけではありません (私はそれが奇妙に振る舞うのを見たことがあります) が、通常ここではドロップされます。2fetch =
残りのブランチは、 refspec に従って名前が変更され、更新されます。(複数のfetch =
refspec がある場合は、それらすべてに従って名前が変更され、更新されます。これは主に、たとえばrefs/notes/
、独自の「リモート タグ」名前空間を に持ち込んだり、作成したりする場合に役立ちrefs/rtags/
ます。)
この場合、fetch は 2 つのブランチbranch
およびmaster
に必要なすべてのオブジェクトを取り込み、必要に応じて (ローカル)「リモート ブランチ」名 および を更新refs/remotes/remote-X/branch
しrefs/remotes/remote-X/master
ます。更新されたものごとにfetch
、次のような行を出力します。
22b38d1..676699a master -> remote-X/master
fetch =
行が欠落している場合、まったく異なる結果が得られます。出力は次のようになります。
* branch HEAD -> FETCH_HEAD
この場合、(欠落している)fetch =
行がそこにあり、含まれているかのようですfetch = HEAD
。
git fetch remote refspec
(refspec
パーツは、以下で説明するように、実際には 1 つ以上の refspec です)。
これはケース 2 に似ていますが、今回のみfetch =
、リモートの構成エントリからではなく、コマンド ラインで "refspecs" が提供されます。ただし、ここではフェッチの動作がかなり異なります。
ちょっと立ち止まって、この特定のケースで refspec を適切に説明しましょう。(refspec も発生しますgit push
が、git ではいつものように、実装の詳細が漏洩し、そこではわずかに異なる動作をします。) refspec にはオプションの先頭のプラス ( +
) 記号がありますが、ここでは無視します。3次に、コロン ( ) で区切られた 2 つの部分:
。多くの場合、どちらも単なるブランチ名ですが、ブランチ名の場合はfetch =
、「完全な」ref-name を綴ることができます (行はそうします) 。refs/heads/branch
フェッチ操作の場合、左側の名前はリモート自体の名前です (git ls-remote
例で示すように)。右側の名前は、ローカルの git リポジトリに保存/更新される名前です。*
特殊なケースとして、 のようにスラッシュの後にアスタリスク ( ) を最後のコンポーネントとして使用できますrefs/heads/*
。この場合、左側で一致した部分が右側で置き換えられます。したがって、(リモートで見られるように)が(ローカルリポジトリで見られるように、短い形式ではラインプリントの右側に)refs/heads/*:refs/remotes/remote-X/*
なる原因です。refs/heads/master
git ls-remote
refs/remotes/remote-X/master
->
git fetch
:
ただし、を入れgit fetch
ないと、「あそこの枝」のコピーを置くのに適した場所がありません。refs/heads/master
リモートのもの(リモートのmaster
ブランチ)を引き継ぐとしましょう。ブランチに独自のコミットがある場合は明らかに悪いことですが、更新する 代わりに、更新を にダンプするだけです。refs/heads/master
master
FETCH_HEAD
ここで、物事が特に厄介になります。を実行するとしますgit fetch remote-X master branch
。つまり、少なくとも 1 つ、場合によっては複数の refspec を指定しますが、すべてコロンは使用しません。
git バージョンが1.8.4 よりも古い場合、更新はFETCH_HEAD
. コロンのない refspec を 2 つ指定した場合、次の2行FETCH_HEAD
が含まれます。
676699a0e0cdfd97521f3524c763222f1c30a094 branch 'master' of ...
222c4dd303570d096f0346c3cd1dff6ea2c84f83 branch 'branch' of ...
Git のバージョンが 1.8.4 以降の場合、更新はそこに行われます (この部分は変更されていません)。ただし、フェッチは、リモートの行で指定されているように、これらのブランチを適切なリモート ブランチに永続的に記録する機会を利用します。fetch =
ただし、何らかの理由で、実際に更新されたリモート ブランチgit fetch
の更新行のみを出力します。->
には常にすべての更新が記録されるためFETCH_HEAD
、ここには常にブランチ名が出力されます。
(git 1.8.4以降が必要になる以外に、リモートブランチを更新する際のその他の問題は、それらのfetch =
行が存在する必要があることです。存在しない場合、フェッチが名前refs/heads/*
をに変更することを認識するマッピングがありませんrefs/remotes/remote-X/*
。)
つまり、git 1.8.4 以降では、すべてのリモート ブランチが実際に「日和見的に更新」されます。古いバージョンの git は ongit push
で実行されるため、以前は一貫性がありませんでした。git 1.8.4 でさえ、それはまだ と矛盾しているgit pull
と思います (私はgit pull
気づくほど十分に使っていませんが :-) ); これは git 1.9 で修正されるはずです。
と の違いに戻りましょう。git fetch remote
git fetch remote refspec ...
を実行すると、つまりすべての refspec を省略した場合、フェッチは通常どおり行に戻ります。フェッチ操作は、行からすべての参照を取り込みます。 これらはすべてに入りますが、今回は「マージ不可」とマークされています (タブ付きで、Web ページに収まるように 1 つのスペースに変更しました)。git fetch remote
fetch =
fetch
FETCH_HEAD
676699a0e0cdfd97521f3524c763222f1c30a094 not-for-merge branch ...
ブランチではない参照 (例えば、refs/notes/
持ち込まれた参照) は、代わりに次のように読みます。
f07cf14302eab6ca614612591e55f7340708a61b not-for-merge 'refs/notes/commits' ...
一方、必要に応じてリモート ブランチ ref が更新され、どのブランチが更新されたかを示すメッセージが表示されます。
22b38d1..676699a master -> remote-X/master
繰り返しますが、すべてが にダンプされますがFETCH_HEAD
、「更新が必要」な参照のみが更新されて出力されます。上記のように、新しいブランチには「新しいブランチ」が印刷され、古いブランチには省略された新旧の SHA-1 が印刷されmaster -> remote-X/master
ます。
一方、 を実行すると、フェッチは指定された refspecのみをもたらします。これらはすべて通常どおり6に入りますが、今回はすべてが印刷されます。次に、git が 1.8.4 以降の場合、(適切な行を介して) マップでき、更新が必要なすべての参照更新も更新され、出力されます。git fetch remote refspec ...
FETCH_HEAD
fetch =
* branch master -> FETCH_HEAD
* branch branch -> FETCH_HEAD
22b38d1..676699a master -> remote-X/master
git のバージョンが 1.8.4 よりも古い場合remote-X/master
、このケースでは の更新は行われません。むしろ、コマンドライン refspec の 1 つがrefs/heads/master:refs/remotes/remote-X/master
、 またはrefs/heads/*:refs/remotes/remote-X/*
、またはプラス記号が付いたもののバリアントでない限り、更新は行われません。前に標識。
1これはたいしたエラー メッセージではありません。引数は「remote-X
リポジトリ」ではなく、「リモート」である必要がありました! git がここでもっと有益なことを言ったらいいかもしれません。
2 git リモート プロトコルには欠陥があります。HEAD は通常、リモートの現在のブランチであるため、間接参照です。したがって、たとえば、「ref: refs/heads/master」として来るはずですが、代わりに、完全に解決された SHA-1。少なくとも 1 つの git コマンド ( git clone
) は、この SHA-1 を各ブランチ ヘッドの SHA-1 と比較することにより、リモートの現在のブランチを「推測」しようとします。たとえば、上記では、同じ SHA-1HEAD
を持っているため、リモートが「ブランチ マスター上」にあることは明らかです。refs/heads/master
しかし、複数のブランチ名が同じコミットを指し、HEAD
そのコミット ID と一致する場合、どのブランチ (存在する場合)HEAD
がオンになっているかを知る方法はありません。リモートも「切り離されたHEAD」状態になる可能性があります。
編集、2019: このバグは Git バージョン 1.8.4.3 で修正されました。クローン元のマシンと自分のマシンの両方の Git バージョンが 1.8.4.3 以降である限り、Git はもはや推測する必要はありません。
3プラス記号は、「強制更新を受け入れる」ことを意味します。つまり、ブランチの「早送りのみ」4ルールまたはタグの「タグを変更しない」5によって拒否される更新を取得します。
4コミット有向非巡回グラフの古い SHA-1 が新しい SHA-1 の祖先である場合、古い SHA-1 から新しいラベルに変更するラベルの「早送り」が可能です。
5「タグを変更しない」ルールは git 1.8.2 で新しく追加されました。git がそれよりも古い場合、git はタグにもブランチ ルールを使用し、「強制更新」なしで早送りを許可します。
6しかしnot-for-merge
今回はなし。基本的に、コロンのない refspec を指定すると、git fetch
それらが「マージ用」であると想定され、検索できるFETCH_HEAD
ように配置されgit merge FETCH_HEAD
ます。(非分岐参照で何が起こるかはテストしていません。)