重写 git 历史记录以将所有 CRLF 替换为 LF?

重写 git 历史记录以将所有 CRLF 替换为 LF?

我要将一个私有 Git 存储库从 win32 机器转移到 Ubuntu。虽然我可以进行最终的 dos2unix 提交,但我想重写整个历史记录,以便某些 Git GUI 可以正确显示日志/差异。例如,吉特格将为每个 CR/LF 插入空行。

答案1

您可以使用git filter-branch为此,请使用--tree-filter选项并指定--all分支。

下面是一个例子(从一个空目录中以 Unix 类型的文本文件开始:

准备:

$ hexdump -C testfile 
00000000  61 0d 0a 62 0d 0a 63 0d  0a                       |a..b..c..|
00000009

$ git init
Initialized empty Git repository in /home/seigneur/tmp/a/.git/

$ git add testfile && git commit -m "dos file checked in"
[master (root-commit) df4970f] dos file checked in
 1 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 testfile

命令:

$ git filter-branch --tree-filter 'git ls-files -z | xargs -0 dos2unix' -- --all

输出:

Rewrite df4970f63e3196216d5986463f239e51eebb4014 (1/1)dos2unix: converting file testfile to Unix format ...

Ref 'refs/heads/master' was rewritten

$ hexdump -C testfile 
00000000  61 0a 62 0a 63 0a                                 |a.b.c.|
00000006

强烈建议事先进行完整备份。从您的 Linux 机器运行该程序(除非您在 Windows 环境中设置了良好的 shell)可能更容易。

编辑:第一次转换时被逆转了。

答案2

Mat's回答一针见血地指出了这个问题。不幸的是,在 Ubuntu Linux 上,从版本 10.04(Lucid Lynx)开始,dos2unix/unix2dos 命令不再可用,已被 fromdos/todos 取代。此外,这两组转换命令对二进制文件的存在都有不同程度的忽略,因此如果您的存储库包含图像、字体等,它们将被此过程破坏。

我找到了一种解决二进制文件损坏问题的方法,即使用 Linux 的“file”命令正确识别和处理文本文件,如下所示。下面的命令使用 --tag-name-filter 选项,通过将现有标签移动到新修改的提交来保留它们。此外,它还使用 --force 标志来确保如果您之前在存储库上运行过 tree-filter,该命令仍能正常工作。

git filter-branch --force --tree-filter 'git ls-files | xargs file | sed -n -e "/.*: .*text.*/s/\(.*\): .*/\1/p" | xargs fromdos' --tag-name-filter cat -- --all

答案3

并且无需任何附加工具(例如“fromdos”、“dos2unix”等):

git filter-branch --force --tree-filter 'git ls-files | xargs file | sed -n -e "/.*: .*text.*/s/\(.*\): .*/\1/p" | xargs -0 sed -i"" -e "s/"$(printf "\015")"$//"' --tag-name-filter cat -- --all

跨平台(OS X、FreeBSD、Linux)有用的模拟‘fromdos’、‘dos2unix’:

sed -i'' -e 's/'"$(printf '\015')"'$//'

也许有用的‘unix2dos’:

sed -i '' -e 's|$|'"`printf '\015'`"'|' file.name

如果你非常清楚自己在做什么,你可以使用这个简单的内联命令从当前目录“。”中的所有文件中删除“/r”:

find . -type f -exec sed -i'' -e 's/'"$(printf '\015')"'$//' {} \;

答案4

git filter-branch有几个问题,主要是在性能方面,因为已被弃用git filter-repo,一个由几个python脚本组成的单独项目。

要转换存储库中的所有行尾,请使用lint-history

lint-history dos2unix # CRLF => LF
lint-history unix2dos # LF => CRLF

您还可以执行其他 linting 任务,例如在文件末尾添加换行符

lint-history sed -i '$a\'

这假定lint-history脚本在您的路径中,并且已git filter-repo使用符号链接等正确安装。

如果您没有完整filter-repo安装lint-history但只有git-filter-repo您可以运行:

git filter-repo --blob-callback '
  if not b"\0" in blob.data[0:8192]:
    filename = ".git/info/tmpfile"
    with open(filename, "wb") as f:
      f.write(blob.data)
    subprocess.check_call(["sed", "-i", "$a\\", filename])
    with open(filename, "rb") as f:
      blob.data = f.read()
    os.remove(filename)
  '

相关内容