尝试使用git filter-branch修复行尾问题,但没有成功。

287

我被git的Windows/Linux换行符问题所困扰。通过GitHub、MSysGit和其他来源,最好的解决方案似乎是将本地存储库设置为使用Linux风格的换行符,但设置core.autocrlftrue。不幸的是,我没有在足够早的时候这样做,所以现在每次拉取更改时,换行符都会出错。

我曾经在这里找到一个答案,但我无法让它对我起作用。我的Linux命令行知识非常有限,因此我甚至不知道他脚本中的“xargs fromdos”命令是做什么的。我一直收到关于不存在文件或目录的消息,当我设法指向一个现有的目录时,它告诉我我没有权限。

我已经尝试过在Windows上使用MSysGit以及通过Mac OS X终端运行该脚本。


我无法给这个帖子点赞,哪怕是接近的数量。+1++ 因为它提供了关于此事的最佳答案。 - sjas
同意Charles的说法。然而在我的情况中(使用Mac OS X 10.8),命令 git config core.autocrlf false 生效了,而不是 git config core.autocrlf input。 - Zhiyong
9个回答

407

最简单的方法是创建一个提交来修复所有行结尾。假设您没有任何修改过的文件,则可以按照以下步骤进行操作。

# From the root of your repository remove everything from the index
git rm --cached -r .

# Change the autocrlf setting of the repository (you may want 
#  to use true on windows):
git config core.autocrlf input

# Re-add all the deleted files to the index
# (You should get lots of messages like:
#   warning: CRLF will be replaced by LF in <file>.)
git diff --cached --name-only -z | xargs -0 git add

# Commit
git commit -m "Fixed crlf issue"

# If you're doing this on a Unix/Mac OSX clone then optionally remove
# the working tree and re-check everything out with the correct line endings.
git ls-files -z | xargs -0 rm
git checkout .

7
附言:我向github.com的开发人员推荐了您的修复方案,他们更新了帮助指南以使用您的解决方案(之前只建议进行全新克隆和硬重置,但似乎无法获取所有文件)。http://help.github.com/dealing-with-lineendings/ - Brian Donahue
33
谢谢...这是一个很好的修复方法。我在GitHub上找到了它。 - PHLAK
4
您可能还想查看config.safecrlf以确保您未更改非文本文件(例如二进制文件)中的换行符。在文档http://www.kernel.org/pub/software/scm/git/docs/git-config.html中查看它。 - vrish88
4
如果你遇到这种情况,很可能是由于混合的行尾符引起的,并且核心设置“safecrlf”可能会妨碍你完成需要做的事情。放弃使用“safecrlf”可能更容易些。Git在检测二进制文件方面通常不会出错,如果出现问题,你可以通过.gitattribute手动标记文件为二进制文件,并从上一个提交版本中恢复正确的版本。 - CB Bailey
28
Russ Egan下面的答案中推荐的新解决方案更简单,不涉及像“删除所有源代码”这样可怕的事情,因此我真的建议人们使用它,尽管这个旧解决方案有10倍的赞数! - Porculus
显示剩余7条评论

203

gitattributes的Git文档中,现在记录了另一种方法来“修复”或规范化项目中的所有行结尾。以下是要点:

$ echo "* text=auto" >.gitattributes
$ git add --renormalize .
$ git status        # Show files that will be normalized
$ git commit -m "Introduce end-of-line normalization"

如果在git status中出现不应该被规范化的任何文件,需要在运行git add -u之前取消它们的文本属性。

manual.pdf -text

相反地,git未检测到的文本文件可以手动启用规范化。

weirdchars.txt text

这利用了在git v2.16.0中新增的--renormalize标志,于2018年1月发布。
但是如果您有“未暂存删除的文件”,则可能会失败,因此请首先将它们暂存,例如:

git ls-files -z --deleted | xargs -0 git add

对于较老版本的git,有几个额外的步骤:

$ echo "* text=auto" >>.gitattributes
$ rm .git/index     # Remove the index to force git to
$ git reset         # re-scan the working directory
$ git status        # Show files that will be normalized
$ git add -u
$ git add .gitattributes
$ git commit -m "Introduce end-of-line normalization"

1
请问一下,git reset 的目的是什么? - crdx
2
强制Git重新构建索引,期间扫描每个文件并猜测其是否为二进制文件。rm删除旧索引,reset构建新索引。 - Russ Egan
17
谢谢,这对我有效。在运行git status后,一个有用的命令是运行git diff --ignore-space-at-eol,以确保您提交的只是行尾的更改。 - zelanix
1
注意:这种方法和“旧”解决方案的唯一“真正”区别在于存在.gitattributes(具有适当的内容)。如果没有它,git reset将检测不到任何修改,因此是无用的。 - Rob
3
gitattributes 页面上的说明已经更新,以利用在 2018 年 1 月发布的 git v2.16.0 中添加的 --renormalize 标志。--renormalize 标志将每个跟踪文件的重新处理行尾的过程合并为一个命令:git add --renormalize . - Mike Hill
显示剩余9条评论

13

我处理行结尾的流程如下(在许多代码库上进行了实战测试):

创建新代码库时:

  • 与其他典型文件一起将.gitattributes放在第一个提交中,例如.gitignoreREADME.md

处理现有代码库时:

  • 相应地创建/修改.gitattributes
  • git commit -a -m“Modified gitattributes”
  • git rm --cached -r . && git reset --hard && git commit -a -m'Normalize CRLF' -n"
    • -n--no-verify是为了跳过提交前钩子)
    • 我经常需要进行此操作,因此将其定义为别名alias fixCRLF="..."
  • 重复上一个命令
    • 是的,这是巫术,但通常我必须运行两次该命令,第一次它会规范化一些文件,第二次会规范化更多文件。通常最好重复直到不再创建新提交为止:)
  • 来回切换旧分支(规范化之前那个)和新分支几次。切换分支后,有时git会找到更多需要重命名的文件!

.gitattributes中,我明确声明所有文本文件具有LF EOL,因为通常Windows工具与LF兼容,而非Windows工具与CRLF不兼容(即使许多nodejs命令行工具也假定LF,因此可能会更改您文件中的EOL)。

.gitattributes的内容

我的.gitattributes通常如下所示:

*.html eol=lf
*.js   eol=lf
*.json eol=lf
*.less eol=lf
*.md   eol=lf
*.svg  eol=lf
*.xml  eol=lf

要找出Git在当前存储库中跟踪的独特扩展名,请查看此处

规范化后的问题

完成此操作后,仍有一个常见问题。

假设您的master已经是最新版本并已被规范化,然后您切换到outdated-branch。通常,在检出该分支后不久,Git会将许多文件标记为已修改。

解决方法是进行虚拟提交(git add -A . && git commit -m 'fake commit'),然后执行git rebase master。重定基后,虚拟提交应该会消失。


1
我以为我疯了,直到我看了你的帖子,因为我也不得不多次运行指定的命令序列。像巫术一样!;) - Sean Fausett
使用 git 版本 2.7.0.windows.1,我使用了以下命令:git rm --cached -r . && git reset --hard && git add . && git commit -m "Normalize EOL" -n - Sean Fausett

4

以下是我使用 git filter-branch 修复整个历史记录中所有行结束符的方法。输入 ^M 字符需要使用 CTRL-V + CTRL-M。我使用了 dos2unix 来转换文件,因为它会自动跳过二进制文件。

$ git filter-branch --tree-filter 'grep -IUrl "^M" | xargs -I {} dos2unix "{}"'

http://superuser.com/questions/293941/rewrite-git-history-to-replace-all-crlf-to-lf https://gist.github.com/richfitz/72ac6cd41c2b531a89f1 - rofrol

4
git status --short|grep "^ *M"|awk '{print $2}'|xargs fromdos

解释:

  • git status --short

    这将显示git所知道的和不知道的每行内容。没有受到git控制的文件在行首标记为'?'。修改过的文件在行首标记为 M。

  • grep "^ *M"

    这将仅筛选出被修改的文件。

  • awk '{print $2}'

    这将仅显示文件名,不包含任何标记。

  • xargs fromdos

    这将从前一个命令中获取文件名,并通过实用程序 'fromdos' 将它们转换为换行符。


这太棒了。谢谢。对于任何使用Homebrew寻找解决方案的人,请使用“dos2unix”而不是“fromdos”。 - Almir Sarajčić

3
"

\"| xargs fromdos\"从标准输入流中读取(由find找到的文件),并将其用作命令fromdos的参数,该命令将行结尾转换为Unix格式。(在这些环境中是否标准使用fromdos?我习惯于使用dos2unix)。请注意,您可以避免使用xargs(特别是当您有足够多的文件时,参数列表过长):

"
find <path, tests...> -exec fromdos '{}' \;

或者

find <path, tests...> | while read file; do fromdos $file; done

我不是完全确定你的错误信息。我已经成功测试了这种方法。哪个程序产生了每个错误?哪些文件/目录您没有权限访问?但是,以下是猜测您可能遇到的问题的尝试:
如果使用相对路径,则很容易出现“找不到文件”的错误 - 使用绝对路径。同样,如果您没有使脚本可执行(chmod +x),则可能会出现权限错误。
添加注释,我将尝试帮助您解决问题!

我看到另一个使用dos2unix的例子,一开始以为是将文件复制到一个名为“that”的文件夹中,但现在我明白了。现在看起来很明显。谢谢你的帮助! - Brian Donahue

1

好的...在cygwin下我们没有轻松获取fromdos,如果您的路径中有任何空格(我们有),那么awk substeb会在您的面前爆炸,所以我不得不以稍微不同的方式处理:

git status --short | grep "^ *M" | sed 's/^ *M//' | xargs -n 1 dos2unix

赞扬 @lloyd 提供了大部分的解决方案。

0

我在我的某个代码库中也遇到了同样的问题。如果你同时使用Windows和Linux系统对同一代码库进行拉取和推送,请尝试以下操作:

首先,针对Windows系统,将git配置设置如下:

git config --global core.autocrlf true

当写入对象数据库时,这将确保将CRLF转换为LF,然后在写入工作目录时再将LF替换为CRLF。因此,您的存储库将只有一种类型的行尾,并且在本地,您将在Windows系统上具有Windows行尾,从而使您的存储库更加安全。

对于Linux/MAC,请将git配置设置如下:

git config --global core.autocrlf input

这将确保在写入对象数据库时将CRLF转换为LF,但不会反过来进行操作,保留LF以供Linux/MAC使用。

对于已经存在于您的Linux/MAC上的错误行尾,请使用dos2unix

对于MAC:

brew install dos2unix # Installs dos2unix Mac
find . -type f -exec dos2unix {} \; # recursively removes windows related stuff

对于Linux:

sudo apt-get install -y dos2unix # Installs dos2unix Linux
sudo find . -type f -exec dos2unix {} \; # recursively removes windows related stuff

希望这能解决你的问题。

-3

如果其他答案都不适用于您,请按照以下步骤操作:

  1. 如果您使用的是Windows,请执行git config --global core.autocrlf true; 如果您使用的是Unix,请执行git config core.autocrlf input
  2. 运行git rm --cached -r .
  3. 删除文件.gitattributes
  4. 运行git add -A
  5. 运行git reset --hard

然后,您的本地应该已经干净了。


5
зңҹзҡ„еҗ—пјҹеҲ йҷӨ.gitattributesж–Ү件жҳҜи§ЈеҶіиЎҢе°ҫй—®йўҳзҡ„ж–№жі•пјҹ - Aleksandr M
请回复@AleksandrM的评论。 - Mr_and_Mrs_D

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