执行'git fetch-upstream-master:master'与'git-pull-upstream-master:master'的确切区别是什么`

执行'git fetch-upstream-master:master'与'git-pull-upstream-master:master'的确切区别是什么`,git,git-pull,git-fetch,Git,Git Pull,Git Fetch,我知道git-fetch和git-pull之间的区别git pull基本上是一个git fetch+git merge命令 然而,我正在研究如何在不签出主分支的情况下使用上游更新我的fork(主分支)。我遇到了这样的答案- 但当我在master上签出后使用git-fetch-upstream-master:master时,我遇到了这个错误- fatal: Refusing to fetch into current branch refs/heads/master of non-bare rep

我知道
git-fetch
git-pull
之间的区别
git pull
基本上是一个
git fetch
+
git merge
命令

然而,我正在研究如何在不签出主分支的情况下使用上游更新我的fork(主分支)。我遇到了这样的答案-

但当我在master上签出后使用
git-fetch-upstream-master:master
时,我遇到了这个错误-

fatal: Refusing to fetch into current branch refs/heads/master of non-bare repository
所以,我尝试了
git-pull-upstream-master:master
,结果成功了。有趣的是,无论我是否在master上,使用
git-pull-upstream-master:master
都会用upstream更新我的fork。而
git-fetch-upstream-master:master
仅在我不在master分支上时工作

这将是非常有趣的阅读解释,从知识渊博的人在这里

git pull
基本上是一个
git fetch
+
git merge
命令

是的,但是,正如你所怀疑的,事情远不止这些

,在您链接到的答案中,实际上有一个关键项。他提到你可以:

使用
fetchoriginbranchb:branchB
,如果合并不是快进的,它将安全地失败

另一个没有很好的文档记录:它是
git fetch
中的
-u
aka
-updateheadok
选项,它设置了
git pull
。确实定义了它的功能,但有点神秘和可怕:

默认情况下,git fetch拒绝更新对应的头 到当前分支。此标志将禁用检查。这纯粹是 对于git pull与git fetch通信的内部使用, 除非你正在实现你自己的瓷器,否则你不会 我应该用它

这让我们了解您的观察:

所以,我尝试了git
拉上游master:master
,它成功了。有趣的是,无论我是否在master上,使用
git-pull-upstream-master:master
都会用upstream更新我的fork。然而,
git-fetch-upstream-master:master
仅在我不在master分支上时工作

这是由于
-u
标志。若您运行了
git-fetch-upstream-master:master
,从某种意义上讲,这是可行的,但会给您带来一个不同的问题。警告的存在是有原因的。让我们看看原因是什么,看看警告是否过于严厉。警告:这里有很多!下面的许多复杂性是为了弥补历史错误,同时保留向后兼容性

分支名称、引用和快进 首先,让我们谈谈引用和快进操作

在Git中,引用只是谈论分支名称(如
master
)或标记名称(如
v1.2
),或远程跟踪名称(如
origin/master
),或任何数量的其他名称的一种奇特方式,所有这些名称都以一种常见且合理的方式进行:我们将每种特定的名称分组为一组。分支名称位于
refs/heads/
下,标记名称位于
refs/tags/
下,等等,因此
master
实际上就是
refs/heads/master

这些名称中的每一个都以
refs/
开头,都是一个参考。还有一些额外的引用也不是以
refs
开头的,尽管Git在决定
HEAD
ORIG\u HEAD
MERGE\u HEAD
等名称是否真的是引用时内部有点不稳定,引用主要用于为Git对象哈希ID提供有用的名称。分支名称特别有一个有趣的特性:它们从一个提交移动到另一个提交,通常以Git称为快进的方式

也就是说,给定一个包含一些提交的分支(此处用大写字母表示),以及一个包含更多提交(包括第一个分支上的所有提交)的第二个分支:

...--E--F--G   <-- branch1
            \
             H--I   <-- branch2
如果我们将名称
branch1
移动到指向commit
I
而不是commit
J
,commit
J
本身会发生什么情况?3这种移动会留下commit,是对分支名称的非快进操作

这些名称可以通过省略
refs/
部分来缩短,或者通常,甚至可以省略
refs/heads/
部分或
refs/tags/
部分或任何内容。Git将使用中描述的六步规则在其引用名称数据库4中查找第一个匹配的数据库。例如,如果您有一个
refs/tags/master
和一个
refs/heads/master
,并且说
master
,Git将首先匹配
refs/tags/master
,然后使用标记。5


1如果引用是具有或可以具有reflog的名称,则
HEAD
是引用,而
ORIG\u HEAD
和其他
*\u HEAD
名称不是引用。不过,这里的边缘有点模糊

2可以通过更多名称访问这些提交。重要的是,在快进之前无法通过
branch1
访问它们,而在快进之后才能访问它们

3直接的答案实际上是什么都没有发生,但最终,如果无法通过某个名称访问commit
I
,Git将对commit进行垃圾收集

4这个“数据库”实际上只是目录
.git/refs
和文件
.git/packed refs
的组合,至少目前是这样。如果Git同时找到一个文件条目和一个路径名,则路径名的散列将覆盖
压缩引用文件中的散列

5异常:
git checkout
将参数作为br进行尝试
...--E--F--G------J   <-- branch1
            \
             H--I   <-- branch2
$ git rev-parse master
468165c1d8a442994a825f3684528361727cd8c0
$ git checkout master
Switched to branch 'master'
$ cat .git/HEAD
ref: refs/heads/master
$ git checkout --detach master
HEAD is now at 468165c1d... Git 2.17
$ cat .git/HEAD
468165c1d8a442994a825f3684528361727cd8c0
$ git status
... nothing to commit, working tree clean
$ git rev-parse master^
1614dd0fbc8a14f488016b7855de9f0566706244
$ echo 1614dd0fbc8a14f488016b7855de9f0566706244 > .git/refs/heads/master
$ git status
...
Changes to be committed:
...
        modified:   GIT-VERSION-GEN
$ echo 468165c1d8a442994a825f3684528361727cd8c0 > .git/refs/heads/master
$ git status
...
nothing to commit, working tree clean
git fetch --update-head-ok upstream master:master
git merge -m <message> <hash ID extracted from .git/FETCH_HEAD>