GitHub 第一坑:换行符自动转换

首席安全砖家 发布于 2013/08/19 21:56
阅读 1K+
收藏 3





原文出处:  CSS魔法



源起

一直想在 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.autocrlffalse

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 一样,你可以通过命令行、图形界面、配置文件三种方法来修改这个选项。具体操作就不赘述了,大家自己举一反三吧。

最后

你可能还会问,如果我的编辑器一不小心把整个文件的换行符都转换成了另一种格式怎么办?还能预防吗?

这……我就真帮不了你了。所以还是建议大家在提交文件之前多留心文件状态:

如果发现变更行数过多,而且增减行数相同,就要警惕是不是出了意外状况。被图形界面惯坏的孩子往往缺乏耐心,对系统信息视而不见,看到按钮就点,容易让小疏忽酿成大事故。所以高手们青睐命令行,并不是没有道理的。

好了,小伙伴们,今天的《踩坑历险记》就到这儿,我们下集再见!祝大家编码愉快!

加载中
1
威廉亨利
威廉亨利
解决办法是用Linux或Mac
0
王振威
王振威

git权威指南中明确的介绍了有关换行符的问题,也有相应的解决方案

0
kuqoi
kuqoi
...被坑过一次。。
0
RayFung
RayFung
呵呵,换行符不一致不算什么,我还遇到过文件编码不一致呢,GBK、UTF-8混合在一起……
0
cisiqo
cisiqo

我也被这个坑过,这个问题我查了2天。在日历mac ics文件中换行是

<U+2028>,在安卓手机中是=0D=0A。

0
中山野鬼
中山野鬼

楼主我不是喷你啊。你自己想想 git是谁开发的,给谁用的。哈。用git的,在window下开发,这个说出去,会被别人笑话的,如同代码中间存在中文一样。这怎么个开源。法国人开源出来的代码一堆法文,韩国人开源出来的代码,一堆“大棒”文。。。于是乎,就有你这些问题了。包括gbk ,utf-8等等转码的问题。

你这个等于没事找事。

反过来,公司内部使用git 也是没事找事。因为git适合开源开发,并不适合公司团队化管理,他们本身在思维上就是矛盾的。。

中山野鬼
中山野鬼
回复 @我是潮汐 : 跨平台多了,或许你会知道,中文字符和英文字符是有差异的,这里不是拿中文和英文做对比。
我是潮汐
我是潮汐
我赞成你说的git不适合公司内部使用.. 但是对你说windows下使用git如同代码中出现中文的观点持保留意见..我觉得这样没什么
中山野鬼
中山野鬼
回复 @晓骏 : 无论几点睡,都睡到自然醒。这样第二天就不困了。哈。
首席安全砖家
首席安全砖家
回复 @中山野鬼 : 鬼哥 每天睡多少小时, 怎么高效睡眠,保证第二天不困
中山野鬼
中山野鬼
回复 @晓骏 : 那我就喷错了。实话,谁要在我管的团队的代码里放中文,我立刻踢出去。。。至少表明英文已经差的不能阅读资料了。
下一页
0
泡不烂的凉粉
泡不烂的凉粉

我总觉得写文章的思想是好的. 可论点和结论有点扯淡. 当别人给你留有选择机会的时候.  你会抱怨选择是多余的. 当别人不给你选择机会的时候, 又抱怨太死板.

我严重怀疑从windows 入门git的用户,同时也失去了更多理解程序的机会.

所有手册中都用大篇幅来说明换行符对代码的管理. 同时也有很多文章以及建议做git的配置, 要关掉自动转换的. 并且连基本的使用手册都没读完的使用者,还在抱怨系统设计的太烂.我觉得很可笑.  我能用浮躁来形容这些抱怨吗? 读完手册. 还有问题再提问也不迟.

ddatsh
ddatsh
+1
0
kakad
kakad

又见windows

0
mahengyang
mahengyang
被坑过的路过,如果是shell脚本,直接就无法运行,报换行符不对。ps:我是用scp复制到服务器上测试用的
0
赵云30
赵云30

引用来自“正想着改什么名好”的答案

我总觉得写文章的思想是好的. 可论点和结论有点扯淡. 当别人给你留有选择机会的时候.  你会抱怨选择是多余的. 当别人不给你选择机会的时候, 又抱怨太死板.

我严重怀疑从windows 入门git的用户,同时也失去了更多理解程序的机会.

所有手册中都用大篇幅来说明换行符对代码的管理. 同时也有很多文章以及建议做git的配置, 要关掉自动转换的. 并且连基本的使用手册都没读完的使用者,还在抱怨系统设计的太烂.我觉得很可笑.  我能用浮躁来形容这些抱怨吗? 读完手册. 还有问题再提问也不迟.

读完手册.  读完手册, 娃都生了, 地球都毁灭了....

我发现网上很多人都喜欢用这个来教训别人. 请问你自己读完手册了吗? 你读了几份手册?都有什么收获?

别在到处教人读手册, 教人用Google, 别人比你想象的聪明,  他们会读手册会Google只是可能读不懂查不到资料,  如果你们有能耐,读得懂查得到,那很好.  如果你们慷慨一点,  分享你的经验, 结论, 我们感谢你.  如果你小气, 也没关系, 不过请闭上你们只会吐垃圾的嘴,  别在到处叫人读手册, 叫人用Google.