git 在通过 SSH 工作时推送不正确的提交 SHA

git pushes incorrect commit SHA when working via SSH

背景: OS: Windows Git版本:2.11.0

根据我们的政策,我们是: 1. 未推送的提交在推送前的提交消息中增加几个字段 2. 在远程验证推送的提交消息是否包含必需的字段

我们正在通过 "git --filter-branch --msg-filter" 编辑提交消息,它在预推送挂钩期间触发。

尝试通过 SSH 推送时,git 在重写之前推送提交 SHA。 例如:

  1. 未推送的提交 SHA 是:38dad1575a3c4239c967564c21347aad3d5b2a55
  2. 运行 git 推送
  3. 预推运行并重新写入提交。
  4. 现在我的提交 SHA 是:8115fdfb3be86a6b51284cd1d278bd55017990ce。
    之前的提交 38dad1575a3c4239c967564c21347aad3d5b2a55 现在存储在 /refs/original/refs
  5. 推送失败,因为提交 38dad1575a3c4239c967564c21347aad3d5b2a55(未编辑的提交)在其提交消息中不包含必要的字段。

此行为仅在通过 SSH 工作时发生。对于通过 HTTPS 工作的用户,一切正常。

如有任何帮助,我们将不胜感激。

谢谢!

作为一般规则,不允许挂钩修改提交本身。有一些特定的情况是明确允许的,有些是偶然发生的,但是一旦提交实际上 存在 不能 被改变。 git filter-branchgit commit --amend 实际上不会更改任何提交;相反,它添加了一个类似于原始提交的 new 提交,但具有您所做的任何更改。这个新提交有一个新的、不同的哈希 ID。

令人惊讶的不是使用 ssh 失败,而是使用 https 成功。这只是运气,有人可能会争辩说,它 有效 运气。 pre-push 挂钩仅用于表示 "yes, this push is allowed" 或 "no, this push is forbidden"。它不应该更改 name-to-commit-ID 映射。很明显,实际发生的事情是基于 http 的推送最终会重复名称到 ID 的映射并获取新的 ID,而基于 ssh 的推送会保留它最初获得的 ID。

现在,您实际上可以完成所有这些工作:

  • 在pre-push钩子中,检查是否允许推送。
    • 如果是,请通过。
    • 如果没有,准备拒收。进行允许的新提交,并可选地推送该提交(递归地,在外部 git push 期间使用内部 git push 运行-递归将终止,因为 new 刚刚提交的内容将被允许,因此该逻辑分支在内部推送期间不会 运行)。然后打印一条错误消息,指出(以及为什么)原始推送被拒绝,是否完成了 "inner" 推送,如果成功,是否成功,如果没有,它的 ID 是什么以及用户如何使用它.最后拒绝推送。

避免这种递归方法有几个原因:(a) 它违反了任何有经验的 Git 用户的期望,他们不希望自己的提交被替换和推送; (b) 如果出错——例如,如果递归没有立即终止——它可能会出错非常。 (b) 部分可以通过 pre-push 钩子中的聪明之处来缓解(例如,将环境变量导出到内部推送,注意正在发生递归,如果要递归,则在设置此变量时会大声失败)。第 (a) 部分也许是可以原谅的,因为 pre-push 挂钩毕竟是用户必须首先自行设置的东西。

即使您处理了这些异议,还有一个避免它的理由:它会使外部 (non-recursive) 推送失败。即使内部推送成功,用户也会看到推送失败。这至少会让人有些困惑。