转自https://www.cnblogs.com/zjoch/p/5400251.html
源起
一直想在 GitHub 上发布项目、参与项目,但 Git 这货比较难学啊。买了一本《Git 权威指南》,翻了几页,妈呀,那叫一个复杂,又是 Cygwin 又是命令行的,吓得我不敢学了。
终于某天发现 GitHub 还有一个 Windows 客户端,试了一下还挺好用。不需要掌握太多的 Git 原理和命令,也可以在 GitHub 上麻溜建项目了,甚是欢喜。可是好景不长,第一次参与开源项目就出洋相了。
经过
小心翼翼地 Fork 了朴灵大大 (@JacksonTian) 的 EventProxy 项目,本地改好提交,同步到服务器,怀着激动的心情发出 Pull Request……这时发现问题了。我发现 diff 图表显示的更新并不仅是我修改的那几行,而是整个文件都显示为已修改。(下图为示意图)
这看起来很奇怪啊,于是赶紧撤回 Pull Request,自己闷头找原因。
初步定位是文件的换行符问题,因为我发现本地的文件是 Windows 换行符,但很显然大家现在做项目都是用 UNIX 换行符啊。这是一大疑点,于是在反复对比 Web 端和本地的各个文件、各个版本之后,基本定位到了问题所在。
背景
在各操作系统下,文本文件所使用的换行符是不一样的。UNIX/Linux 使用的是 0x0A
(LF),早期的 Mac OS 使用的是0x0D
(CR),后来的 OS X 在更换内核后与 UNIX 保持一致了。但 DOS/Windows 一直使用 0x0D0A
(CRLF)作为换行符。(不知道 Bill Gates 是怎么想的,双向兼容?)
这种不统一确实对跨平台的文件交换带来麻烦。虽然靠谱的文本编辑器和 IDE 都支持这几种换行符,但文件在保存时总要有一个固定的标准啊,比如跨平台协作的项目源码,到底保存为哪种风格的换行符呢?
Git 作为一个源码版本控制系统,以一种(我看起来)有点越俎代庖、自作聪明的态度,对这个问题提供了一个“解决方案”。
Git 由大名鼎鼎的 Linus 开发,最初只可运行于 *nix 系统,因此推荐只将 UNIX 风格的换行符保存入库。但它也考虑到跨平台协作的场景,并且提供了一个“换行符自动转换”功能。
这个功能默认处于“自动模式”,当你在签出文件时,它试图将 UNIX 换行符(LF)替换为 Windows 的换行符(CRLF);当你在提交文件时,它又试图将 CRLF 替换为 LF。
(看明白了吗?一个版本控制系统会在你不知不觉的情况下修改你的文件。这 TM 简直酷毙了,对吧?)
缺陷
Git 的“换行符自动转换”功能听起来似乎很智能、很贴心,因为它试图一方面保持仓库内文件的一致性(UNIX 风格),一方面又保证本地文件的兼容性(Windows 风格)。但遗憾的是,这个功能是有 bug 的,而且在短期内都不太可能会修正。
问题具体表现在,如果你手头的这个文件是一个包含中文字符的 UTF-8 文件,那么这个“换行符自动转换”功能 在提交时是不工作的(但签出时的转换处理没有问题)。我猜测可能这个功能模块在处理中文字符 + CRLF 这对组合时直接崩溃返回了。
这可能还不是唯一的触发场景(毕竟我没有太多精力陪它玩),但光是这一个坑就已经足够了。
踩坑
这是一个相当大的坑,Windows 下的中文开发者几乎都会中招。举个例子,你在 Windows 下用默认状态的 Git 签出一个文件,写了一行中文注释(或者这个文件本来就包含中文),然后存盘提交……不经意间,你的文件就被毁掉了。
因为你提交到仓库的文件已经完全变成了 Windows 风格(签出时把 UNIX 风格转成了 Windows 风格但提交时并没有转换),每一行都有修改(参见本文开头的示意图),而这个修改又不可见(大多数 diff 工具很难清楚地显示出换行符),这最终导致谁也看不出你这次提交到底修改了什么。
这还没完。如果其他小伙伴发现了这个问题、又好心地把换行符改了回来,然后你又再次重演上面的悲剧,那么这个文件的编辑历史基本上就成为一个谜团了。
由于老外几乎不可能踩到这个坑,使得这个 bug 一直隐秘地存在着。但在网上随便搜一下,就会发现受害者绝对不止我一个,比如 这位大哥的遭遇 就要比我惨痛得多。
防范
首先,不要着急去整 Git,先整好自己。你的团队需要确定一个统一的换行符标准(推荐使用 UNIX 风格)。然后,团队的成员们需要分头做好准备工作——配置好自己的代码编辑器和 IDE,达到这两项要求:
- 在新建文件时默认使用团队统一的换行符标准
- 在打开文件时保持现有换行符格式不变(不要做自动转换)
这样一方面可以最大程度保证项目代码的规范一致,另一方面,即使现有代码中遗留了一些不规范的情况,也不会因为反复转换而导致混乱。(当然,作为一个强迫症患者,我还是祝愿所有的项目从一开始就步入严谨有序的轨道。)
接下来,我们就可以开始调教 Git 了。我的建议是, 完全关掉这个自作聪明的“换行符自动转换”功能。关闭之后,Git 就不会对你的换行符做任何手脚了,你可以完全自主地、可预期地控制自己的换行符风格。
下面主要针对不同的 Git 客户端,分别介绍一下操作方法。
Git for Windows
这货由 Git 官方出品,在安装时就会向你兜售“换行符自动转换”功能,估计大多数人在看完华丽丽的功能介绍之后会毫不犹豫地选择第一项(自动转换)。请千万抵挡住诱惑,选择最后一项(不作任何手脚)。
如果你已经做出了错误的选择,也不需要重新安装,可以直接使用命令行来修改设置。很简单,直接打开这货自带的命令行工具 Git Bash,输入以下命令,再敲回车即可:
1
|
git config --global core.autocrlf false |
TortoiseGit
很多从 TortoiseSVN 走过来的同学很可能会选用 TortoiseGit 作为主力客户端,那么也需要配置一下。在 Windows 资源管理器窗口中点击右键,选择“TortoiseGit → Settings → Git”,做如下设置。
(由于 TortoiseGit 实际上是基于 Git for Windows 的一个 GUI 外壳,你在上一节所做的设置会影响到上图这些选项的状态,它们可能直接就是你所需要的样子了。)
GitHub 的 Windows 客户端
它是今天的第二被告。这货很容易上手,很适合小白,我主要用它来一键克隆项目到本地。可能正是为了维护简洁易用的亲切形象,这货并没有像 TortoiseGit 那样提供丰富的选项(对“换行符自动转换”这样的细节功能完全讳莫如深啊,我这样的小白死了都不知道怎么死的……)。因此,我们需要手动修改一下它的配置。
GitHub 的 Windows 客户端实际上也是一个壳,它自带了一个便携版的 Git for Windows。这个便携版和你自己安装的 Git for Windows 是相互独立的,不过它们都会使用同一个配置文件(实际上就是当前用户主目录下的 .gitconfig
文件)。
所以如果你已经配置好了自己安装的 Git for Windows,那就不用操心什么了。但如果你的机器上只装过 GitHub 的 Windows 客户端,那么最简单的配置方法就是手工修改配置文件了。
修改 Git 的全局配置文件
进入当前用户的主目录(通常 XP 的用户目录是 C:\\Documents and Settings\\yourname
,在 Vista 和 Win7 下是C:\\Users\\yourname
),用你最顺手的文本编辑器打开 .gitconfig
文件。
在 [core]
区段找到 autocrlf
,将它的值改为 false
。如果没找到,就在 [core]
区段中新增一行:(最终效果见图)
1
|
autocrlf = false |
事实上上面介绍的所有命令行或图形界面的配置方法,最终效果都是一样的,因为本质上都是在修改这个配置文件。
还有
关掉了 Git 的“换行符自动转换”功能就万事大吉了吗?失去了它的“保护”,你心里会有点不踏实。你可能会问:如果我不小心在文件中混入了几个 Windows 回车该怎么办?这种意外可以防范吗?
事实上 Git 还真能帮你制止这种失误。它提供了一个换行符检查功能(core.safecrlf
),可以在提交时检查文件是否混用了不同风格的换行符。这个功能的选项如下:
false
- 不做任何检查warn
- 在提交时检查并警告true
- 在提交时检查,如果发现混用则拒绝提交
我建议使用最严格的 true
选项。
和 core.autocrlf
一样,你可以通过命令行、图形界面、配置文件三种方法来修改这个选项。具体操作就不赘述了,大家自己举一反三吧。
最后
你可能还会问,如果我的编辑器一不小心把整个文件的换行符都转换成了另一种格式怎么办?还能预防吗?
这……我就真帮不了你了。所以还是建议大家在提交文件之前多留心文件状态:
如果发现变更行数过多,而且增减行数相同,就要警惕是不是出了意外状况。被图形界面惯坏的孩子往往缺乏耐心,对系统信息视而不见,看到按钮就点,容易让小疏忽酿成大事故。所以高手们青睐命令行,并不是没有道理的。