使用--since的git filter-branch --tree-filter返回“未发现可重写内容”。

4
我正在尝试在特定日期之后的所有提交上执行一个命令:
git filter-branch -f --tree-filter HEAD -- --since="2014-06-01 13:37" 不幸的是,它总是输出“Found nothing to rewrite”并停止。在“--”之后的所有内容都应该被视为“rev-list”的选项,所以如果我尝试:
git rev-list --since="2014-06-01 13:37" 我将得到自2014-06-01以来的所有修订版本集合。你能告诉我哪里出了问题吗?

2
这看起来像是filter-branch脚本中的一个bug;它最终会使用两个“--”参数调用git rev-list,但是git rev-list只能正确处理一个。实际的bug来源对我来说不是很清楚,但在现有的git版本中,你实际上无法将这些选项传递给git rev-list - torek
谢谢@torek - 我很感激你的研究。所以不是我的问题,是git的问题! :) 为什么不发布一个带有这些信息的答案,我会接受它。 - ierceg
我其实没有时间写一个正式的答案,但如果你愿意,我可以把评论复制到一个答案里... - torek
当然。我可以自己做,但这样你就得不到声望了(虽然你也不是非常需要! :) - ierceg
1个回答

6
我复制了一个git存储库,并使用类似的选项运行了filter-branch脚本(只是编写了不同的--since以选择特定分支上的一个或两个版本),发现在脚本的深处,文档中说跟随--的参数将传递给git rev-list,但是filter-branch代码最终将这些参数作为路径指示符传递。这似乎是filter-branch中的一个错误。
特别地:
$ git rev-list --since=2013-01-01 branch
222c4dd303570d096f0346c3cd1dff6ea2c84f83
$ git rev-list branch
222c4dd303570d096f0346c3cd1dff6ea2c84f83
fb45c22c932d16903ae9e4debb8483a58a4e7799
fc85842f31baa62292abc3a539e86d0971caf8d9
2a5aaf8429ad810b25ef49f62bfda302b3193852
630a11ba2516023246e1e3eccf7023e915870489
4b597cf400a040a6bb14329890d65f126be88af2

这表明,如果我有意重写branch本身,则应该复制(经过筛选的)六个提交,但是使用--since=2013-01-01,它只应复制(经过筛选的)一个提交。由于我实际上不在branch分支上,因此我还将上面的HEAD替换为branch,以消除重写分支HEAD名称时选择的revs指向的问题。
同时,我提供了一个无意义的树过滤器(不做任何更改,但提供一些输出)。因此,我的filter-branch命令如下:
$ git filter-branch -f --tree-filter ls branch -- --since=2013-01-01 branch
Found nothing to rewrite

现在让我们揭示一下git filter-branch的内部工作原理。为了做到这一点,我必须暂时将/usr/local/libexec/git-core添加到我的$PATH中:
$ c=/usr/local/libexec/git-core  # so I don't have to retype it
$ PATH=$c:$PATH sh -x $c/git-filter-branch \
>  -f --tree-filter ls branch -- --since=2013-01-01 branch

-x 选项显示每行代码执行过程中的运行情况。在很多设置之后,输出会包含如下内容:

+ git rev-parse --no-flags --revs-only --symbolic-full-name --default HEAD branch -- --since=2013-01-01 branch
+ sed -e /^^/d /tmp/t2/.git-rewrite/raw-heads

(这将获取“需要重写的正引用列表”;尽管看起来似乎有“多余”的--,但此部分是正确的。)
+ test -s /tmp/t2/.git-rewrite/heads

(这可以确保至少有一个分支名称需要重写)

+ pwd
+ GIT_INDEX_FILE=/tmp/t2/.git-rewrite/t/../index
+ export GIT_INDEX_FILE

这是更多关于索引过滤器等设置的内容。

+ mkdir ../map
+ git rev-parse --no-revs branch -- --since=2013-01-01 branch
+ nonrevs='--
--since=2013-01-01
branch'
+ test -z '--
--since=2013-01-01
branch'
+ dashdash=''
+ remap_to_ancestor=t

此处检查传递给git rev-list的参数,以查找是否有基于路径的修订限制器。它确认存在此类限制器:将--since=...视为路径规范,因此将$dashdash设置为空,并将remap_to_ancestors设置为t(即true的缩写)。奇怪的是,$dashdash只在我们提供了--subdirectory-filter时使用,而我们并没有这样做。这段代码看起来可疑,但不是问题的直接源头。
下一部分:
+ git rev-parse --revs-only branch -- --since=2013-01-01 branch

将要重写的修订列表写入临时文件(../parse,此处未显示)。在我的情况下,这就是branch指向的地方,即222c4dd...本身。就目前而言,这还可以,但接下来:
+ git rev-parse --sq --no-revs branch -- --since=2013-01-01 branch
+ eval set -- \''--'\'' '\''--since=2013-01-01'\'' '\''branch'\'' '
+ set -- -- --since=2013-01-01 branch

这将更新参数为“仅传递给git rev-list的参数”,然后:
+ git rev-list --reverse --topo-order --default HEAD --parents --simplify-merges --stdin -- --since=2013-01-01 branch
+ wc -l
+ tr -d ' '
+ commits=0
+ test 0 -eq 0
+ die 'Found nothing to rewrite'

上面的git rev-list应该生成最终要重写的提交集。原始集合(来自--stdin和临时文件../parse)是正确的,但是它将--since=... branch作为路径规范传递(纯粹是限制修订版),而不是额外的修订版生成器(可能会添加修订版,如--all)。如果您使用手册页面中的一些示例,例如(我已修改此示例以成为无操作):
git filter-branch --env-filter : -- --all

它们之所以有效,是因为git rev-parse理解--all,因此不会传递给最后的git filter-branch

+ git rev-list --reverse --topo-order --default HEAD --parents --simplify-merges --stdin

实际上,--all 导致引用进入 parse 文件,这是 --stdin 的内容,因此该命令适用于所有可达提交记录。
总之,尽管这篇文章太长了(因为我没有时间缩短它),但它的核心问题在于,至少目前为止,在这里不能使用 --since 限制器。它们可能会起作用,但需要 filter-branch 脚本更加聪明地解析参数(可能需要使用 git rev-parse 的帮助)。如果最后一个 "set" 没有插入字面量 --,则 rev-list ... --stdin 将接收 --since=... branch 作为参数并执行正确操作。但对于实际的路径限定符,应该使用 --
我不确定 bug 是在于 filter-branch 文档建议在此处允许使用所有 rev-list 选项(实际上不允许),还是脚本中我所强调部分的代码应该更加智能或者只是不同(例如,路径限制符是否需要两个 -- 参数)。但无论如何,这必须是 git 中的一个 bug,因为文档建议可以使用 --since,而实际上却不能。

非常感谢您提供如此详尽的答案! - ierceg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接