试图用git filter-branch修复行尾,但是没有运气


270

我一直被git的Windows / Linux行尾问题所困扰。似乎通过GitHub,MSysGit和其他来源,最好的解决方案是将本地存储库设置为使用linux样式的行尾,但设置core.autocrlftrue。不幸的是,我还没有做得足够早,所以现在每次我进行更改时,行尾都令人讨厌。

我以为我在这里找到了答案但我无法为我工作。我的Linux命令行知识充其量是有限的,因此我什至不确定“ xargs fromdos”行在其脚本中的作用。我不断收到有关不存在此类文件或目录的消息,当我设法将其指向现有目录时,它告诉我我没有权限。

我已经在Windows上通过Mac OS X终端在MSysGit上进行了尝试。


我什至不能足够赞这个线程。+1 ++就此问题提供了最佳答案。
sjas 2012年

同意查尔斯。但是,以我为例(使用Mac OS X 10.8)> git config core.autocrlf false起作用了,而不是> git config core.autocrlf输入
user1045085 2013年

Answers:


187

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状态,请在运行git add -u之前取消设置其text属性。

manual.pdf -text

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

weirdchars.txt text

这利用了--renormalize2018年1月发布的git v2.16.0中添加的新标志。对于较旧版本的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 2012年

1
强制git重建索引,在此期间它将扫描每个文件以猜测其二进制文件。rm删除旧索引,reset生成新索引。
Russ Egan

16
谢谢,这对我有用。运行后一个有用的命令git status就是运行git diff --ignore-space-at-eol,以确保您提交的唯一更改是行尾。
zelanix 2014年

1
注意:此解决方案与“旧”解决方案之间的唯一“实际”区别是存在.gitattributes(具有适当的内容)。没有这个,git reset将不会检测到任何修改,因此是无用的。
罗布(Rob)

3
gitattributes页面上的说明已更新,以利用--renormalizegit v2.16.0中添加的标志的优势,该标志于2018年1月发布。该--renormalize标志将重新处理每个跟踪文件的行结尾的过程合并为一个命令:git add --renormalize .
迈克·希尔

389

解决此问题的最简单方法是进行一次提交,以修复所有行尾。假设您没有任何修改的文件,则可以按以下步骤进行操作。

# 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
PS:我向github.com上的人推荐了您的修复程序,他们更新了帮助指南以使用您的解决方案(以前它只是建议重新克隆并进行硬重置,似乎并没有获取所有文件。) help.github。 com / dealing-with-lineendings
Brian Donahue 2009年

31
谢谢...这是一个很好的解决方案。在GitHub上找到它。
PHLAK

4
您可能还需要检出config.safecrlf以确保您未更改非文本文件(例如二进制文件)中的crlfs。在docs kernel.org/pub/software/scm/git/docs/git-config.html中进行检查。
vrish88

4
@ vrish88:但是,如果您处在这种情况下,则可能会遇到混合行尾和core.safecrlf可能实际上阻止了您执行需要做的事情。不使用safecrlf可能会更容易。git经常不会错误地检测二进制文件,如果这样做,您可以使用.gitattribute手动将其标记为二进制文件,并从上一次提交中恢复正确的版本。
CB Bailey 2010年

26
在下面的Russ Egan的答案中推荐的较新解决方案更简单,并且不涉及删除所有源代码之类的可怕事情,因此,即使此旧解决方案的投票数是原来的10倍,我还是真的建议人们使用它!
Porculus

11

我处理行尾的过程如下(在许多存储库上经过了实战测试):

创建新的仓库时:

  • .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会将许多文件标记为已修改。

解决的方法是先进行伪commit(git add -A . && git commit -m 'fake commit'),然后执行git rebase master。重新设置基准之后,伪造的提交将消失。


1
在我读完您的文章之前,我以为我会发疯,因为我也不得不多次运行指定的命令序列。巫毒!;)
肖恩·福塞特

在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 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
AlmirSarajčić17年


3

“ | xargs fromdos”从标准输入中读取(文件会find找到),并将其用作命令的参数,该命令fromdos将转换行尾。(在那些环境中fromdos是标准的吗?我习惯于dos2unix)。请注意,您可以避免使用xargs(如果您有足够的文件以至于参数列表对于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表示感谢


-2

如果没有其他答案适合您,请按照下列步骤操作:

  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

那么您的本地人应该现在就干净了。


4
真?删除.gitattributes文件是解决行尾问题的方法吗?
Aleksandr M

有请@AleksandrM报告的评价
Mr_and_Mrs_D
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.