58
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不允许您使用范围表示法使用两个任意参考之间的范围。

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

4

8 回答 8

44

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

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

filter-branch 可以检查提交作者或其他信息,以选择更改或不提交,因此有多种方法可以完成您想要的,请参阅https://git-scm.com/book/en/v2/Git -Tools-Rewriting-History,寻找“在全球范围内更改电子邮件地址”

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

于 2014-01-07T14:49:13.113 回答
23

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

  1. 在 .创建一个临时分支refb
  2. 在 上应用分支过滤器refa..temp
  3. 重新定位到临时分支并将其删除。

IE。

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
于 2013-03-06T19:44:43.557 回答
12

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

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

如果只给出提交,它将不知道使用过滤分支更新什么 ref。

于 2013-03-06T14:52:33.453 回答
7

在检查该范围的 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

如果您只想重写当前分支,请替换--allHEAD

于 2013-03-07T07:53:09.130 回答
5

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

因此,masterandbranch将保持不变。我不认为这是你想要的。这意味着您必须覆盖所有提交。历史将是:

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
于 2013-03-06T14:53:07.947 回答
5

我这样做。

假设您要过滤一个名为 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 处的提交不会改变。分支branch-you-are-filtering 中所有已过滤\更改的提交都将基于ref-for-commit-to-stop-at。

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

于 2015-04-21T16:30:22.177 回答
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
于 2018-01-28T18:23:50.720 回答
0

使用 filter-branch 的--setupparm 和一些 shell power:

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'
于 2018-01-29T04:26:49.390 回答