gpt4 book ai didi

git - 当用户按下git时,它会锁定 Remote 以进行写入吗?

转载 作者:行者123 更新时间:2023-12-01 11:15:16 26 4
gpt4 key购买 nike

如果两个或更多用户同时将其本地存储库状态推送到同一远程,则执行git:

  • 锁定远程提交/分支/存储库以进行写入,然后再完成一个用户的全部提交,然后再提交另一个用户的?
  • 还是在它从一批N次提交中从单个用户写入单个提交后,释放它持有的提交/存储/分支的锁?

  • 首先是有道理的,但我想我还是会问。

    最佳答案

    TL; DR:mu

    该问题包含错误的假设,因此这两个选项都不正确。

    存在原子性问题,但不是基于每次提交的问题。它们基于每个引用。

    如果您仅推送一个引用(例如git push origin master),则只有一个引用需要更新。更新是成功还是失败,对于发送方而言,更新就差不多了(尽管仍然有很多接收方细节)。

    如果您推送多个引用(例如git push origin develop master),则有多个引用需要更新。如果您的Git支持它(两面都是v2.4或更高版本),请使用git push --atomic来确保两个推送都成功,或者都不成功。

    如果您不编写推送前,接收前,更新和/或接收后挂钩,则可以在此处停止。如果确实要编写它们,请继续阅读。



    锁定发生在接收方,而不是发送方(出于我希望是显而易见的原因:-))。该文档永远不会显式地调用内部细节,即使应该这样做也是如此。但是有许多单独的锁和锁定步骤。特别是:

  • 每个包文件有一个锁。
  • 如果存储库较浅,则为浅层移植点设置了一个锁。
  • 打包引用后端数据存储有一个锁(覆盖所有打包引用)。
  • 每个参考名称都有一个锁。1
  • 索引有一个锁(在大多数情况下,这并不重要)。

  • 读取参考不需要锁定。仅更新一个需要锁定它。这意味着纯读者可能会在过渡期间看到旧值。但是,在内部可以锁定一系列引用。请参阅下面的原子性注释。

    进行锁定包括使用原子性的“如果文件已存在,则创建或失败”操作来创建锁定文件。这必须由基础操作系统提供。解锁是通过删除或重命名锁定文件来实现的:锁定文件通常包含该锁定文件锁定的文件的新内容,因此要删除该锁定而不更改其内容,Git只需删除该锁定文件,然后删除该文件即可。锁定并更改文件的内容,作为单个原子操作,Git重命名了锁定文件。基本操作系统还必须提供原子重命名操作。

    更新压缩引用会将其转换为解压缩(“松散”),从而获得每引用锁定。打包引用显然需要获得packed-refs锁。但是,通过两种方式删除引用是一种特殊情况:
  • 未压缩的引用也可能出现在packed-refs文件中。 (当存在散装副本时,打包副本将被忽略。)在这种情况下,Git还必须更新打包引用文件以删除两个副本。
  • 如果引用存在,则删除引用会删除其引用日志。这在大多数情况下是不可见的,但这确实意味着参考更新代码想提前知道这是一个删除操作。


  • 1值得注意的是:有些参考是针对每个工作树的。最初这只是 HEAD,但随着 git worktree错误的浮出水面,现在它包括所有 refs/bisect/refs/rewritten/引用。 refs/rewritten/引用本身是新的,引入了新的,更新颖的交互式rebase,它可以重新创建任意合并。拆分bisect引用是Git 2.7.0中的一个修复。参见 commit ce414b33ec038

    另外,某些引用被认为是“伪引用”。这些从来没有打包。伪引用是 ORIG_HEADMERGE_HEAD等。这主要是一个内部细节,但会影响哪些锁可能适用:常规引用(例如 refs/heads/master)可以打包(在这种情况下适用打包的引用锁),也可以拆包(在这种情况下适用未打包的引用锁) 。

    推送顺序

    由于您对推送期间的原子性感兴趣,因此我们必须查看该过程的工作方式。

    第一步取决于传输协议版本,但通常,发送方从接收方收集参考名称和值的列表。这里没有锁。这些参考名称和值将显示在发送者的预推钩中。

    接下来,接收者让发送者收集对象并打包并发送它们(或发送单个对象,但这在今天非常罕见)。这里也没有锁,这可能要花费很多时间。在此过程中,接收器的参考值可能会更改。 的含义:在打包前的钩子中,您对发送方进行的任何检查都不能保证在打包文件到达完好且接收方开始处理它之前,接收方的引用是相同的。 但是,打包文件本身一旦完成就被锁定。

    在这一点上,如有必要,浅层移植文件将被锁定(我认为,这并不完全明显;稍后可能会发生)。

    接下来,发送方发送一系列更新请求(带有可选的强制标志)。接收器现在有机会查找并有选择地锁定每个要更新的参考。但是实际上,这里也没有发生锁定。接收器在没有锁定的情况下运行预接收挂钩。如果预接收钩拒绝了推送,则此时整个推送都将中止,因此没有任何改变。接收前挂钩审核整个更新后,如果您使用的是Git 2.11或更高版本(引入隔离),则打包文件(或单个对象)也将从隔离区中移出。

    接下来,接收器运行所有更新。这是原子性变得特别有趣的地方。 自Git 2.4.0版以来,git push具有一个新标记--atomic。这依赖于接收者通告原子更新。 有一个配置值 receive.advertiseAtomic,您可以在接收器上设置以禁用原子更新。如果:
  • 接收方发布原子更新功能(默认为true),而
  • 发送者
  • (无论谁运行git push)都了解原子更新功能,并且
  • 发件人选择--atomic

  • 那么接收方将立即锁定所有要更新的参考,然后再更新其中的任何一个。如果这些锁中的任何一个失败,则此处将放弃整个推送。如果它们全部成功,则接收器将在应用任何更新之前一次运行一次每个更新挂钩,以验证每个更新。如果任何更新挂钩失败,则整个推送将中止。如果所有更新挂钩都接受每个更新,那么将通过重命名释放每个锁来自动提交整个参考更新系列。2

    另一方面,如果发送方未选择 --atomic 3,则接收方将一次更新每个引用。它运行更新钩子,如果更新钩子说要继续,则用lock-update-unlock序列更新一个引用。因此,每个单独的更新都可以成功或失败。

    含义:带有或不带有--atomic,更新钩子都不应dilly-dally。 此时正在进行其他操作。由于推送可能没有 --atomic进行,即使您不确定是否会更新哪些引用,您也不能假定任何其他引用在这里都是稳定的。

    无论如何,在更新所有可更新的引用之后,Git会丢弃所有锁。如我们在顶部所述,通过更新基准锁来删除基准锁,但是如果需要,在更新浅移植点之后,Git现在也将浅锁和打包锁删除。然后,在未持有任何锁的情况下,Git运行后接收挂钩。 含义:接收后挂钩不能假定任何引用的当前值与其标准输入中的值匹配。 要查看已更新的内容,必须阅读stdin。要查看当前值,必须重新阅读参考;这两个可能不同步。

    2虽然单个重命名是原子重命名,但是当其他较早的重命名成功时,某些重命名可能会失败。目前尚不清楚在这种情况下会发生什么。

    3如果接收方配置说不通告原子,并且发送方使用 --atomic,则发送方本人将取消其事务。就是说,如果您运行 git push --atomic,并且接收者尚未宣告原子支持(由于接收者太旧而无法使用原子支持,或者因为接收者被配置为这种方式),您的Git会在此时停止。实际上,在这种情况下,您不能选择原子推送。

    结论

    从发送者的角度看,它看起来相当简单:如果您没有在预推钩中进行假设(或者首先没有预推钩),则可以使用 git push --atomic来使所有引用更新成为原子-整个推送将成功或失败-或不成功,在这种情况下,每个引用更新都会成功或失败。每个参考更新都包含以下其中一项:
  • 请将ref设置为hash(常规/非--force推送)
  • ref设置为hash! (git push --forcegit push ... +master:master)
  • 如果ref = old-hash,请将其设置为hash! (git push --force-with-lease)

  • 并且每个都可以单独拒绝,但是 --atomic表示如果任何一个被拒绝,则不会发生。

    从接收方来看,您可以在其中编写三种钩子,这很复杂。

    关于git - 当用户按下git时,它会锁定 Remote 以进行写入吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52662020/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com