0

この質問は、通常のSOの質問よりも「理論的」であるため、一定の正当性が適切であると感じています。私が抱えている問題の1つは、git「うまくいくはず」と思われることを常に行っていることですが、そうではありません。gitこれは、過去10か月ほど毎日ほとんど使用し、仕事で使用する他のどのソフトウェアツールよりも集中的に研究した後でも当てはまります。これは、深く、git単に私には意味がないことを意味します。したがって、この質問の目的は、意味を理解すること gitだけであり、うまくいけば、私が「機能するはず」と思うものと実際に機能するものとの間の不一致を減らすことです。(ところで、「ドキュメントを読む」は解決策ではありません。

この質問は、私が尋ねた以前の質問のフォローアップです:git pull <REMOTE> <BRANCH>をフェッチ+マージに分解する方法は?。特に、その質問で与えられたサンプルスクリプトを実行し、その出力を調べることをお勧めします。

git pullコマンドは、aと。の組み合わせであると想定されていgit fetchますgit merge

したがって、<BRANCHNAME>が現在のブランチである場合、実行しているという事実に戸惑います。

git pull --no-edit <REMOTENAME> <BRANCHNAME>

トラッキングブランチ<REMOTENAME>/<BRANCHNAME>1を更新しますが、

git fetch <REMOTENAME> <BRANCHNAME>

変更されないまま<REMOTENAME>/<BRANCHNAME>であるため、それをマージしようとすること<BRANCHNAME>は通常、何もしません。

この明らかな矛盾を理解する方法はありますか?あるいは、なぜgit fetch <REMOTENAME> <BRANCHNAME>完全に失敗しないのか、少なくとも警告を出さないのですか?保存する価値のある一般的なユースケースは何ですか?

要するに、この明らかな矛盾はインターフェースの設計のバグなのか、それとも機能なのか、そして後者の場合、それはどのように機能なのか?git

編集:明確にするために:この質問は、上記の動作がそのドキュメントとどの程度一致しているかという問題にとらわれず、関係ありません。この質問が確認しようとしているのは、それらの引数を解釈するときgit pullの規則の違いが(設計)バグなのか(設計)機能なのかということです。git fetch

1<BRANCHNAME>さらに、とこの更新されたものの間で必要なマージを実行するか<REMOTENAME>/<BRANCHNAME>、少なくともこのマージを開始します。

4

2 に答える 2

1

これが私の見解です。

refspecs

まず、「refspec」と呼ばれるものが何であるかをデバンキングしましょう。これは将来必要になるためです。

refspecは「参照仕様」の略です。参照とは、コミットまたは別の参照を指すものです。ブランチは参照であり、タグは参照です。HEAD特別な(いわゆる「シンボリック」)リファレンスです。現時点では、さまざまな種類の参照の違いを無視しましょう。

参照を単に「refs」と略して呼ぶのが一般的です。

refspecの形式は次のとおりです。

[+]source[:destination]
  • source一連のコミットを取得するための参照を指定します。
  • destination、指定されている場合、から取得された一連のコミットで更新sourceする参照を指定します。
  • プラス記号が含まれている場合、が指すコミットの行が。が指すコミットの行に完全に含まれていない場合でも、強制的に更新が行われます。destinationsource

refspecsはフェッチとプッシュの両方に使用され、これらの場合、ソースと宛先のrefの意味が逆になります。明らかに、フェッチするとソースrefがリモートリポジトリにあり、プッシュするとソースrefがローカルレポ。

プレーンGit(つまりリファレンス実装)では、refは、名前がリポジトリのルートに相対的な名前のファイルにすぎません(通常のリポジトリの場合、これは「.git」ディレクトリであり、ベアリポジトリの場合、これはリポジトリのルートディレクトリです)。

HEAD(またはORIG_HEADまたはMERGE_HEADまたは)などの一部の参照FETCH_HEADはリポジトリのルートにあり、ブランチやタグ、リモートブランチなどの一部の参照は「refs」という名前のディレクトリにあり、それぞれのサブディレクトリの下でさらに並べ替えられます。

  • ブランチは「refs/heads」サブディレクトリにあります。
  • タグは「refs/tags」にあります。
  • リモートブランチは、適切なリモートの「refs / remotes/<remote>」ディレクトリにあります。
  • メモは「refs/notes」にあります。
  • ..。

したがって、「master」という名前のブランチを表すrefのフルネームは実際には「refs / heads / master」であり、名前付きのリモートリポジトリ「origin」からフェッチされた「foo」という名前のリモートブランチは「refs / remotes / origin/foo」です。 "。

Gitはスマートルックアップメカニズムを使用して、ほとんどの場合ref名を省略できるようにしますが、厳密にしたいときだけのあいまいさがある場合は、完全な(または「より完全な」)名前が使用されることがあります。残酷な詳細はマニュアルにありgit rev-parseます。

(完全な参照名に関するこれらの説明は、以下で説明する単純な呼び出しがどのように機能するかを理解するために必要ですgit fetch。)

フェッチ

フェッチに戻ります。

git fetch基本的に、指定した引数に応じて、いくつかの動作モードがあります。

  • git fetch <git_url>を介してアクセスされるリモートリポジトリ内のrefがHEAD指すものをすべて<git_url>取得し、その履歴をフェッチして、そのチップコミットのSHA-1名を.git/FETCH_HEADファイルに書き込みます。

    ベアリポジトリHEADでは、通常、「master」という名前のブランチを指します(ただし、これは変更される可能性があります)。非ベア(通常)リポジトリHEADでは、明らかに現在ワークツリーにチェックアウトされているものを指します。

  • git fetch <git_url> <refspec> ...指定されたrefspecsを使用して(の代わりにHEAD)リモートリポジトリからフェッチします。—「宛先」部分を含むrefspecsの場合、フェッチされた履歴でそれらのローカルrefを更新しようとします。フェッチされた各refspecのSHA-1名がファイルに書き込まれ.git/FETCH_HEADます。

    実証するために:

    git fetch git://server/repo.git master devel test
    From git://server/repo.git
     * branch            master     -> FETCH_HEAD
     * branch            devel      -> FETCH_HEAD
     * branch            test       -> FETCH_HEAD
    

    コミットがフェッチされている間、ローカル参照は更新されなかったことに注意してください。

    それでは、もっと複雑な方法でそれを実行しましょう。

    git fetch git://server/repo.git master devel:foo test:bar
    From git://server/repo.git
     * branch            master     -> FETCH_HEAD
     * [new branch]      devel      -> foo
     * [new branch]      test       -> bar
    

    ご覧のとおりgit fetch、「master」がフェッチされたばかりで、ローカルリポジトリに何も作成されていないときに、「foo」と「bar」の2つのローカルブランチを作成しました。3つすべてのリモート参照のSHA-1名は、.git/FETCH_HEADファイルに残ります。

  • git fetch <remote> <refspec> ...—を使用して構成された、またはによって自動的に作成され<remote>た名前付きリモートリポジトリである—明示的なフォームとまったく同じように動作しますが、その名前付きリモートの構成から使用するURLを取得します。git remote addgit clone<git_url>git fetch

  • git fetch <remote>そのリモートからすべてのブランチをフェッチし、そのリモートのいわゆるリモートブランチを更新(または存在しない場合は作成)しようとします。

    これは、この一連の操作が魔法ではないことを理解するために重要です。名前付きリモートを追加する(またはgit clone作成する)と、Gitはそのリモートを説明するいくつかの変数をリポジトリ構成に追加します。そのうちの1つは、refspecあり、 refspecが直接渡されない場合に使用されるremote.<name>.fetchパラメーターになります( !)。git fetch

    ここで、「origin」という名前のリモートの場合、Gitはにremote.origin.fetch設定されたパラメーターを作成します+refs/heads/*:refs/remotes/origin/*。あなたはあなたの地元のリポジトリに行き、あなた自身のためにこれを見ることができます:

    $ git config --get remote.origin.fetch
    +refs/heads/*:refs/remotes/origin/*
    
  • 「最も魔法」は明白ですgit fetch—それはちょうどリモコンが自動的に拾われる上記の呼び出しのように機能します。

プル(フェッチとマージ)

それをプルすると、「フェッチしてから、フェッチされたものを現在チェックアウトされているブランチにマージする」と定義されます。に渡されたrefspec(git pullまたはその欠如)はに直接渡されるためgit fetch、上記のすべてのルールが適用されます。

git pull <remote> <branch>`git fetchが更新しないのに、なぜ一致するリモートブランチを更新するのですか?

この「謎」を理解するには、Gitがリモートブランチとそのリモートトラッキングブランチに関して、舞台裏で特定の便利な魔法を使用していることを覚えておくと役立ちます(yoがgit fetch origin続く場合git branch foo origin/foo、「origin/foo」はリモートですブランチと「foo」は、対応するリモートブランチを追跡するため、リモート追跡ブランチです)。覚えておくべきもう1つのポイントは、最後のフェッチ時にリモートリポジトリ内の対応するブランチの状態をキャプチャするためにリモートブランチが存在することです。

ここで、リモート追跡ブランチをリモートリポジトリ内の同じ名前のブランチにプッシュするとします(最も一般的なケース)。あなたがこれをしたとしましょう:

$ git fetch origin
(this created the "origin/foo" branch)
$ git chekcout -b foo origin/foo
(this created a new local branch "foo" tracking "origin/foo")
$ git commit ...
$ git push origin foo

プッシュしている間、Gitは、一致するリモートブランチに同時にフェッチする場合、プッシュされたばかりの同じコミットを取得することに気付きます。したがって、それは続行され、「origin/foo」が「foo」が指すのと同じコミットを指すようになります。

さて、戻ってgit pull...あなたの場合、git pull完了後、Gitは、ローカルにリモートブランチがあるブランチから取得したコミットでリモートトラッキングブランチを更新したことに気付いたので、このリモートブランチを更新しますこれが最後に見られた状態だからです。

要約しましょう:

  1. 「origin/foo」は、リモートリポジトリ「origin」内の「foo」という名前のブランチの状態をキャプチャするリモートブランチです。
  2. 「foo」は「origin/foo」を追跡するローカルブランチです。
  3. あなたがやる

    git checkout foo
    git pull origin foo
    

    その結果、Gitは「foo」ブランチを更新しますが、これは「foo」に少なくとも1つのコミット「origin / foo」が含まれることをgit fetch origin意味git push origin fooします。したがって、Gitは続行して「origin/foo」も更新します。

つまり、私の意見では、あなたの場合のリモートブランチの更新は、git fetch ...実行されたためではなく、マージの結果としてローカルリモートトラッキングブランチが更新されたために発生しました。今は意味がありますか?

参考文献

リポジトリ内の「.git/config」ファイルを読んで、このすべての「リモートのもの」がどのように構成されているかを理解することをお勧めします。

于 2013-03-19T19:38:51.753 に答える
0

git-fetch man ページは言う

フェッチされた ref の ref 名とそのオブジェクト名は、.git/FETCH_HEAD に保存されます。この情報は、後で git merge によって行われるマージ操作のために残されます。

実行git fetch origin masterすると、フェッチの結果がFETCH_HEADref に格納されます。で現在のブランチにマージできますgit merge FETCH_HEAD

マニュアルページには次のようにも書かれています

パラメーターの形式<refspec>は、オプションのプラス + の後にソース ref<src>が続き、その後にコロン:が続き、その後に宛先 ref が続きます<dst>

一致するリモート ref<src>がフェッチされ、<dst>が空の文字列でない場合は、一致するローカル ref が を使用して早送りされ<src>ます。

git fetch に ref を更新するように指示できますgit fetch origin master:refs/remotes/origin/master

于 2013-03-19T16:16:18.093 に答える