即使使用了"receive.denyNonFastForwards=true"拒绝推送,GIT仍然会通过"receive.denyCurrentBranch=updateInstead"更新工作树。

5
我把这个问题/bug提交到了GIT官方频道,但没有得到回应。希望这里的某个人能够帮助我。
receive.denyCurrentBranch设置为updateInstead并且receive.denyNonFastForwards设置为true时,下面的行为对我来说似乎不正确。以下是重现该场景的步骤。 步骤1-设置远程仓库(远程主机):
git config --global receive.denyCurrentBranch updateInstead
git config --global receive.denyNonFastForwards true
mkdir /tmp/hello
cd /tmp/hello
git init
echo hello > hello.txt
git add . && git commit -m "hello.txt"

步骤2 - 创建2个克隆(本地主机):

git clone ssh://REMOTEIP/tmp/hello /tmp/hello1
git clone ssh://REMOTEIP/tmp/hello /tmp/hello2

第三步 - 从克隆1提交更改

cd /tmp/hello1
echo hello1 > hello1.txt
git add . && git commit -m "hello1.txt"
git push

目前为止,服务器工作树中包含了预期的 hello1.txt 文件。

步骤4:尝试从克隆2强制推送提交

cd /tmp/hello2
echo hello2 > hello2.txt
git add . && git commit -m "hello2.txt"
git push

远程主机拒绝我的推送,并提示我本地没有的工作内容,这是正确的。现在我强制推送。

git push --force

远程服务器再次拒绝并显示错误:拒绝非快进的refs/heads/master(您应该先拉取)

由于推送被拒绝,我预计服务器的工作树不包含任何被拒绝的更改。但是,服务器的工作树已更新以删除hello1.txt并创建hello2.txt。推送被拒绝但实际上并没有。

当更新钩子(而不是pre-receive钩子)拒绝更改时,我也注意到相同的行为(不正确)。


这个问题在 Git 2.20(2018年第四季度)中已经得到解决:请参见我的下面的答案 - VonC
2个回答

4
问题在于这两个配置存在冲突(尽管原则上它们不应该存在冲突):

receive.denyCurrentBranch 设置为 updateInstead

在这种情况下,在接收期间,Git 注意到目标是当前分支,因此 Git 检出提交。

receive.denyNonFastForwards 设置为 true

这是分开发生的:对于名称的更改被拒绝。提交已经被接受,并且仓库的检出已经发生了,现在对于名称的更改被拒绝。

我还注意到当更新钩子拒绝服务器上的更改时(但不是 pre-receive 钩子时),也会出现相同的行为(不正确)。

这是相同的问题:pre-receive 钩子在任何单独的引用更新之前运行一次,并可以拒绝整个推送。然后,如果 pre-receive 钩子已经清除了继续进行的事情,则更新钩子可以拒绝任何单个 ref-name 更新。但是,updateInstead 与分支名称更新被接受或拒绝是分开的。
内部避免工作树更改直到审核引用更新可能会更好。这需要在 Git 的内部进行一些重构。如果这不被认为是 Git 中的明显错误,那么至少它非常令人惊讶。实际上,所有这些代码都需要进行一些工作,因为如果使用 git worktree add,则 Git 无法将添加的工作树的 HEAD 视为当前分支。鉴于所有这些警告,我建议仅使用裸仓库作为推送目标,并使用 post-receive 钩子将更新定向到其他仓库或工作树。
另外(这是一个注释,但长度太短,表达得不够清楚):
git config --global receive.denyCurrentBranch updateInstead
git config --global receive.denyNonFastForwards true
尽管这与您观察到的特定行为无关,但这绝对是错误的做法。运行git config --global会为您个人设置配置项。也就是说,这些配置项存储在/home/rajesh(或任何您的主目录配置所在的位置)。但是receive.*设置应该是每个仓库独立的。
由于您使用ssh作为自己进行推送,因此这些配置参数确实生效了。但是,如果您通过其他方法进行推送,则可能不会生效。任何使用git config而没有使用--global的每个仓库设置仍将生效。

1
我完全理解“--global”的问题,感谢您的回答并指出全局配置的问题。根据您的解释以及为了获得我想要的行为(使用一个或多个内置的receive.deny*选项以及更新远程工作树),您认为重新实现updateInstead功能作为post-receive钩子是更好的方法吗?基本上在post-receive中执行checkout操作。 - Rajesh
3
是的,post-receive 方法会更好地工作。(事实上,updateInstead 行为以及所有与 git worktree add 相关的行为都不好,因此服务器一开始就应该有裸库,并在其他地方执行任何部署技巧,包括 post-receive 钩子。) - torek
1
当然点赞了,但是“Git在审核引用更新之前内部避免工作树更改可能会更好。”可能有点温和,如果这不是违反POLA原则的话,我不知道还有什么能违反。 - jthill
1
@jthill:同意。我想我会更加强调这一点。 - torek

1

我希望服务器的工作树不包含任何被拒绝的更改。但是,服务器的工作树已经更新以删除 hello1.txt 并创建 hello2.txt。
推送被拒绝了,但实际上并没有。

从 Git 2.20 版本开始(2018年第4季度),推送将 真正 被拒绝。

在此之前,即使由于其他原因(例如不是快进或更新挂钩拒绝它)而应该拒绝推送,receive.denyCurrentBranch=updateInstead 代码路径仍会启动,这一问题已得到修正。

请看提交 b072a25(2018年10月19日)由Junio C Hamano (gitster)进行。
(由Junio C Hamano -- gitster --合并于提交 4c7f544,2018年10月30日)

receive: denyCurrentBranch=updateinstead should not blindly update

处理receive.denyCurrentBranch=updateInstead的方式被添加到了一个switch语句中,该语句处理变量的其他值,但是所有其他情况都只检查拒绝尝试推送的条件,或者让同一函数中的后续逻辑仍然进行干预,因此不快进的推送(在相关的switch语句之后检查)仍然被拒绝。

但是,updateInstead的处理错误地立即生效,没有给其他检查的机会来干预。

不要调用立即导致副作用的update_worktree(),只需注意我们需要稍后调用该函数的事实,并首先给其他检查拒绝请求的机会。
update-hook有机会拒绝推送(这是一系列检查的最后一步),当我们早期检测到需要时,调用update_worktree()

在您的情况下,由于推送被拒绝,您的工作树将不包含任何被拒绝的更改,这是预期的。


Torek说:

事实上,所有这些代码都需要进行一些工作,因为如果您使用git worktree add,Git会失败地将添加的工作树的HEAD视为当前分支

现在不再是这样了。

从Git 2.26(2020年第一季度)开始,在设置receive.denyCurrentBranch配置时,"git push"应该停止更新已检出的分支,但它未能注意到次要工作树中的检出。

这个问题已经得到了纠正。

请查看由Hariom Verma (harry-hov)于2020年3月4日提交的提交4d86489, 提交4ef3464, 提交f869211提交45f274f
(由Junio C Hamano -- gitster --合并于2020年3月5日的提交4a2e91d)

receive.denyCurrentBranch:尊重所有工作树

协助者:Johannes Schindelin
签署者:Hariom Verma

receive.denyCurrentBranch配置选项控制了当你向一个被检出到非裸仓库的分支推送时会发生什么。
默认情况下,它会拒绝推送。
可以通过ignorewarn来禁用它。另一个更棘手的选项是updateInstead

然而,当git worktree命令被引入时,这个设置被忽略了:只有主工作树的当前分支被尊重。

通过这个改变,所有工作树都会被尊重

这个改变也揭示了另一个错误,即在使用ref名称空间将提交推送到未出现的当前分支时,receive.denyCurrentBranch = true被忽略了。
因为is_ref_checked_out()返回0,这意味着receive-pack不会进入条件语句以相应地切换deny_current_branch(ignore、warn、refuse、unconfigured、updateInstead)。

receive.denyCurrentBranch使用refs_resolve_ref_unsafe()函数(通过resolve_refdup()调用)来解析符号引用HEAD,但是当HEAD不指向有效提交时,该函数会失败。

由于我们用find_shared_symref()替换了对refs_resolve_ref_unsafe()的调用,即使分支尚未出现,它也可以找到给定分支的工作树,因此这个错误同时被修复了:receive.denyCurrentBranch现在也可以正确处理有未出现的分支的工作树,即使使用ref名称空间


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接