其他

Git中 Windows与Linux行尾换行符

前言


最近在使用 Git 提交代码的时候,老是碰到一段看起来 “没有任何改动” 的代码,被 diff 检测出异常,很是苦恼,特别是项目紧急的时候,不敢用 VSCode 编辑了,只能用 vim 进行少量代码的修改、上库。

另一件事是 shell 脚本文件行尾换行符异常,导致在 linux 环境下无法正常运行,总是提示什么语法错误。

换了台电脑,git拉取代码之后传到linux设备上,然后git status发现所有文件都被修改了,仔细一看是每行最后都多了一个__^M__。

Git 的多平台换行符问题(LF or CRLF)

Windows下文本文件所使用的换行符是CRLF,而Linux/UNIX/macOS用的是LF。

如果使用 VSCode ,可以通过右下角确认当前文件行尾换行符格式

换行符


关于“回车”(carriage return)和“换行”(line feed)这两个概念的来历和区别。在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。

于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。

后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。Unix系统里,每行结尾只有“<换行>”,即“\n”;Windows系统里面,每行结尾是“ <回车><换行>”,即“\r\n”;Mac系统里,每行结尾是“<回车>”。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。

Windows创建的文件是 \n\r结束的, 而Linux等类Unix操作系统是\n结束的。所以Unix的文本到Windows会出现换行丢失(ultraedit这类软件可以正确识别); 而反过来就会出现^M的符号了

Windows等操作系统用的文本换行符和Unix/Linux操作系统用的不同,Windows系统下输入的换行符在Unix/Linux下不会显示为“换行”,而是显示为 ^M 这个符号(这是Linux等系统下规定的特殊标记,占一个字符大小,不是 ^ 和 M 的组合,打印不出来的)。Linux下很多文本编辑器(命令行)会在显示这个标记之后,补上一个自己的换行符,以避免内容混乱(只是用于显示,补充的换行符不会写入文件,有专门的命令将Windows换行符替换为Linux换行符)。 Unix/Linux系统下的换行符在Windows系统的文本编辑器中会被忽略,整个文本会乱成一团。

找一份linux上编辑的文档和一份windows上编辑的文档,用notepad++查看,设置显示所有符号,一目了然。

因此经常在两个平台间传输文件经常会在换行符上出问题,网络上有很多转换换行符的方法,随便百度就有,看你用什么编辑器。

这两个符号的差异,会导致文件格式不同。

比如会导致 CRLF 的文件,想要在 Linux 系统执行,就会出问题。

举例来说,我在 Windows 下新建一个 Shell 脚本文件,行尾换行符是 CRLF 格式的,那么此时放到 Linux 系统执行,是会报错,无法执行的。

Git 换行符自动转换


这里先指定两个非官方的概念,方便后面解释与描述:(重要,否则后面看不懂)

  1. 标准化 指在提交代码到git数据库(本地库) 中将文本文件中的换行符CRLF转为LF的过程
  2. 转换 指在检出Git数据库代码过程中将文本文件中的换行符LF转换为CRLF的过程

core.autocrlf & core.safecrlf

Git 提供了一个名为 core.autocrlf 的配置,可以自动完成标准化与转换。它的设置方式如下:

git config --global core.autocrlf  [true | input | false]  # 全局设置
git config --local core.autocrlf  [true | input | false] # 针对本项目设置
  • true 自动完成标准化与转换
  • input 只做标准化操作,不做转换操作
  • false 提交与检出的代码都保持文件原有的换行符不变
CRLF 与 LF 混合的文本文件不受此配置控制。
Git 安装后默认为 false

所以,一种规范换行符的方式是这样的

使用 Windows 系统的开发者设置:

git config --global core.aurocrlf true

# Windows 打开 autocrlf
# pull 时转换为 CRLF ,push 时转换为 LF

使用 Linux/MacOS 的开发者设置:

git config --global core.autocrlf input

由于没有一个绝对有效的算法来判断一个文件是否为文本,所以Git 提供了一项禁止/警告不可逆转换的配置来防止错误的标准化与转换。它主要是影响到多种换行符混合的文件,我们可以手动将其转换为同一种换行符:

git config --global core.safecrlf [true | false | warn]
  • true 禁止提交混合换行符的文本文件(git add 的时候会被拦截,提示异常)
  • warn 提交混合换行符的文本文件的时候发出警告,但是不会阻止 git add 操作
  • false 不禁止提交混合换行符的文本文件(默认配置)

可以运行 git config –list 查看当前配置

若运行git config –list,其中没有出现autocrlf设置,则默认为false,即不对git项目的行尾符做任何处理。

gitattributes 文件

core.autocrlf 的配置依赖于每一位参与项目的开发机器上的配置,这很难确保每个人都能正确配置。于是在规范项目中的换行符方面,还有一套添加配置文件的方案。在项目的根目录下可以添加一个.gitattributes 文件。它的优先级高于core.autocrlf的设置,可以覆盖core.autocrlf的。它类似于 .gitignore 文件,随提交修改生效,一个项目中可以维持一份相同的配置。所以,它能够避免每个开发人员配置不同的问题。

.gitattributes文件的功能不只有配置换行符,所以它的配置相对复杂一下。详细的说明文档可以参考 地址。这里只针对换行符的配置做一下简单的介绍:

每行基本形式:

filter attr1 attr2 ....

filter 代表匹配文件的通配符,在它后面跟着相应的属性,用空格间隔。

filter 的选项比较简单,常见的:

* 匹配所有文件
*.txt  匹配文件名以txt结尾的文件

attr的选择比较多,其中与换行符相关的属性只有几条:

这里给出一个简单的例子来说明一下:

*         text=auto
# These files are text and should be normalized (convert crlf => lf)
*.cs      text
*.xaml    text
*.csproj  text
*.sln     text
*.tt      text
*.ps1     text
*.cmd     text
*.msbuild text
*.md      text

# Images should be treated as binary
# (binary is a macro for -text -diff)
*.png     binary
*.jepg    binary

*.sdf     binary

除了下面匹配到的文件,剩下的依赖Git 决定是否参与标准化与转换。上面一段是参与标准化与转换的文件;下面一段是不参与标准化与转换的文件;

其实,在文件里只有下面这行配置的时候,就相当于根据操作系统自动填入 core.autocrlf 的设置。

* text=auto