【问题标题】:Running filter-branch over a range of commits在一系列提交上运行 filter-branch
【发布时间】:2013-03-06 14:33:23
【问题描述】:
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 不允许您使用范围表示法 使用两个任意引用之间的范围。

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

【问题讨论】:

  • 说真的,谁发明了这么无用的错误信息?该消息的唯一用途似乎是将其输入谷歌......“范围的结尾需要作为参考,而不是提交的 ID”(感谢@qqx)似乎更有帮助.

标签: git git-filter-branch


【解决方案1】:

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

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

filter-branch 可以检查提交作者或其他信息,以选择更改或不提交,所以有办法完成你想要的,请参阅https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History,查找“Changing Email Addresses Globally”

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

【讨论】:

  • PSA:它不能重写历史的中间,因为它会使附加在中间的尾端的父字段无效。
【解决方案2】:

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

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

即。

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

【讨论】:

  • +1 – 不错的技巧,但我认为过滤器分支需要在 refa..temp 上运行,并且您的 rebase 命令应该是 git rebase --onto temp refa refb
【解决方案3】:

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

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

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

【讨论】:

  • 在这种情况下,如何阻止我的过滤器脚本被应用于commita之前的提交和commitb之后的提交?
【解决方案4】:

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

【讨论】:

    【解决方案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?..)      
    

    因此,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
    

    【讨论】:

    • 如果我不想在C 之后对提交应用过滤器,您认为最好的方法是什么?我可以创建一个临时标签吗?
    • @Acorn 如果你想在过滤 C 之后保留提交(我称之为 C'),你必须过滤所有提交,至少将它们的祖先从 C 更改为 C',如果不,只需将它们移至主分支到C'。没有选择。提交之前的所有历史记录都在其 SHA1 id 中加密,因此每个提交都受到加密保护,您无法在不被注意的情况下更改历史记录。
    • 对,所以我觉得我必须决定是否在我的 bash 脚本中应用作者/电子邮件更改。如何判断我是否在该范围内?
    • @Acorn 这是一个新问题。也许更容易做的事情是通过git rev-list commita..commitb 获取您感兴趣的提交列表,并且当当前提交在列表中时检查您的脚本。另外,可能有用:stackoverflow.com/q/3005392/438742
    【解决方案6】:

    我是这样做的。

    假设您要过滤名为 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 或其他东西取决于您。

    【讨论】:

      【解决方案7】:

      @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

      【讨论】:

        【解决方案8】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-11-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-05-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多