我将一个旧的,多项目的Subversion repo转换为一系列单项目Git repos并试图保留尽可能多的原始数据。我已经对大部分转化进行了排序,但是标签出现问题。
由于标记被检入Subversion仓库的方式,每个标记当前都附加到一个空提交,它是应该附加的提交的子代。换句话说,我有一个具有以下提交结构的repo:
A -> B -> C -> D
\ \-> (empty) -> 1.1
\-> (empty) -> 1.0
理想情况下,我想做的事情就像交互式rebase,但我不想重新排序提交,而是希望将这些标记移到正确的提交上。
这样的事情可能吗?新的回购天堂已被推到任何地方,所以我完全可以自由地重写历史。
答案 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
)。
在使用此代码块之前,您需要计算的两个关键事项是:
$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-setup
和sane_grep () {
GREP_OPTIONS= LC_ALL=C grep "$@"
}
函数 - ettes位于warn
(设置脚本中还有die
),并做了显而易见的事情。
答案 2 :(得分:0)