yliu

时来天地皆同力,运去英雄不自由

.gitattributes 正确使用姿势


这篇文章的由来是因为项目上的一个神奇问题,之前觉得写重复的创建项目太繁琐就封装了一个创建模板文件 CLI ,当时心想自己真是一个上进的小伙子。

后来某天 xx 领导对我说有一个新项目交给我做,当时心想终于可以大展拳脚试试新的工具了,一顿操作后项目完成了,不过在推送 GitLab 时发现图片竟然无法预览。why?后面根据 git log 记录一个个回滚和但是这也无法复现 entire,后面又对项目文件屏蔽检查终于发现原来是 .gitattributes 文件填写问题。

之前.gitattributes 写法

* text eol=lf

那是不是直接删除 .gitattributes 文件就可以了呢?如果只考虑解决图片提交格式损坏当时是没问题。

.gitattributes 作用

不过回到标题,当时之所以在模板文件中添加 .gitattributes 主要是有两个目的:

  • 确保文件行尾序列符合一致,在 windows 下行尾序列为 CRLF 而在 mac 和 linux 则是 LF。行尾序列不同在某些 Eslint 规则下会显示报错,这是为了规范你的代码。

Code File With Prettier Linting Errors - .gitattributes

  • 避免 git 提交记录莫名差异,例如从仓库下拉取了一个项目,更改了一个某一个文件的内容发现不对又撤销了,但是因为系统不同行尾序列已经发生了变更,git 会显示这个文件的差异。

image-20220410152753128.png

以上操作是假设仓库的文件是 LF 格式,且你是 windows 电脑下操作

如何编写.gitattributes 文件

按照开头我最初的写法肯定是错误的

* text eol=lf

这里会把代码的所有文件都 LF 化,对于文本文件是没问题的,但是对于一些图片文件、pdf 等二进制文件就会导致额外的问题。

这里参考两个库的写法 v8 .gitattributes

# Automatically normalize line endings (to LF) for all text-based files.
* text=auto eol=lf
# Do not modify line endings for binary files (which are sometimes auto
# detected as text files by git).
*.png binary
# Don't include minified JS in git grep/diff output
test/mjsunit/asm/sqlite3/*.js -diff

上面会把所有的文件都 LF 化,不同的是单独为需要的资源添加了 binary,而翻阅 git 官方 gitattributes 文档 可以看到

如果你不希望产生文本差异,以及行尾转换应用到任何二进制文件。可以使用系统内置的 binary,它会取消 text 和 diff 属性。

而第三行 test/mjsunit/asm/sqlite3/*.js -diff 则是说不要跟踪 diff 差异。

再来看一个 pdf .gitattributes

# Force Unix line endings for most file formats (except binary files)
*.js         text eol=lf
*.jsm        text eol=lf
*.css        text eol=lf
*.html       text eol=lf
*.md         text eol=lf
*.properties text eol=lf
*.yml        text eol=lf
*.json       text eol=lf
*.config     text eol=lf
*.inc        text eol=lf
*.manifest   text eol=lf
*.rdf        text eol=lf
*.jade       text eol=lf
*.coffee     text eol=lf

# PDF files shall not modify CRLF line endings
*.pdf -crlf

# Linguist language overrides
*.js linguist-language=JavaScript
*.jsm linguist-language=JavaScript
*.inc linguist-language=XML

它将资源前置化处理,需要的文件全部 LF 处理,而 *.pdf -crlf 则是说强制 crlf 处理,下面的 linguist-languageLinguist 库的属性,它用于生成我们常见的语言分布

image-20220410155340149.png

综合上面两个库来看,有两种编写 .gitattributes 的方式

前置后添加

这就是 pdf.js 所采用的,将需要用的序列化,例如如果你是 React 项目,只可能只需要这样指定

*.js        text eol=lf
*.jsm       text eol=lf
*.jsx     	text eol=lf
*.ts        text eol=lf
*.tsx   	text eol=lf
*.json      text eol=lf
*.css       text eol=lf
*.html      text eol=lf

后面如果还有新的需要行尾序列的后缀可以继续添加

全局后添加

这是 V8 的做法,假定所有资源都是文本相关,如果后续有新的二进制文件再对 .gitattributes 文件进行编辑。

* text eol=lf
# 如果有新的二进制文件
* png eol=binary

迁移指南

如果你的项目已经存在很久,但是也想添加 .gitattributes 文件来统一行尾序列,下面就介绍一下相关方法。

  1. 添加相关的 .gitattributes 文件到项目(详情见如何编写.gitattributes 文件
  2. 运行 git add . 添加所有文件
  3. 运行 git commit -m "Saving files before refreshing line endings" 来保存本次更改
  4. 删除所有文件,不包括 .git 目录,git rm -rf --cached .
  5. 运行 git reset --hard HEAD,恢复上一次的提交,这里会得到正确的行尾序列

兼容指南

如果你发现 monorepo 项目中还是没有得到正确的行尾序列,可以参考一下下面 .gitattributes 的写法

* text=auto

# Force the following filetypes to have unix eols, so Windows does not break them
*.* text eol=lf

# Windows forced line-endings
/.idea/* text eol=crlf

#
## These files are binary and should be left untouched
#

# (binary is a macro for -text -diff)
*.png binary

添加了兼容性的写法,强制在 window 下 lf 化

最后

如果文章有相关错误欢迎指出

参考了