在一系列提交中运行filter-branch

时间:2013-03-06 14:33:23

标签: git git-filter-branch

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不允许你使用范围符号使用两个任意引用之间的范围。

如果无法采用这种方法,那么在一系列连续提交(分支历史中某处)上运行过滤器的最直接方法是什么。

8 个答案:

答案 0 :(得分:25)

如@kan所说,你不能在历史中间应用过滤器分支。您必须从已知提交申请到历史记录的末尾

git filter-branch --env-filter '...' SHA1..HEAD

Filter-branch可以检查提交作者或其他信息,选择是否更改提交,因此有多种方法可以完成您想要的操作,请参阅http://git-scm.com/book/ch6-4.html,查找“更改电子邮件地址全局“

请记住:如果您已将提交推送到公共存储库,则不应使用filter-branch

答案 1 :(得分:23)

我发现最干净的解决方案是执行以下操作:

  1. refb创建临时分支。
  2. refa..temp上应用分支过滤器。
  3. 重新登录临时分支并将其删除。
  4. <强>即

    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
    

答案 2 :(得分:10)

git filter-branch 确实接受范围表示法,但范围的结尾需要是引用,而不是提交的ID。

git checkout -b tofilter commitb
git filter-branch .... commita..tofilter

如果只是提交,它将不知道什么引用过滤分支更新。

答案 3 :(得分:6)

将过滤命令包含在检查该范围的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

如果您只想重写当前分支,请将--all替换为HEAD

答案 4 :(得分:4)

我是这样做的。

假设您要过滤名为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

执行后,ref-for-commit-to-stop-at的提交不会被更改。分支分支中所有已过滤\更改的提交将基于ref-for-commit-to-stop-at。

你是否正在使用--commit-filter或其他东西取决于你。

答案 5 :(得分:2)

您不能只在历史记录中覆盖提交,因为提交的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?..)      

因此,masterbranch将不会受到影响。我不认为这是你想要的。 这意味着您必须覆盖所有提交。那时的历史就是:

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

答案 6 :(得分:0)

来自@Acron的解决方案对我来说似乎不对。我建议跟随在refa和refb之间进行更改,包括两个哈希:

  1. git tag master.bak
  2. git reset --hard refa
  3. git filter-branch --env-filter ' export GIT_AUTHOR_EMAIL="foo@example.com"' refa^..master
  4. git cherry-pick refb..master.bak
  5. git tag -d master.bak

答案 7 :(得分:0)

使用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'