Git 或 libgit2 如何处理竞争条件?

How does Git or libgit2 deal with race conditions?

我使用 libgit2 并问自己,如果 2 个进程同时对同一个 repo 执行写操作会怎样?

下面的示例是提交到回购协议的简单示例。它由许多命令组成。

如果一个进程A执行了前3条命令挂了几秒,而另一个进程恰好执行了相同顺序的命令却跑遍了所有,那么A 继续。我知道这很少见,因为 Git 操作主要是用户交互,用户很少同时使用 2 个这样的命令,但是 我想了解的理论含义Git 中的此类比赛条件可确保我不会破坏任何东西。

非常感谢!

check_lg2(git_repository_index(&index, repo), "Could not open repository index", NULL);
check_lg2(git_index_write_tree(&tree_oid, index), "Could not write tree", NULL);;
check_lg2(git_index_write(index), "Could not write index", NULL);

check_lg2(git_tree_lookup(&tree, repo, &tree_oid), "Error looking up tree", NULL);

check_lg2(git_signature_default(&signature, repo), "Error creating signature", NULL);

check_lg2(git_commit_create_v(
    &commit_oid,
    repo,
    "HEAD",
    signature,
    signature,
    NULL,
    comment,
    tree,
    parent ? 1 : 0, parent), "Error creating commit", NULL);

Git 使用多种方法来处理这种情况,libgit2 使用相同的方法来实现兼容性。

当 Git 需要更新具有固定名称的文件时,如索引或引用,它会创建一个具有相同名称但以 .lockO_CREAT 结尾的文件和 O_EXCL 然后将新内容写入该文件。然后它对现有文件使用原子重命名。在 Unix 系统上,这意味着打开旧文件的进程将看不到任何变化,新进程将打开新文件。

如果您将一个对象写入对象存储,例如树或提交,那是无竞争的,因为它的名称是唯一的。该文件存在或不存在,如果不存在,则将其创建为临时文件并重命名到位。重命名意味着如果已经存在相同的对象,它将被替换为相同的副本。

现在,您可能想要写入一个文件,例如索引,而其他人已经在这样做了,在这种情况下,您必须等到锁定文件消失。通常等待的时间很短,所以等待不是问题。对于索引之类的东西,libgit2 提供了内存中的索引,它们都不需要锁定,并且如果您决定不需要它们更容易丢弃。

请注意,Git 不能保证工作树中的原子操作,因为如果出现问题,真的不可能完全回滚。如果您只是处理存储库内容而不更新工作树,那么 Git 应该没有竞争条件并且对多个用户具有合理的性能。