git filter-branch --env-filter '
export GIT_AUTHOR_EMAIL="foo@example.com"
export GIT_AUTHOR_NAME="foo"' -- commita..commitb
Which ref do you want to rewrite?
所以filter-branch
似乎不允许你使用范围表示法使用两个任意参考之间的范围
在连续提交的范围内(在分支的历史记录中)运行过滤器的最直接的方法是什么?
正如@kan所说,您不能在历史记录的中间应用过滤器分支。您必须从已知的提交应用到历史记录
的末尾。git filter-branch --env-filter '...' SHA1..HEAD
Filter-branch可以检查提交作者或其他信息,选择更改或不提交,所以有方法来完成你想要的,见https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History,寻找"全局更改电子邮件地址"
请记住:如果您已经将提交推送到公共存储库,则不应该使用filter-branch
我发现最干净的解决方案是这样做:
- 创建
refb
临时分支 - 在
refa..temp
上应用分支滤波器 - 将base重新配置到临时分支并删除。
。
git branch temp refb
git filter-branch --env-filter '
export GIT_AUTHOR_EMAIL="foo@example.com"' refa..temp
git rebase temp
git branch --delete temp
git filter-branch
接受范围表示法,但范围的末尾必须是引用,而不是提交的ID。
git checkout -b tofilter commitb
git filter-branch .... commita..tofilter
如果给定的只是提交,它将不知道用过滤后的分支更新什么ref。
将过滤器命令包含在if语句中,以检查该范围。您可以使用以下命令检查提交是否在给定范围内:
git rev-list start..end | grep **fullsha**
当前提交将被存储在过滤器的$GIT_COMMIT
中。所以你的过滤器变成了:
git filter-branch --env-filter '
if git rev-list commita..commitb | grep $GIT_COMMIT; then
export GIT_AUTHOR_EMAIL="foo@example.com"
export GIT_AUTHOR_NAME="foo"
fi' -- ^commita --all
如果你只想重写你的当前分支,用HEAD
替换--all
你不能在历史记录的中间重写提交,因为每个提交的sha1都依赖于父类的。因此,git不知道过滤后您想要将HEAD引用指向哪里。因此,您应该重写到HEAD。
的例子:
A---B---C---D---E---F master
--G---H branch
如果你想过滤B和C的提交,你也应该过滤D, E, F, G, H之后的所有提交。所以,这就是为什么git告诉你在范围的末尾使用ref,这样它就不会以一个分离的头结束。
当你修改了B和C后,提交和停止将看起来像这样:
A---B---C---D---E---F master
--G---H branch
-B'--C' (HEAD or a temporary TAG?..)
因此,master
和branch
将保持不变。我觉得这不是你想要的。这意味着您必须覆盖所有提交。历史记录将会是:
A---B---C---D---E---F (loose end, will be garbage collected one day)
--G---H (loose end, will be garbage collected one day)
-B'--C'--D'--E'--F' master
--G'--H' branch
我是这样做的。
假设您想要过滤名为branch-you-are-filtering的分支的内容。
假设有一个祖先提交到该分支,带有一个ref-for-commit-to-stop-at。
git filter-branch --commit-filter 'YOUR_FILTER_COMMANDS' branch-you-are-filtering...ref-for-commit-to-stop-at
执行后,commit at ref-for-commit-to-stop-at将不会被更改。分支branch-you-are-filtering中所有被过滤的更改的提交都将基于ref-for- committostopat。
您是否使用——commit-filter或其他东西取决于您。
@Acron的解决方案对我来说似乎是错误的。我建议在refa和refb之间进行更改,包括两个散列:
-
git tag master.bak
-
git reset --hard refa
-
git filter-branch --env-filter ' export GIT_AUTHOR_EMAIL="foo@example.com"' refa^..master
-
git cherry-pick refb..master.bak
-
git tag -d master.bak
使用filter-branch的--setup
parm和一些shell功能:
git filter-branch --setup '
for id in `git rev-list commitA..commitB`; do
eval filterfor_$id=rewrite
done
rewrite() {
GIT_AUTHOR_NAME="Frederick. O. Oosball"
GIT_AUTHOR_EMAIL=foobar@example.org
}
' --env-filter 'eval $filterfor_$GIT_COMMIT'