如何在" interactive-rebase-like"中重写Git标签?方式?

时间:2015-08-24 01:21:39

标签: git

我将一个旧的,多项目的Subversion repo转换为一系列单项目Git repos并试图保留尽可能多的原始数据。我已经对大部分转化进行了排序,但是标签出现问题。

由于标记被检入Subversion仓库的方式,每个标记当前都附加到一个空提交,它是应该附加的提交的子代。换句话说,我有一个具有以下提交结构的repo:

A -> B -> C -> D
 \         \-> (empty) -> 1.1
  \-> (empty) -> 1.0

理想情况下,我想做的事情就像交互式rebase,但我不想重新排序提交,而是希望将这些标记移到正确的提交上。

这样的事情可能吗?新的回购天堂已被推到任何地方,所以我完全可以自由地重写历史。

3 个答案:

答案 0 :(得分:1)

您可以使用filter-branch命令执行此操作。

过滤每个标记,找到相关的子提交。然后把标签放在那里。

如果您愿意,也可以删除空提交作为过滤器的一部分。

答案 1 :(得分:1)

标记(无论是注释还是轻量级)只是指向提交。 1 因此,作为ryanve noted,您可以简单地强制标记指向不同的提交(因为它&# 39; s仍然是一个新的私人回购,没有其他人可以搞砸;你不必担心远程回购和标签副本。)

虽然git filter-branch can do the whole thing,但它往往是一个非常重量级的操作。相反,您可能希望删除复制带注释标记的filter-branch部分,并使用它来进行自己的替换。

为此,首先在编辑器中打开过滤器分支脚本,例如:

$ vim $(git --exec-path)/git-filter-branch

搜索git mktag,您将找到以下代码:

            if [ "$type" = "tag" ]; then
                    new_sha1=$( ( printf 'object %s\ntype commit\ntag %s\n' \
                                            "$new_sha1" "$new_ref"
                            git cat-file tag "$ref" |
                            sed -n \
                                -e '1,/^$/{
                                      /^object /d
                                      /^type /d
                                      /^tag /d
                                    }' \
                                -e '/^-----BEGIN PGP SIGNATURE-----/q' \
                                -e 'p' ) |
                            git mktag) ||
                            die "Could not create new tag object for $ref"
                    if git cat-file tag "$ref" | \
                       sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
                    then
                            warn "gpg signature stripped from tag object $sha1t"
                    fi
            fi

            git update-ref "refs/tags/$new_ref" "$new_sha1" ||
                    die "Could not write tag $new_ref"

请注意,仅当标记是实际带注释的标记对象时,$type才是$tag。另请注意gpg签名剥离 - 在您的情况下可能不是问题,如果您想签署新标签,则可以这样做。

此代码片段的一个键输入是现有的带注释的标记名称($ref)。

在使用此代码块之前,您需要计算的两个关键事项是:

  • 要标记的提交的SHA-1($new_sha1):它是现有标记指向的提交的父提交,因此它只是{的输出{1}}:找到标记(间接)指向的提交,并将提交图遍历到该提交的第一个(也是唯一的)父级。
  • 标签的新名称。如果要系统地修改标记名称,请相应地设置git rev-parse ${ref}^{commit}^。如果没有,只需使用new_ref(请注意,$ref将会就地更新现有引用,而不是创建新的引用,因此失败的错误消息会变得有点错误。)

完成上述操作后,您只需将此代码块 2 应用于每个带注释的标记。由于您已经验证它们都是带注释的标签,因此您根本不需要git update-ref测试:只需将相应的代码子集写入新的shell脚本,将其包装为一个函数,并在所有已知的带注释标签上调用该函数(可以使用if [ "$type" = "tag" ]获得)。

一旦你将所有标签移动到远离空端提交的位置,那么由于没有指向它们,侧面提交将变为不可见,并最终将被垃圾收集。

1 轻量级标记将直接指向提交 - 这是使其轻量级的原因 - 而带注释的标记以与轻量级标记相同的方式开始,即, git for-each-ref refs/tags名称空间中的引用,但该引用指向"标记"类型的存储库对象,其中包含the git mktag documentation中描述的文本。它指向的对象通常是提交,因此类型字符串也是refs/tags/,并且带注释的标记对象中的标记名称与轻量级标记的名称相同(减去{{ 1}}部分)。

标签(轻量级或带注释)可以指向提交以外的其他内容,例如,您可以将一个标记指向另一个标记。当然,如果您将轻量级标记指向带注释的标记对象,则会有一个带注释的标记,但如果带注释的标记的内部名称与轻量级标记的名称不匹配,则至少可以发现这种奇怪的情况...并不是说它有什么可做的,除了指出它是奇怪的。

2 大多数块是常规shell内置函数或命令。一个不常见的部分是commit,它实际上只是在运行refs/tags/但在某些特定情况下解决了破坏问题。您可以在同一个exec-path目录中的sane_grep脚本中找到它,或者只使用我从grep复制的脚本:

git-sh-setup

git-sh-setupsane_grep () { GREP_OPTIONS= LC_ALL=C grep "$@" } 函数 - ettes位于warn(设置脚本中还有die),并做了显而易见的事情。

答案 2 :(得分:0)

要重命名代码,您可以delete the old tags,然后tag the correct commits with the new names