1

私はフェッチ/マージを行っていましたが、実行に違いがあるかどうかを知りたいと思っていました

git fetch

git fetch origin master

私はremote repositoryGitHub に他のブランチやオリジン ポイントを持っていません。

私がする時:

git fetch origin master
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:XXXXXXXXXXXXXXX
 * branch            master     -> FETCH_HEAD

しかし、ただ:

git fetch
From github.com:XXXXXXXXXXXXXXX
   531d466..aaf6df0  master     -> origin/master

master は異なるものを指すことに注意してください。場合FETCH_HEADによってはorigin/master?彼らは違うのですか?

4

1 に答える 1

8

これが "TL;DR" バージョンです (多くの特殊なケースを詳しく説明しています):git fetch 常にFETCH_HEADupdateで、さまざまなケースで複数の行があります。フルネームが で始まる参照である「リモートブランチ」を更新することがありrefs/remotes/ます。git fetch残りは主に「時々」に関するもので、 に与えられた引数の数と git バージョンの両方に基づいて異なります。


これをテストする機会がありました。3 つのケースを区別してみましょう。これらはすべて、または などgit fetchの追加オプションなしで実行することを前提としています。また、URL を直接使用したり、エントリや または にリストされたファイルを使用したりするなど、の変種も除外しましょう。(推測にすぎないことは認めますが、これらはエントリが git の構成ファイルに入る前の日の残り物だと思います。編集、2019: それは正しいことが判明しました。)-a--allgit fetchinsteadOf.git/remotes.git/branches[remote "name"]

  1. 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 と同じです。つまり、次のようになります。

  2. git fetch remote(そしてそれ以上の議論はありません)。

    今回は、Git は現在のブランチを参照しません。使用するリモートは、コマンド ラインで指定されたものです。指定されたリモートの構成セクションを探しますを使用しているとしましょうremote-X: この場合、以下を探します:

    [remote "remote-X"]
        url = ...
    

    そのセクションが存在しない場合、またはurl =エントリがない場合は、次のエラーが発生しますfatal: 'remote-X' does not appear to be a git repository1 それ以外の場合、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/branchrefs/remotes/remote-X/masterます。更新されたものごとにfetch、次のような行を出力します。

       22b38d1..676699a  master     -> remote-X/master
    

    fetch =行が欠落している場合、まったく異なる結果が得られます。出力は次のようになります。

     * branch            HEAD       -> FETCH_HEAD
    

    この場合、(欠落している)fetch =行がそこにあり、含まれているかのようですfetch = HEAD

  3. 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/mastergit ls-remoterefs/remotes/remote-X/master->git fetch

:ただし、を入れgit fetchないと、「あそこの枝」のコピーを置くのに適した場所がありません。refs/heads/masterリモートのもの(リモートのmasterブランチ)を引き継ぐとしましょう。ブランチに独自のコミットがある場合は明らかに悪いことですが、更新する 代わりに、更新を にダンプするだけです。refs/heads/mastermasterFETCH_HEAD

ここで、物事が特に厄介になります。を実行するとしますgit fetch remote-X master branch。つまり、少なくとも 1 つ、場合によっては複数の refspec を指定しますが、すべてコロンは使用しません。

  • git バージョンが1.8.4 よりも古い場合、更新FETCH_HEAD. コロンのない refspec を 2 つ指定した場合、次の2FETCH_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 remotegit fetch remote refspec ...


  • を実行すると、つまりすべての refspec を省略した場合、フェッチは通常どおり行に戻ります。フェッチ操作は、行からすべての参照を取り込みます。 これらはすべてに入りますが、今回は「マージ不可」とマークされています (タブ付きで、Web ページに収まるように 1 つのスペースに変更しました)。git fetch remotefetch =fetchFETCH_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_HEADfetch =

     * 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ます。(非分岐参照で何が起こるかはテストしていません。)

于 2014-02-04T05:25:26.697 に答える