これが私の見解です。
refspecs
まず、「refspec」と呼ばれるものが何であるかをデバンキングしましょう。これは将来必要になるためです。
refspecは「参照仕様」の略です。参照とは、コミットまたは別の参照を指すものです。ブランチは参照であり、タグは参照です。HEAD
特別な(いわゆる「シンボリック」)リファレンスです。現時点では、さまざまな種類の参照の違いを無視しましょう。
参照を単に「refs」と略して呼ぶのが一般的です。
refspecの形式は次のとおりです。
[+]source[:destination]
source
一連のコミットを取得するための参照を指定します。
destination
、指定されている場合、から取得された一連のコミットで更新source
する参照を指定します。
- プラス記号が含まれている場合、が指すコミットの行が。が指すコミットの行に完全に含まれていない場合でも、強制的に更新が行われます。
destination
source
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 add
git 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は、ローカルにリモートブランチがあるブランチから取得したコミットでリモートトラッキングブランチを更新したことに気付いたので、このリモートブランチを更新しますこれが最後に見られた状態だからです。
要約しましょう:
- 「origin/foo」は、リモートリポジトリ「origin」内の「foo」という名前のブランチの状態をキャプチャするリモートブランチです。
- 「foo」は「origin/foo」を追跡するローカルブランチです。
あなたがやる
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」ファイルを読んで、このすべての「リモートのもの」がどのように構成されているかを理解することをお勧めします。