git - git filter-branch --tree-filter与--since返回“找不到要重写的内容”

标签 git git-filter-branch

我正在尝试对自某个日期以来的所有提交执行命令:
git filter-branch -f --tree-filter <command> HEAD -- --since="2014-06-01 13:37" <name-of-branch>
不幸的是,它总是输出Found nothing to rewrite并停止。--之后的所有内容都应视为rev-list的选项,因此如果我尝试:
git rev-list --since="2014-06-01 13:37" <name-of-branch>
我会得到自2014-06-01以来所有修订的正确集合。
我做错什么了?

最佳答案

我复制了一个git repo,并使用类似的选项运行了filter-branch脚本(只需创建一个不同的--since来从某个特定分支上的那些选项中选择一个或两个版本),然后在脚本的深处发现,文档中说--后面的参数被传递给了git rev-list,过滤器分支代码最终会像传递路径说明符一样传递这些参数。这似乎是过滤器分支中的一个错误。
特别地:

$ git rev-list --since=2013-01-01 branch
222c4dd303570d096f0346c3cd1dff6ea2c84f83
$ git rev-list branch
222c4dd303570d096f0346c3cd1dff6ea2c84f83
fb45c22c932d16903ae9e4debb8483a58a4e7799
fc85842f31baa62292abc3a539e86d0971caf8d9
2a5aaf8429ad810b25ef49f62bfda302b3193852
630a11ba2516023246e1e3eccf7023e915870489
4b597cf400a040a6bb14329890d65f126be88af2

这表明,如果我故意重写branch本身,它应该复制(通过过滤)六个提交,但是使用--since=2013-01-01它应该只复制(通过过滤)一个提交。因为我实际上并不在branchbranch上,所以我也将上面的HEAD替换为branch只是为了消除重写branchHEAD名称意味着什么的问题,同时选择由branch指向的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

将要重写的rev列表写入临时文件(../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和temp文件../parse)是正确的,但它已通过--since=... branch作为路径说明符(纯粹限制修订),而不是附加修订生成器(可能添加revs,如--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导致ref进入parse文件,即--stdin内容,因此这适用于所有可访问的提交。
无论如何,虽然这太长了(因为我没有时间缩短它),但它归结为一个事实,至少现在,你不能在这里使用--since限制器。它们可能应该可以工作,但它需要filter分支脚本更巧妙地解析参数(可能需要git rev-parse的帮助)。如果最后一个“set”没有插入文本--,则rev-list ... --stdin将接收--since=... branch作为参数,并执行正确的操作。不过,对于实际的路径限制器,应该有一个--
我不清楚这个bug是因为filter分支文档建议此处允许所有rev list选项(如果不允许),还是我突出显示的脚本部分周围的代码应该更聪明,或者可能只是不同(例如,路径限制可能需要两个--参数)。但不管怎样,这肯定是git中的一个bug,因为文档表明--since可以工作,但它不能。

关于git - git filter-branch --tree-filter与--since返回“找不到要重写的内容”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25615598/

相关文章:

git - 如何将 HEAD 移动到 git 中的最新日期?

git:如何从项目中分离出库?过滤器分支,子树?

git - Jitterbit 项目 + Git + 持续集成

git - 如何使用git-filter-repo将一个仓库作为子目录 merge 到另一个仓库

regex - 如何替换 Git 历史中的单词并正确调试相关问题?

git - 在 post-receive git hook 中使用 sudo 重启服务器

git log – 在不相关的历史之间添加分隔符,特别是对于--oneline

Git:什么是 graftcommit 或 graft-id?

git - 从 git 历史记录中删除已删除的文件

重命名文件夹后的 Git 过滤