GIT和0000000000000000000000000000000000000000的差异

3

我遇到了一个git问题,需要在我的pre-receive中运行"git --no-pager diff --name-only {oldrev} {newrev}"命令以比较两个commit之间的差异,但是当oldrev为"000000000000000000000000000000000000000000"时,会出现错误的对象错误。请问如何解决这个问题?


数据丢失 - bahrep
2
您没有先前的提交记录000000000000000000000000000000000000000000。如果您想询问完全没有先前提交记录的情况,请提出该问题。 - user743382
1
hvd,git将“00000000…”发送到标准输入(stdin),而不是我。 - ArthNRick
@cmaster 好主意。4b825dc642cb6eb9a060e54bf8d69288fbee4904 是你要找的那个。这应该对 OP 有用作为一个答案,如果你把它发布为这样的话,我会很高兴点赞的。 - user743382
但是我该如何进行比较呢?在状态“000”和{newrev}之间可能有500个提交,那么git diff {newrev}会返回所有的修改,不管有多少次提交吗? - ArthNRick
显示剩余7条评论
2个回答

4

git因为提交记录不存在而将其报告为000[…]000。这是一个刚创建且没有任何历史记录的分支/仓库的状态。因此,git diff 000[…]000..master会给出错误提示,因为您想要比较的“提交”不存在。

在某些情况下,与空树进行比较可能会有所帮助:

git diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904..master

这会给你一个包含当前 master 分支状态的差异。在这里,神秘的数字 4b825dc642cb6eb9a060e54bf8d69288fbee4904 只是空树的 SHA-1 值,根据定义它是一个真正的全局常量(感谢 @hvd 发现了该哈希值)。git 已经默认知道了这个空树,因此您不需要将任何内容提交到 git 中,它就可以正确地解析该哈希。


分支创建后主分支被修改过,这会有问题吗? - ArthNRick
请参见 https://dev59.com/UGkw5IYBdhLWcg3wm74h - torek
我这么说是因为主分支的最新版本可能与创建分支时不同,将分支的最后一次提交与当前主分支进行比较可能并不完全是分支创建时的主分支版本,而我们目前也不想进行任何合并或类似的操作(抱歉我的英语不好)。 - ArthNRick

3

cmaster的回答向您展示了如何与空树进行差异比较,这可能是您想要在此处执行的操作。不过,值得注意的是,您的pre-receive钩子中的全零哈希值代表着什么。同样的“null hash ID”也会出现在update、post-receive和post-update钩子中。您应该根据实际情况处理这些内容。在处理钩子中的null-hash-ID时,没有单一正确的答案。

Git引用——分支名称、标签名称或任何可以通过git fetchgit push发送的名称——总是标识一个(1)对象(通常是一个提交对象,除了指向提交对象的注释标签对象)。如果引用是分支名称,则可能认为其他附加提交“在分支上”,更正确地说是从分支名称可达到。 (有关更多信息,请参见Think Like (a) Git。)另一种思考方式是提交包含在分支中:通常,一个特定的提交包含在许多分支中。
当你的 Git 是 git push 的目标时,另一个 Git--这是另一个发起 git push 的源 Git--会通过 SSH 或 HTTPS 连接调用你的 Git 并与其交流。它们向你的 Git 提供一些对象(提交、注释标签、树和 / 或 blob),然后你的 Git 将它们保存在临时位置。1 然后它们会发送一些请求(非强制推送更新)或命令(强制推送更新),形式如:请设置您的 refs/heads/master,使其指向提交 1234567... 请设置您的 refs/tags/v1.2 , 使其指向注释标签 fedcba9...
你的Git接收这些请求或命令,并调用你的pre-receive钩子。因为这是你的仓库,所以你的Git知道这些名称(在这里是refs/heads/master和refs/tags/v1.2)是否已经存在于你的仓库中。如果它们确实存在于你的仓库中,它们将标识一个特定的Git对象。另一个Git正在提议更改它们,以便它们标识一些其他的Git对象。因此,你的Git会给你的钩子三个信息:
- 名称; - 名称当前标识的对象;以及 - 另一个Git建议(请求或命令)你的Git应该代替标识的对象。
(另一个Git也可能使用--force或+,但由于没有特别好或坏的原因,你的Git不会告诉你的钩子。)
但是有可能——对于标签而言,很有可能——他们Git发送的名称目前在你自己的仓库中并没有标识任何对象。在这种情况下,你的Git会给你的钩子提供一个全零哈希ID,假设你知道该怎么处理它。
这意味着当你编写这些钩子时,你必须准备接受一个全零哈希,并将其视为一个请求,即“创建指向指定对象的新名称”。事实上,这就是它的含义。
如果新名称是一个标签名称,那么我们通常认为标签指向一个特定的提交(可能通过一系列注释标签对象),并且“创建新标签”是我们通常期望的:标签不应该“移动”;标签名称v1.2今天不应该标识提交1234567...,明天也不应该标识提交89abcde...。(Git自1.8.4起自动执行此操作,甚至在运行您的钩子之前拒绝标签更新,除非其他Git设置了强制标志。)
对于分支名称,在提交Y处创建新的分支名称是一项预期操作,将现有分支名称从提交X移动到提交Y也是如此。如果从哈希值X到哈希值Y的移动仅向包含在分支中的集合中添加了新提交,则这是一种正常(或快进)操作。如果它删除了一些提交,则必须根据定义是强制推送,因为否则Git将在之前拒绝它。

(对于既不是分支名称也不是标签名称的名称,Git没有关于强制与非强制更新的内置规则 - 除非它使用分支规则来更新refs/notes/名称空间;我没有为这个答案检查这一点。)

请注意,还有一种引用更新,即删除请求或命令。在这种情况下,另一个Git要求或命令你的Git删除一些引用。您的钩子将接收该引用的名称,其当前对象以及所有零哈希ID作为新ID,以指示其他Git正在请求或命令删除操作。

在 pre-receive 或 update 钩子中,您可以通过以零状态退出来允许操作,或通过以非零状态退出来禁止操作。pre-receive 钩子在其标准输入上一次性获取整个提案 - 所有创建、删除和更新。update 钩子逐个获取一个建议的更改作为参数。因此,pre-receive 钩子要么允许所有更新(然后可以在 update 钩子中逐个选择拒绝),要么禁止所有更新(使发起 git push 的人再试一次,可能使用较小的更新集)。


在现代Git中,传入的对象存储在“隔离区”,只有在请求被接受后才会迁移到常规存储库数据库中。在旧版本的Git中,传入的对象会立即进入存储库数据库;如果需要,Git使用git gc将它们丢弃。

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