问题描述
我是,并想知道是否有任何区别
git fetch
和
git fetch origin master
我的<$ c $没有任何其他分支和原点c>远程仓库在GitHub上。
当我这样做时:
git fetch origin master
remote:计数对象:4,完成。
remote:压缩对象:100%(2/2),完成。
remote:合计3(delta 1),重用0(delta 0)
开箱对象:100%(3/3),完成。
从github.com:XXXXXXXXXXXXXXX
*分支大师 - > FETCH_HEAD
但只是:
git fetch
从github.com:XXXXXXXXXXXXXXX
531d466..aaf6df0 master - >起源/大师
请注意,master指向不同的东西;在一种情况下 FETCH_HEAD ,在另一种情况下, origin / master ?
他们有区别吗?
以下是TL; DR版本(特殊情况下会覆盖 : git fetch always 更新 FETCH_HEAD ,在不同的情况下有多行。它有时更新远程分支,它们是全名以 refs / remotes / 开头的ref。其余的大部分是关于有时,根据给 git fetch 和git版本的参数数量而定。
我有机会测试这个。我们来区分三种情况,所有这些情况都假设没有像 -a 甚至<$ c $这样的额外选项运行 git fetch C> - 所有。我们还要排除 git fetch 的变体,例如直接使用URL或者 insteadOf 条目或列出的文件在 .git / remotes 或 .git / branches 中。 (我承认我只是在猜测,但我认为这些都是在 [remotename] 条目进入git的配置文件之前的剩余时间。)
-
git fetch ,并且没有其他参数。
Git通过阅读 HEAD 来确定您当前的分支(通常情况下,您当然可以看到 git branch 或 git status )。然后它为该分支寻找一个配置条目,命名其 remote 。例如,假设你在分支 dummy 和 .git / config has(在其他条目中) p>
[branchdummy]
remote = remote-X
在这种情况下, git fetch 相当于 git fetch remote-X 。在这一点之后,这相当于案例2,即:
- code>(除此之外没有其他参数)。
这次Git没有看你当前的分支。使用的遥控器是命令行上给出的。它不为寻找给定远程的配置部分。假设您使用 remote-X :在这种情况下,它会查找:
[remoteremote-X]
url = ...
如果该部分不存在,或者没有 url = 条目,则会出现错误: fatal:'remote-X'不会出现成为一个git仓库。否则会提供URL,并且 git fetch 会尝试连接到那里。假设它可以连接...
通常还有至少一个配置条目,可能更多,阅读:
fetch = + refs / heads / *:refs / remotes / remote-X / *
(远程的名称在这里是硬编码的)。假设有...
接下来, git fetch 询问远程它有什么参考(分支和标记主要,尽管你可以获得所有的参考资料,但大多数人只关心分支和标签)。你可以用 git ls-remote remote-X 自己做同样的事情,这样做会泄漏出这样的东西:
<$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $'
$ b对 HEAD ref的处理并不完全一致(我已经看到它的表现很奇怪),但通常在这里它会被丢弃。其余分支根据 fetch = refspec重新命名和更新。 (如果有多个 fetch = refspecs,它们会根据它们全部重命名和更新。这对于重载 refs / notes例如,在 refs / rtags / 下创建自己的远程标签名称空间。)
在这种情况下,fetch将引入两个分支分支和 master ,并更新(本地)远程分支名称, refs / remotes / remote-X / branch 和 refs / remotes / remote-X / master ,如有需要。对于每一个更新的, fetch 打印这样一行:
22b38d1..676699a master - > remote-X / master
如果 fetch = 行缺失,你会得到完全不同的东西。输出结果为:
*分支HEAD - > FETCH_HEAD
在这种情况下,就好像(缺少) fetch = 行在那里并包含 fetch = HEAD 。
-
git fetch remote refspec ( refspec 部分是一个或多个refspecs,真正的,如下所述)。
这与案例2类似,只是这次,refspecs是在命令行中提供的,而不是从 fetch = 远程的配置条目。然而,这里的抓取行为是非常不同的。
让我们暂停在这个特殊的情况下,适当地描述一个refspec。 (Refspecs对于 git push 也是如此,但是和往常一样,使用git时,实现细节会泄漏出去,而且它们的工作方式略有不同。)refspec有一个可选的前导加号( + )符号,我将在这里忽略; 然后是两部分,用冒号分隔(:)。两者通常只是一个分支名称,但你可以(和 fetch = 行)拼出fullref-name, refs / heads在分支名称的情况下,分支 。
对于获取操作,左侧的名称是远程本身的名称(例如,如 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 ,即至少给出一个或多个refspecs,但所有都没有冒号。
-
如果您的git版本早于1.8.4,那么仅更新进入 FETCH_HEAD code>。如果您提供了两个无冒号的refspecs, FETCH_HEAD 现在包含两行:
222c4dd303570d096f0346c3cd1dff6ea2c84f83分支'分支'...
-
如果你的git版本是1.8.4或更新版本,那么更新就会在那里 - 这个部分没有改变 - 但是也是,fetch会借此机会将这些分支永久记录在适当的远程分支中,如远程的 fetch = 行所示。
不管出于什么原因, git fetch 只打印出一个更新 - > 实际更新的远程分支。因为总是记录 FETCH_HEAD 中的所有更新,所以总是在这里打印分支名称。
(另外一个问题,除了需要git 1.8.4或更新版本以外,更新远程分支是因为那些 fetch = 行必须如果它们不存在,则不存在映射,该提取知道将 refs / heads / * 重命名为 refs / remotes / remote-X )
换句话说,git 1.8.4和newer真的可以机会性地更新所有的远程分支机构。旧版本的git在 git push 上执行,所以它之前一直不一致。即使在git 1.8.4中,它仍然与 git pull 不一致,我认为(尽管我没有使用 git pull 足以注意到:-));这应该是固定在git 1.9中。
现在让我们回到 git fetch remote 和 git fetch remote refspec ... 。
-
如果运行 git fetch remote ,即省略所有的refspecs,抓取都像往常一样回落到 fetch = 行中。 fetch操作会引入 fetch 行中的所有引用。 所有都进入 FETCH_HEAD ,但这次它们被标记为not-for-merge(带有选项卡,我将其更改为一个更好地适应网页的空间):
676699a0e0cdfd97521f3524c763222f1c30a094非合并分支...
code>不是分支的参考,例如 refs / notes / 参考请阅读:
f07cf14302eab6ca614612591e55f7340708a61b非合并'refs / notes / commitits'...
同时,必要时可以更新远程分支引用,并告知您哪些更新已被更新:
22b38d1..676699a master - > remote-X / master
再次, 被转储到 FETCH_HEAD ,但只会更新并打印需要更新。新分支打印出新分支,旧分支打印新旧SHA-1, master - > remote-X / master 以上。
另一方面,如果您运行 git fetch remote refspec ... ,fetch只会带来指定的refspecs。像往常一样,这些 all 进入 FETCH_HEAD ,但是这次每打印一次。然后,如果你的git是1.8.4或更新的,那么可以映射的任何引用更新(通过明智的 fetch = 行)并需要更新 FETCH_HEAD -
git fetch, and no other arguments.
Git determines your current branch (in the usual way, by reading HEAD, but you can of course see what it is with git branch or git status). It then looks for a config entry for that branch naming its remote. For instance, suppose you're on branch dummy and .git/config has (among other entries):
[branch "dummy"] remote = remote-X
In this case git fetch is equivalent to git fetch remote-X. After that point, this is equivalent to case 2, which is:
git fetch remote (and no more arguments beyond that).
Git does not look at your current branch this time. The remote to use is the one given on the command line. It does look for a config section for the given remote. Let's say you're using remote-X: in this case, it looks for:
[remote "remote-X"] url = ...
If that section does not exist, or there is no url = entry, you get an error: fatal: 'remote-X' does not appear to be a git repository. Otherwise that gives the URL, and git fetch will attempt to connect to there. Assuming it can connect...
Normally there's also at least one config entry, possibly more, reading:
fetch = +refs/heads/*:refs/remotes/remote-X/*
(the name of the remote is hard-coded here). Assuming there is...
Next, git fetch asks the remote what refs it has (branches and tags mostly, although you can get all refs, but most people just care about branches and tags). You can do this same thing yourself with git ls-remote remote-X, which spills out stuff like this:
676699a0e0cdfd97521f3524c763222f1c30a094 HEAD 222c4dd303570d096f0346c3cd1dff6ea2c84f83 refs/heads/branch 676699a0e0cdfd97521f3524c763222f1c30a094 refs/heads/master
The treatment of the HEAD ref is not entirely consistent (I've seen it behave oddly) but usually here it just gets dropped. The remaining branches are renamed and updated according to the fetch = refspec. (If there are multiple fetch = refspecs, they're renamed and updated according to all of them. This is mainly useful for bringing over refs/notes/ or making your own "remote tags" name-space under refs/rtags/, for instance.)
In this case, fetch will bring over any objects needed for the two branches branch and master, and update the (local) "remote branch" names, refs/remotes/remote-X/branch and refs/remotes/remote-X/master, as needed. For each one that is updated, fetch prints a line like this:
22b38d1..676699a master -> remote-X/master
If the fetch = lines are missing, you get something quite different. The output will read:
* branch HEAD -> FETCH_HEAD
In this case, it's as if the (missing) fetch = line were there and contained fetch = HEAD.
git fetch remote refspec (the refspec part is one or more refspecs, really, as described below).
This is similar to case 2, only this time, the "refspecs" are supplied on the command line, instead of from the fetch = configuration entries for the remote. However, the fetch behavior is pretty different here.
If your git version older than 1.8.4, the update only goes into FETCH_HEAD. If you gave two colon-less refspecs, FETCH_HEAD now contains two lines:
676699a0e0cdfd97521f3524c763222f1c30a094 branch 'master' of ... 222c4dd303570d096f0346c3cd1dff6ea2c84f83 branch 'branch' of ...
If your git version is 1.8.4 or newer, the update goes there—this part is unchanged—but also, the fetch takes the opportunity to record these branches permanently in their proper remote branches, as given by the fetch = lines for the remote.
For whatever reason, though, git fetch only prints out an update -> line for the remote branches that are actually updated. Since it always records all the updates in FETCH_HEAD, it always prints the branch names here.
(The other issue, besides needing git 1.8.4 or newer, with getting the remote branches updated is that those fetch = lines must exist. If they don't, there's no mapping by which the fetch knows to rename refs/heads/* to refs/remotes/remote-X/*.)
If you run git fetch remote, i.e., omit all the refspecs, the fetch falls back to the fetch = lines as usual. The fetch operation brings over all the refs from the fetch lines. All of these go into FETCH_HEAD, but this time they're marked "not-for-merge" (with tabs, which I changed to one space to fit on the web pages better):
676699a0e0cdfd97521f3524c763222f1c30a094 not-for-merge branch ...
Refs that are not branches, e.g., refs/notes/ refs that are brought over, read instead:
f07cf14302eab6ca614612591e55f7340708a61b not-for-merge 'refs/notes/commits' ...
Meanwhile, remote branch refs are updated if necessary, with messages telling you which ones were updated:
22b38d1..676699a master -> remote-X/master
Again, everything gets dumped into FETCH_HEAD, but only refs that "need updates" are updated and printed. New branches get the "new branch" printed and old ones have their abbreviated old-and-new SHA-1 printed, as for master -> remote-X/master above.
If, on the other hand, you run git fetch remote refspec ..., the fetch brings over only the specified refspecs. These all go into FETCH_HEAD as usual, but this time every one of them is printed. Then, if your git is 1.8.4 or newer, any reference-updates that can be mapped (via sensible fetch = lines) and need updating are also updated and printed:
* branch master -> FETCH_HEAD * branch branch -> FETCH_HEAD 22b38d1..676699a master -> remote-X/master
If your version of git is older than 1.8.4, the update of remote-X/master does not occur for this case—or rather, it does not occur unless one of your command-line refspecs was refs/heads/master:refs/remotes/remote-X/master, or refs/heads/*:refs/remotes/remote-X/*, or the variants of those with the plus-signs in front.
*分支分支 - > FETCH_HEAD
22b38d1..676699a master - > remote-X / master
如果您的git版本早于1.8.4, code> remote-X / master 对于这种情况不会发生 - 或者说,除非您的命令行参考指令之一是 refs / heads / master:refs / remotes / remote-X / master 或 refs / heads / *:refs / remotes / remote-X / * 或在前面带有加号的那些变体。
$ hr
这不是一个很好的错误信息。 remote-X 参数从来不应该是一个存储库,它应该是一个远程!这可能是很好的,如果git在这里说更多的信息。
git远程协议有一个缺陷:HEAD通常是一个间接参考因为它是遥控器上的当前分支,所以它应该以ref:refs / heads / master的形式出现,但相反它会以完全解析的SHA-1的形式出现。将至少一个git命令( git clone )试图通过比较这个SHA-1与每个分支头的分支来猜测远程当前分支。例如,在上面的例子中,很明显,远程是在分支主机上,如 HEAD 和 refs / heads / master 具有相同的SHA-1。但是,如果多个分支名称指向同一个提交,并且 HEAD 与该提交ID相匹配,则无法确定哪个分支(如果有) HEAD 已打开。无论SHA-1值如何,远程设备也可以处于分离HEAD状态,在这种情况下,它不在任何分支上。
加号表示接受强制更新,即接受将被分支的只有快进规则拒绝的更新,或永不改变标签标签。
标签的快进如果提交Directed Acyclic Graph中的旧SHA-1是新SHA-1的祖先,则可以将SHA-1更改为新的SHA-1。
在git 1.8.2中,never change tags规则是新的。如果你的git比这个更旧,git也会使用标签的分支规则,这样就可以在不进行强制更新的情况下进行快速转发。
但是这次没有 not-for-merge 。基本上,当你提供无冒号的refspecs时, git fetch 假定它们是for merge,并把它们放到 FETCH_HEAD 以便 git merge FETCH_HEAD 可以找到它们。 (我还没有测试过非分支文件会发生什么。)
I was doing a fetch/merge and wanted to know if there is any difference between doing
git fetch
and
git fetch origin master
I do not have any other branches and origin points to my remote repository on GitHub.
When I do:
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
But just:
git fetch From github.com:XXXXXXXXXXXXXXX 531d466..aaf6df0 master -> origin/master
Note that master points to different things; in one case FETCH_HEAD and in other case, origin/master?Are they different?
Here's the "TL;DR" version (which glosses over a lot of special cases): git fetch always updates FETCH_HEAD, with more than one line in various cases. It sometimes updates "remote branches", which are refs whose full name starts with refs/remotes/. The rest is mostly about the "sometimes", which vary based on both the number of arguments given to git fetch, and the git version.
I had a chance to test this. Let's distinguish three cases, all of which assumes running git fetch without extra options like -a or even --all. Let's also exclude the weirder variants of git fetch, like using a URL directly, or the insteadOf entries, or files listed in .git/remotes or .git/branches. (I admit I'm just guessing, but I think these are left-overs from the days before the [remote "name"] entries went into git's config files.)
Let's pause a moment and describe a refspec properly, in this particular case. (Refspecs also occur for git push but, as usual with git, implementation details leak out and they work slightly differently there.) A refspec has an optional leading plus (+) sign, which I'll ignore here; then two parts, separated by a colon (:). Both are often just a branch name, but you can (and fetch = lines do) spell out the "full" ref-name, refs/heads/branch in the case of a branch name.
For fetch operations, the name on the left is the name on the remote itself (as shown by git ls-remote for instance). The name on the right is the name to be stored/updated in your local git repository. As a special case, you can have an asterisk (*) after a slash as the last component, like refs/heads/*, in which case the part matched on the left is replaced on the right. Hence refs/heads/*:refs/remotes/remote-X/* is what causes refs/heads/master (as seen on the remote, with git ls-remote) to become refs/remotes/remote-X/master (as seen in your local repository, and in shorter form, on the right side of the -> line git fetch prints).
If you don't put in the :, though, git fetch has no good place to put a copy of "the branch over there". Let's say it's going to bring over the remote's refs/heads/master (the master branch on the remote). Instead of updating your refs/heads/master—obviously that would be bad if you have your own commits in branch master—it just dumps the update into FETCH_HEAD.
Here's where things get particularly squirrely. Let's say you run git fetch remote-X master branch, i.e., give at least one, and maybe several, refspecs, but all with no colons.
In other words, git 1.8.4 and newer really does "opportunistically update" all the remote branches. Older versions of git do it on git push, so it has been inconsistent before. Even in git 1.8.4 it's still inconsistent with git pull, I think (although I don't use git pull enough to notice :-) ); that's supposed to be fixed in git 1.9.
Now let's get back to the difference between git fetch remote and git fetch remote refspec ....
This is not a great error message. The remote-X argument was never supposed to be a "repository", it was supposed to be a "remote"! It might be nice if git said something more informative here.
There's a flaw in the git remote protocol: HEAD is usually an indirect ref as it's the current branch on the remote, so it should come over as "ref: refs/heads/master" for instance, but instead it comes over as the fully resolved SHA-1. At least one git command (git clone) attempts to "guess" the current branch on the remote by comparing this SHA-1 to that of each branch-head. In the above, for instance, it's clear that the remote is "on branch master", as HEAD and refs/heads/master have the same SHA-1. But if multiple branch names point to the same commit, and HEAD matches that commit-ID, there's no way to tell which branch (if any) HEAD is on. The remote could be in "detached HEAD" state too, in which case it's not on any branch, regardless of SHA-1 values.
The plus sign means "accept forced updates", i.e., take updates that would be rejected by the "nothing but fast forward" rule for branches, or "never change tags" for tags.
A "fast forward" for a label, changing it from an old SHA-1 to a new one, is possible when the old SHA-1 in the commit Directed Acyclic Graph is an ancestor of the new SHA-1.
The "never change tags" rule was new in git 1.8.2. If your git is older than that, git uses the branch rules for tags too, allowing fast-forwarding without "forced update".
But without the not-for-merge this time. Basically, when you supply colon-less refspecs, git fetch assumes they're "for merge" and puts them into FETCH_HEAD so that git merge FETCH_HEAD can find them. (I have not tested what happens with non-branch refs.)
这篇关于git fetch和git fetch origin master之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!