如何重新创建git提交历史记录并应用预提交钩子?

3
我正在尝试重写git存储库的历史记录,并应用新的pre-commit钩子:
  1. 获取每个提交
  2. 应用pre-commit钩子
  3. 保留原始元数据(作者,日期,消息)
  4. 手动解决冲突(如果有的话,钩子可以更改提交)
  5. 提交到新的存储库
最终状态是具有不同提交历史记录的新存储库。
我已经找到的内容:
  • cherry-pick不运行pre-commit钩子。
  • 我可以执行以下操作:
git cherry-pick --no-commit
git commit --no-edit

但是它并不保留提交日期。而且,我不确定如何为历史记录中的每个提交执行此操作(除非我为此编写例如 Python 脚本)。
有没有什么高效的方法可以做到这一点?

2
为了避免询问 XY 问题,请解释为什么需要这样做,因为“常见”的应用新钩子的方法是通过引入一个提交并将其哈希存储在 .git-blame-ignore-revs 文件中,以从 git blame 输出中排除这些修改。(更改将看起来像钩子应用后,但没有对其的引用;日期显示自旧提交,因此与您的期望相匹配) - SUTerliakov
@SUTerliakov 我想重写历史,防止在文本文件中添加额外的换行符。我知道我可以在新提交中删除它们,但我更喜欢从历史记录中删除它们。我有一个预提交钩子来实现这一点;现在我需要将该钩子应用于历史提交并重新提交它们。 - Dennis Golomazov
2个回答

3

使用--exec标志和自定义的GIT_SEQUENCE_EDITOR,在git rebase命令中跳过交互式提示和选择列表。示例:

GIT_SEQUENCE_EDIT=cat git rebase --root --exec .git/hooks/pre-commit

这将在选择列表中的每个pick <commit>之后添加exec .git/hooks/pre-commit。如果pre-commit钩子失败,那么将中断rebase

Executing: .git/hooks/pre-commit
warning: execution failed: .git/hooks/pre-commit
You can fix the problem, and then run

  git rebase --continue

您可以手动解决问题,然后执行git rebase --continue

0

借鉴@larsks的想法,我使用以下命令实现了目标。我在这里留下它作为参考。

  1. 创建一个空分支[1]:
git switch --orphan new_history

2. 将主分支重新基于此,对每个提交执行 pre-commit 钩子:
git rebase --root main --exec .git/hooks/pre-commit -X theirs

请注意,我使用了theirs合并策略,以便在冲突的情况下,使用主分支的版本([2],[3])。
3.之后,所有提交的提交日期都设置为今天。为了解决这个问题,我运行了([4]):
git filter-branch --env-filter 'export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"' 

这是我的pre-commit脚本:

#!/bin/sh

set -x 

IFS=$'\n'

files=$(git diff-tree --no-commit-id  --name-status -r HEAD | grep -v ^D | cut -c3-)

if [ "$files" != "" ]
then

  for f in $files
  do
      if [[ "$f" =~ [.]md$ ]]
      then

          # Add a linebreak to the file if it doesn't have one
          if [ "$(tail -c1 $f)" != '\n' ]
          then
            echo >> $f
          fi

          # Remove trailing whitespace if it exists
          if grep -q "[[:blank:]]$" $f
          then
            sed -i "" -e $'s/[ \t]*$//g' $f
          fi

          # Remove blank lines
          python3 ~/bin/remove_empty_lines_md.py $f $f 
      fi
  done

fi

unset IFS

git add -A
git commit --amend --no-edit --no-verify

请注意,末尾的--no-verify很重要(它跳过了钩子),否则会出现无限循环。
为了完整起见,这里是remove_empty_lines_md.py脚本:
def remove_empty_lines(text):
    r"""
    >>> text = "\n\n- test\n  - test2\n\n- test3\n\n\n  cointinue\n\n- test 4\n\n- test 5\n  - test 6\n    continue\n    continue\n\n\n\n    test7\n\n\n    tset8\n\n\n- test 9\n\n\n- final\n\n\n"
    >>> remove_empty_lines(text)
    '- test\n  - test2\n- test3\n\n\n  cointinue\n- test 4\n- test 5\n  - test 6\n    continue\n    continue\n\n\n\n    test7\n\n\n    tset8\n- test 9\n- final\n'
    """    
    new_lines = []
    buffer = []
    for i, line in enumerate(text.split('\n')):
        if not line.strip():
            buffer.append(line)
        else:
            if buffer:
                if line.lstrip() and line.lstrip()[0] != '-':
                    new_lines.extend(buffer)
                buffer = []
            new_lines.append(line)
    return '\n'.join(new_lines) + '\n'


if __name__ == '__main__':
    import doctest
    import sys
    doctest.testmod()
    filename_in, filename_out = sys.argv[1], sys.argv[2]
    with open(filename_in) as fin:
        text = fin.read()
    with open(filename_out, 'w') as fout:
        fout.write(remove_empty_lines(text))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接