代码之家  ›  专栏  ›  技术社区  ›  davidA

为什么Git允许远程标记移动,或者为什么不能将Git标记用于原子测试和设置

  •  4
  • davidA  · 技术社区  · 15 年前

    我有一个问题,两个相似的进程在同一个存储库(通常在不同的计算机上)的不同克隆中并行运行。每次进程运行时,它都从远程获取最新的标记,然后根据所看到的标记推断出一个唯一的数字。

    例如,如果远程设备上存在这些标签:1.0 1.1 1.2 1.3 然后一个进程将选择1.4作为下一个数字。

    在进程开始之前,它会创建一个新标记并将其推回到远程:

    $ git tag 1.4 HEAD
    $ git push origin tag 1.4
    

    这个 主意 这是一种原子选择数字的方法。另一个进程,如果同时查看,也可能决定使用1.4,但是当它推送它的标签时,它应该发现1.4已经存在,并选择1.5(然后重试)。

    我希望我能把git标签推送看作原子的。

    不幸的是,由于一些奇怪的原因,Git允许远程标签在某些情况下移动!

    例如,假设标签1.4已放在原点/主节点上并被推送。另一个进程希望将标签1.4放在origin/master^上,这将涉及向后移动标签。Git将以“非快进”错误拒绝此操作:

    进程A:

    $ git tag 1.4 origin/master
    $ git push origin tag 1.4
    Total 0 (delta 0), reused 0 (delta 0)
    To /repo1
     * [new tag]         1.4 -> 1.4
    

    进程B:

    $ git tag 1.4 origin/master^
    $ git push origin tag 1.4
    To /repo1
     ! [rejected]        1.4 -> 1.4 (non-fast forward)
    error: failed to push some refs to '/repo1'
    

    好的,没关系,过程B可以用这个来尝试1.5。

    但考虑一下这种情况:

    进程A:

    $git tag 1.4源站/主服务器
    $git推送源站标签1.4
    总计0(增量0),重复使用0(增量0)
    对/进行1
    *[新标签]1.4->1.4
    

    进程B:

    $ git tag 1.4 origin/master
    $ git push origin tag 1.4
    Everything up-to-date
    

    哦。真遗憾-Git没有指出这个标签已经存在于远程。实际上,它是这样的,用-v:

    $ git push origin tag 1.4 -v
    Pushing to /repo1
    To /repo1
     = [up to date]      1.4 -> 1.4
    Everything up-to-date
    

    好的,所以我可以做一些stderr重定向,搜索“=”,这将允许进程B确定1.4已经在使用中。

    但那有点傻。更糟的是:

    进程A:

    $ git push origin tag 1.4
    Total 0 (delta 0), reused 0 (delta 0)
    To /repo1
     * [new tag]         1.4 -> 1.4
    

    进程B:

    $ git push origin tag 1.4
    Total 0 (delta 0), reused 0 (delta 0)
    To /repo1
       fd0e09e..c6cdac9  1.4 -> 1.4
    

    阿尔格! 什么?Git没有警告就移动了远程标签!

    所以在我看来,Git中的远程标签基本上是被破坏的——它们不应该在没有明确请求的情况下“移动”。更重要的是,他们应该默认地拒绝搬迁。

    此外,git tag命令应该提供一种原子测试和设置标签的方法。

    但显然没有。首先运行git fetch没有帮助,因为仍然有一个冲突窗口,即使有冲突,在这三个场景中的一个,标签也会移动!

    这是怎么回事?

    有没有其他方法来测试和设置标签?

    如果不是,人们如何在自动构建环境中分配和保留构建编号?当两个进程无意中获得相同的内部版本号时,如何可靠地检测?

    使用Git 1.6.1.2。

    1 回复  |  直到 15 年前
        1
  •  8
  •   CB Bailey    15 年前

    我认为,如果你使用真正的标签对象而不是更设计为本地标签的轻量级标签,那么你的标签策略将是最好的选择。

    可以通过指定 -a (或) -m / -F ) -s -u 选项( git help tag )

    尝试你的例子,但是添加 -m "1.4 tag" 每次调用 git tag . 标记对象不能直接指向其他标记对象的后代,因此上面希望失败的每个推送案例都应该失败。