【问题标题】:Two users git commit in the same second两个用户在同一秒内 git commit
【发布时间】:2025-12-27 12:30:12
【问题描述】:

如果两个用户同时在各自的笔记本电脑上 'git commit',然后 git push 到同一个 git 存储库,git 如何确定在运行 'git log' 时哪个提交首先出现?

This post 表示 git 的时间戳分辨率是 1 秒。

【问题讨论】:

  • 第二个推送的人将无法推送,并且会收到一条消息告诉他们先推送。我不认为提交时间戳解析参与其中
  • 时间对 git log 中的提交顺序不敏感。具有小时间戳的提交可以位于具有大时间戳的提交之前或之后。

标签: git timestamp unix-timestamp


【解决方案1】:

提交顺序由提交图表决定,而不是由日志中的时间戳决定。但是,git log 的输出可以按时间戳顺序显示,而不是按图形顺序显示。因此,您的问题的答案是“是”、“否”或mu,而不是真正有用的东西。这是棘手的部分。

首先请记住,每个提交都有一个唯一哈希ID。这个散列 ID,通常表示为一个大的 hexadecimal 数字,看起来是随机的,通常不被人类使用,实际上是提交内容的加密校验和,这意味着。它之所以独特,部分原因在于它会:理论告诉我们最终会发生哈希冲突,但它也不会在数十亿年内发生。1 即使有一个 发生,Git 对此的回答是“你不能添加那个提交”。2 所以哈希 ID 在某种意义上的提交。

同时,每次提交都会记录两件事:

  • 每次提交都有所有文件的完整快照。
  • 每个提交都有一些元数据,例如提交的人员和时间:例如,git log 输出中可见的时间戳来自此元数据。

任何给定提交中的元数据都包括上一个提交的哈希ID。所以这意味着提交实际上是根据一些后来的提交记录其哈希 ID 的事实来排序的。这个“后来提交记录早期提交的哈希 ID”提供了提交图。此提交图采用有向无环图的形式:较晚的提交指向较早的提交,并且循环被排除,因为较早的提交不能包含稍后提交的哈希 ID。3

这一切归结为 DAG 本身,而不是时间戳,决定了提交顺序。在您描述的场景中,用户 U1 和 U2 从同一个起始提交“同时”进行两次提交,我们最终在图中出现了分歧:

             1
            /
...--o--o--C
            \
             2

提交 1 的父节点是提交 C,提交 2 的父节点是提交 C。提交12 相对于彼此无序:它们是图中的兄弟姐妹,因此它们之间没有父/子关系。两者都是C 的子级(两者都以C 作为父级),因此在图建立的偏序中,C1C2,但1 和@ 987654339@ 只是无序的。

现在,不可能定位提交 12,除非:

  • 您知道他们的原始哈希 ID,或者
  • 您有名字可以用来定位他们或他们的任何继任者。

也就是说,如果我们有两个分支名称,我们可以找到两个提交:

             1   <-- user1
            /
...--o--o--C
            \
             2   <-- user2

运行git log user1 会生成一个显示提交1 的日志,然后是提交C,然后是C 左侧的第一个提交(此处标记为o),依此类推:提交2 根本没有显示。运行git log user2 会生成一个日志,显示提交2,然后提交C,然后提交到图中C 的左侧。

但是,运行 git log user1 user2 会将 both 提交 12 插入到 to-be-shown 队列中。 此时git log 将选择“更高优先级”的提交来显示。

在此git log 优先级队列中,提交的优先级由以下各项控制:

  • 默认的提交者日期戳,或者
  • 如果使用--author-date,则为作者日期戳,或
  • 如果在 git log 时间指定,则按其他标准。

如果我们假设提交者和作者的日期戳相同,那么这两个提交在队列中具有相同的优先级,其中一个会先出来,但不能保证 哪个。他们只是出来,但是他们出来了。

我们无法通过一个名称找到两个提交,因此在两个用户都可以git push这些提交之前,至少一个必须做一些棘手的事情,例如如:

  • 创建一个新的分支名称,或者
  • rebase 提交(将提交复制到新的改进提交),或
  • 合并一些提交(添加一个具有两个或更多父级的新提交)。

我们已经知道创建新分支名称的效果,正如我们在上面看到的那样。如果一个用户rebase他或她的提交在另一个之上,结果是一个新的不同的提交在一个新的和不同的图表中:

...--o--o--C--1--2'   <-- branch-name

例如。在这里,git log 将首先显示提交 2',然后提交 1不管任何日期戳

如果一个用户合并,我们可以得到这个:

             1
            / \
...--o--o--C   M   <-- branch-name
            \ /
             2

现在,如果有人在此分支上运行 git log,他们将首先看到提交 M,然后按优先队列顺序提交 12 — 因为查看提交 M 会将两个提交都推送到优先队列——然后像以前一样提交C。优先级队列中的顺序取决于 git log 的参数,就像我们通过两个分支名称给它两个起始提交哈希 ID 的情况一样。但是,将--first-parent 添加到git log 将导致git log 仅将提交Mfirst 父级推入队列,因此访问提交@987654377 后队列深度仅为1 @。因此,我们将只看到提交M,然后只看到第一个父级(无论是哪个父级),然后只提交C,依此类推:访问提交会将其父级推入队列,--first-parent 将其限制为它的第一个父母。


1实际经过的时间取决于我们生成哈希的速率,以及有人在破解密码学方面取得的任何进展,但在实践中它“足够长” .

2以“您无法添加新提交”为代价巧妙地解决潜在问题(旧的提交哈希 ID 保持唯一)。

3为了强制执行此操作, 较晚提交的加密校验和 包括较早提交的哈希 ID 的十六进制表示。这意味着您必须知道未来提交的哈希 ID 是什么,才能将该哈希 ID 包含到当前提交中;但是知道这一点并包含它会更改 this 提交的哈希 ID,这反过来会更改该未来提交的哈希 ID。除非你能找到一个固定点或小循环,否则你是做不到的。

【讨论】:

    最近更新 更多