是否允许在 Mercurial 中重新定位标签?

Is rebasing a tag in Mercurial allowed?

要更新我们软件中的版本号,脚本会执行以下操作:

$ hg update v3.3
(sed+awk magic to edit version numbers in code base)
$ hg commit -m"Create v3.3.50"
$ hg tag v3.3.50
$ hg push    
abort: push creates new remote head 101b0ff402c6 on branch 'v3.3'!
(pull and merge or see "hg help push" for details about pushing new heads)
$ hg pull --branch v3.3 --rebase
...
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
$ hg push
...
added 2 changesets with 4 changes to 4 files

但是在提交后,标签似乎不存在于目标存储库中:

$ hg tags | grep v3.3.50
$

令人困惑的是,标签在 .hgtags 文件中:

$ grep v3.3.50 .hgtags 
e7d6c19f8dd86cdad4cb41f543d09dbe5d30405e v3.3.50

并且在修订历史中:

$ hg log -b v3.3
changeset:   7067:701358ca0f4b
branch:      v3.3
user:        Joe User <juser@example.com>
date:        Wed Nov 11 12:41:15 2015 -0800
summary:     Added tag v3.3.50 for changeset e7d6c19f8dd8

changeset:   7066:19aafdd33263
branch:      v3.3
user:        Joe User <juser@example.com>
date:        Wed Nov 11 12:41:15 2015 -0800
summary:     Create v3.3.50

hg commit / tag / push 序列按预期工作,但添加 rebase 似乎至少部分删除了标签。标签是否需要对 rebase 命令进行一些特殊处理?

Mercurial 版本是 2.9.2,有问题的系统是 运行 Ubuntu 的最新版本。

.hgtags 文件中的标签由变更集哈希 ID 标识。变基更改集会更改哈希,因为在计算哈希时包含有关其父项的数据。

在您的示例输出中,"Create v3.3.50" 变更集的散列值为“19aafdd33263”- 这与标签文件和标签变更集中引用的 e7d6c19f8dd8 不同。

不过请注意,您可以重新设置添加标签的变更集的基准,因为标记的变更集散列不会更改。

这要么是疏忽,要么是有意设计,很难说到底是哪一个(详情见下文)。

您可以通过更新到分支的头部并通过以下方式重新标记来修复它:

hg tag -f -r REVID TAGNAME

能够更改或删除标签是 intended design,所以这并不奇怪。不过,原始标记提交将保留。

如果您想明确说明这是标签更新,请使用

hg tag -f -e -r REVID TAGNAME

它允许您编辑标签提交消息,表明这是对原始标签的更新。

如果你已经安装了evolve,你可以避免查找原始版本并使用:

hg tag --hidden -f -r 'successors(TAGNAME)' TAGNAME

此处,successors(...) revset 函数描述了带有 TAGNAME 标记的原始修订版本已演变为哪个修订版本。

如果您已经进化或使用 hg histedit(并且您的变基历史不包含自标签以来的合并),您也可以(原则上)更改原始标签提交,但我建议不要它,因为它可能有点挑剔(你基本上必须手动编辑 .hgtags 并更新提交消息)。

如果你想这样做,最简单的进化方法是:

hg update --hidden -r 'successors(TAGNAME)'
edit .hgtags                                 # update tag information
hg commit --amend                            # update commit message
hg evolve -a                                 # propagate changes

histedit 有点复杂:

hg histedit -r REVID                         # REVID = tag commit
# In the histedit editor, change "pick" to "edit" for the tag commit,
# then write the file and leave the editor.
edit .hgtags                                 # update tag information
hg commit                                    # provide new commit message
hg histedit --continue                       # rebuild rest of history 

这两种方法都依赖于这样一个事实,即 hg tag 只是执行一个正常的提交,该提交会更改 .hgtags 并自动生成提交消息。 Mercurial 只会依赖 .hgtags 中的信息,不会检查任何元数据。我还建议事先在本地克隆存储库,以防万一您犯了错误并且不知道如何从中恢复。

同样,我认为这不是必需的(甚至不是一个好主意),但最终决定权在您。


那么,这是错误还是设计使然?在 rebase 期间自动移动标签的一个问题是原始修订通常会保留(hg rebase --keep 或只是简单的 rebase with evolve),在这种情况下,您不清楚是否要移动标签或不是。它也没有解决 hg graft 的类似问题。所以,它可能是。

我们最近也遇到了这个问题,并试图为其提供复杂的解决方案,包括历史操作和更新提交消息等。最后我们意识到,rebase 一个未推送的变更集是错误的,因为我们随后将指向 out 存储库的唯一快照的东西移动到一个新的快照,这是不一样的,因为 rebase 做了什么,所以我们最终采用了以下规则并相应地更改了我们的脚本:

Never tag an unpushed changeset

即在我们的构建脚本中,我们现在 pull、测试、增量版本、commitpull --rebase(如果需要)、push 并记住哈希,然后 tagthat 散列上,commitpull --rebase 如果需要再次 pull --rebase(现在没有任何东西可以再损坏),最后 push 对“.hgtags”所做的更改。

我为 Mercurial 打开了一个功能请求以更好地解决这个问题: https://bz.mercurial-scm.org/show_bug.cgi?id=5352