错误信息来自
builtin/fetch.c#check_not_current_branch()
函数。
该函数可以追溯到
2008年10月的提交8ee5d73,git 1.6.0.4。
(也可以参考“
Git拒绝将内容提取到当前分支”)。
注释很有启示性:
Some confusing tutorials suggested that it would be a good idea to fetch
into the current branch with something like this:
git fetch origin master:master
(or even worse: the same command line with "pull" instead of "fetch").
While it might make sense to store what you want to pull, it typically is
plain wrong when the current branch is "master
".
This should only be allowed when (an incorrect) "git pull origin master:master
" tries to work around by giving --update-head-ok
to underlying "git fetch
", and otherwise we should refuse it, but somewhere along the lines we lost that behavior.
The check for the current branch is now only performed in non-bare
repositories, which is an improvement from the original behaviour.
考虑到函数check_not_current_branch()
是在以下情况下被调用的:
if (!update_head_ok)
check_not_current_branch(ref_map);
那意味着
git fetch -u origin develop:develop
应该能够正常工作。
-u
--update-head-ok
默认情况下,git fetch 拒绝更新与当前分支对应的头部。此标志禁用了该检查。这仅用于 git pull 与 git fetch 之间的内部通信,除非您正在实现自己的 Porcelain,否则不应使用它。尽管您不应使用该选项,但它确实满足了您最初的要求,使“git fetch origin branch:branch”在当前分支上工作。
关于这个补丁的起源,
请参阅讨论。
尽管将想要拉取的内容存储起来可能是有道理的。
那就是
fetch
的部分:它会存储来自更新的
origin/master
的远程历史记录。
但是当当前本地分支也是
master
时,情况就特别糟糕了。
正如
这个答案 中所提到的:
我认为 "git fetch url side:master
" 当 master
是当前分支,并且我们省略了 --update-head-ok
时是有问题的。
测试在当前的 master
上失败了。
它也无法更新工作目录,并且会将索引留下来,就好像你要删除所有东西一样。
以 "使用 refspec 的 git pull
" 为例。
torek 展示了一个例子,其中:
suppose that I run git fetch and it brings in two new commits that I will label C
and D
.
C
's parent is A
, and D
's is the node just before B
:
C
/
...--o--o--A <-- master
\
o--B <-- develop
\
D
The output from this git fetch will list this as:
aaaaaaa..ccccccc master -> origin/master
+ bbbbbbb...ddddddd develop -> origin/develop (forced update)
如果你当前的分支不是
develop
,那么强制更新可能正是你想要的。
但是,如果你在输入
git fetch origin develop:develop
时处于
develop
分支上,并且允许更新HEAD,则你当前的索引将反映
D
而不再是
B
。
因此,在你的工作树中进行
git diff
会显示你的文件与
D
之间的差异,而不是之前的HEAD
B
。
这很糟糕,因为你最初的
git checkout develop
创建了一个与
B
HEAD文件相同的工作树。
即使你的
git status
干净(没有任何修改),如果
git fetch origin develop:develop
更新了HEAD(从B强制更新到D),
git status
现在也会报告之前不存在的差异。
这就是为什么默认情况下,
git fetch
拒绝更新对应于当前分支的HEAD。
注意:Git 2.29中的一个错误也会触发类似的错误消息。
当 "
git commit-graph
"
(man) 在合并层时检测到同一个提交记录多次记录时,它过去会崩溃。现在的代码忽略除其中一个以外的所有内容并继续运行,在Git 2.30(2021年第一季度)中修复。
查看 提交 85102ac,提交 150f115(2020年10月9日)由Derrick Stolee (derrickstolee
)进行。
(由Junio C Hamano -- gitster
--于2020年11月2日合并到提交 307a53d中)
报告者:Thomas Braun
协助者:Taylor Blau
共同作者:Jeff King
签名者:Derrick Stolee
Thomas报告说一个 "git fetch
"(手册) 命令失败了,出现了一个错误,提示“意外的重复提交ID”。
$ git fetch origin +refs/head/abcd:refs/remotes/origin/abcd
fatal: 意外的重复提交ID
31a13139875bc5f49ddcbd42b4b4d3dc18c16576
根本原因是他们启用了 fetch.writeCommitGraph
,它生成了 commit-graph
链,并且此实例合并了两个包含相同提交ID的层次结构。
最初的假设是,如果 Git 已经将提交ID写入较低的 commit-graph
层,则不会将其写入 commit-graph
层。
不知何故,这种特殊情况确实发生了,导致了这个错误。
虽然意外,但实际上这并不无效(只要两个层次结构在提交的元数据上达成一致)。当我们解析一个没有 graph_pos
的提交时,在 commit-graph
层次结构中使用二进制搜索来查找提交并设置 graph_pos
。
在这种情况下,该位置将不再使用。但是,当我们从 commit-graph
文件中解析提交时,我们从 commit-graph
中加载其父项,并在那时分配 graph_pos
。
如果这些父项已经从 commit-graph
中解析过了,则不需要做任何操作。否则,该 graph_pos
是 commit-graph
中的一个有效位置,因此我们可以在必要时解析父项。
因此,这个 die()
过于激进。最简单的方法是忽略重复项。
如果只忽略重复项,那么我们将生成一个在相邻位置列出了相同提交ID的 commit-graph。这些多余的数据永远不会从 commit-graph
中删除,这可能会导致文件大小显著增大。
幸运的是,我们可以折叠列表以消除重复的提交指针。这使我们能够获得所需的最终结果,而不需要额外的内存成本和最小的 CPU 时间。
根本原因是禁用了 core.commitGraph
,这会防止在 'git commit-graph write --split
'(手册) 命令期间从较低层次结构解析提交。
由于我们使用 'graph_pos
' 值来确定提交是否在较低层次结构中,因此我们永远不会发现那些提交已经在 commit-graph
链中并将它们添加到顶层。然后合并此层,从而创建重复项。
没有这个更改,t5324-split-commit-graph.sh
中添加的测试将失败。但是,我们仍然没有完全消除对此重复检查的需要。这将在后续更改中实现。
并且:
报告者:Thomas Braun
协助者:Jeff King
协助者:Taylor Blau
签署者:Derrick Stolee
core.commitGraph
配置设置为'false
'可以防止从commit-graph
文件中解析提交记录。但这会导致一个问题,当尝试使用"--split
"进行写操作时,需要区分已存在的commit-graph
层中的提交和不在其中的提交。
现有机制使用parse_commit()
,并通过检查是否存在“graph_pos
”来判断提交是否来自commit-graph
文件。
当core.commitGraph=false
时,我们不会从commit-graph
中解析提交,并且'graph_pos
'表示没有提交在现有文件中。
--split
逻辑继续向前创建一个新层,包含所有可达的提交,然后可能向这些层合并,导致重复提交。先前的更改使得合并过程更加健壮,以处理在写入的commit-graph
数据中出现这种情况。
这里的简单答案是,如果禁用了读取commit-graph
,则避免编写commit-graph
。因为生成的commit-graph
将不会被后续的Git进程读取。这比强制core.commitGraph
对于“write”过程为true
更自然。
git commit-graph
现在在其手册页面中包含以下内容:
基于packfiles中的提交编写commit-graph
文件。如果禁用config选项core.commitGraph
,则该命令将输出警告,然后返回成功而不编写commit-graph
文件。
git fetch origin master:master
吗?我觉得理解这一点的关键可能在于这句话中,但是它的意思对我来说不太清楚:“虽然将要拉取的内容存储起来可能是有道理的,但当当前分支是'master'时,通常是错误的。”作者所说的“存储要拉取的内容”是什么意思? - sleeparrowgit fetch
的默认行为设置为不更新HEAD。但是为什么?我看到了这个讨论,但是无法理解原因为何? - Number945