.gitattributes 的用途和工作机制

This article is extracted from the chat log with AI. Please identify it with caution.

https://git-lfs.com/

一、.gitattributes 概念与总体作用#

1. .gitattributes 是什么#

  • .gitattributes 是 Git 仓库中的一个配置文件,用来“按文件路径匹配”控制 Git 对这些文件的各种行为。
  • 它可以存在于:
    • 仓库根目录(最常见)
    • 任意子目录(子目录中的 .gitattributes 会叠加/覆盖上层目录的规则)
  • 在克隆仓库时,.gitattributes 作为普通文件一起被拉取,因此所有协作者都会拿到同样的“文件行为规则”。

2. .gitattributes 大致控制哪些行为#

主要控制这些:

  • 文本/二进制识别
    • 告诉 Git 某些文件是文本还是二进制(影响 diff、merge、换行处理等)
  • 换行符(CRLF/LF)与文本规范
    • 控制工作区里的换行符(eol=lf, eol=crlf)
    • 控制提交到仓库里的换行处理策略(text, text=auto)
  • diff 行为
    • 是否对某些文件做文本 diff(还是视为二进制)
    • 是否使用自定义 diff 驱动(diff=NAME)
  • merge 行为
    • 是否对某些文件使用特殊合并策略(merge=NAME)
  • filter(清洗/还原过滤器)
    • 使用 clean/smudge 过滤器在工作区与仓库存储之间双向转换内容
    • Git LFS 就是基于 filter 机制实现的
  • 导出行为(git archive)
    • export-ignore:打包/发布归档时忽略哪些文件
    • export-subst:打包时做占位符替换(如 $Id$)

3. 与 .gitignore、.gitconfig 的区别#

  • .gitignore
    • 用来告诉 Git 哪些文件“不参与版本控制”(不加入 index)
    • 不影响已经被跟踪的文件的行为
    • 不控制 diff/merge/换行等
  • .gitattributes
    • 作用于“已被 Git 跟踪或将要跟踪的文件”
    • 控制这些文件的 diff、merge、换行、filter 等行为
  • .gitconfig
    • Git 配置文件,分系统级、用户级、仓库级
    • 配置用户信息、别名、全局 diff/merge 驱动等
    • 某些配置(比如 diff 驱动、filter)在 .gitconfig 定义,在 .gitattributes 按路径引用

一句话区分:

  • .gitignore = 哪些文件不进 Git
  • .gitattributes = 进了 Git 的文件要怎么被处理
  • .gitconfig = Git 的行为默认设置和扩展组件定义(diff/merge/filter 名称等)

二、语法与匹配规则基础#

1. 模式匹配规则#

  • 跟 .gitignore 很像(但不是百分百相同),常用:
    • *.txt:匹配所有 .txt 文件
    • docs/**:匹配 docs 目录及其子目录下的所有内容
    • path/file.ext:精确文件路径
    • subdir/*.js:匹配指定目录下的 js 文件
  • 重要区别(常见误区):
    • .gitignore 支持前缀 ! 用来“反选忽略”,.gitattributes 不需要这个行为(它是叠加属性)。
    • .gitattributes 更像“最后匹配优先”:同一文件被多条规则匹配时,后面的规则会覆盖前面的同名属性。

2. 属性标记方式#

一条规则一般长这样:

  • pattern attr
  • pattern -attr
  • pattern attr=value

含义:

  • attr:开启/设置该属性为“true”。比如 text 表示此匹配的文件被当作文本处理。
  • -attr:显式关闭该属性。比如 -text 告诉 Git“不要当作文本”,通常意味着视为二进制。
  • attr=value:设置属性具体值。比如 eol=lfdiff=markdownfilter=lfs

3. 一个最简单示例#

假设仓库根目录有一个 .gitattributes:

# 所有文本文件统一做换行规范
*.txt text eol=lf

# 所有 PNG 图片标记为二进制
*.png -text

# Markdown 使用自定义 diff 驱动
*.md text diff=markdown

逐行说明:

  • *.txt text eol=lf
    • 匹配所有 .txt 文件
    • text:告诉 Git 这些是文本(提交时可以按文本规则处理)
    • eol=lf:工作区中的换行统一为 LF(仓库内部实际会标准化处理)
  • *.png -text
    • 匹配所有 .png
    • -text:明确标记为非文本(视为二进制)
    • Git 不会对它做换行转换,diff 和 merge 会当作二进制处理
  • *.md text diff=markdown
    • 匹配所有 .md
    • text:文本文件
    • diff=markdown:使用名为 markdown 的 diff 驱动(具体行为在 .gitconfig 里配置)

三、常用属性分类详解(非 LFS 场景)#

3.1 文本与换行符相关属性#

1)text、-text、text=auto

  • text
    • 把文件当作文本处理
    • Git 在提交到仓库时会做一定的换行规范化(结合 core.autocrlf 等)
  • -text
    • 禁用文本处理,通常意味着当作二进制
    • 不做换行转换,不做文本 diff(除非另有 diff 属性)
  • text=auto
    • 让 Git 自动根据内容判断(启发式)文件是不是文本
    • 类似 core.autocrlf 默认处理方式,但显式写在仓库中,所有人一致
    • 推荐在跨平台项目中使用

2)eol=lf / eol=crlf

  • eol=lf
    • 工作区中使用 LF 换行(Unix 风格)
  • eol=crlf
    • 工作区中使用 CRLF 换行(Windows 风格)
  • 注意:eol 控制的是“工作区”换行形式,仓库内部通常会被规范化为一种一致的格式(一般是 LF)。

3)跨平台团队推荐策略

  • 推荐:仓库内部统一使用 LF,工作区按需转换(但尽量也用 LF)
  • 实际配置建议:
    • 文本文件统一 text=auto,对某些语言/文件使用 eol=lf 强制 LF
    • 对确需 CRLF 的文件(某些 Windows 批处理脚本 .bat/.cmd)使用 eol=crlf

示例:跨平台项目常用配置

# 1. 默认所有文件自动按文本检测
* text=auto

# 2. 源代码统一 LF(推荐)
*.c     text eol=lf
*.h     text eol=lf
*.cpp   text eol=lf
*.hpp   text eol=lf
*.js    text eol=lf
*.ts    text eol=lf
*.py    text eol=lf
*.java  text eol=lf
*.go    text eol=lf
*.rb    text eol=lf

# 3. 脚本文件:同样统一 LF
*.sh    text eol=lf
*.bash  text eol=lf

# 4. Windows 特有脚本使用 CRLF(cmd/powershell)
*.bat   text eol=crlf
*.cmd   text eol=crlf
*.ps1   text eol=crlf

# 5. 明确声明二进制文件(不做换行转换)
*.png   -text
*.jpg   -text
*.jpeg  -text
*.gif   -text
*.pdf   -text
*.zip   -text
*.exe   -text
*.dll   -text

3.2 binary / diff 相关属性#

1)binary 属性

  • binary 等价于 -text -diff(在多数版本的 Git 中的约定)
  • 表示:
    • 不做文本换行转换
    • 不做文本 diff(把 diff 视为二进制)
  • 何时用:
    • 对于图像、音视频、压缩包等确实不能按文本解析的文件,显式标记 binary 更安全清晰

示例:

*.png  binary      # 图片当作二进制,不做文本处理
*.jpg  binary
*.zip  binary
*.pdf  binary

2)diff / diff=NAME

  • diff(不带值)
    • 表示允许对这些文件做文本 diff(默认行为)
    • 实际上很多时候不写 diff 属性也会被按文本 diff
  • diff=NAME
    • 使用自定义 diff 驱动 NAME
    • 需要在 .gitconfig(或仓库级 .git/config)中定义对应的 diff 驱动行为

例:为 *.md 使用自定义 markdown diff 驱动

.gitattributes:

*.md text diff=markdown

.gitconfig(或 .git/config)中:

[diff "markdown"]
  textconv = markdown-diff-filter  # 调用一个外部脚本对 Markdown 做预处理再 diff
  • 这样,Git 在 diff .md 文件时,会先用 textconv 指定的命令转换内容,再比较,能更好处理 Markdown 标题、列表等。

再例:为 *.png 显式标记为 binary:

*.png binary   # 强制当作二进制,禁用文本 diff

3.3 merge 相关属性#

1)merge / merge=NAME

  • merge(单独)
    • 表示允许三路合并(默认行为)
  • merge=NAME
    • 为匹配的文件使用自定义 merge 驱动(在 .gitconfig 里定义)
  • 常见场景:
    • 某些配置文件(如 JSON/ YAML / XML )希望使用更智能的合并脚本
    • 某些文件不适合自动合并(如锁文件、生成文件),希望用 ours/theirs 之类策略简化冲突

示例:对某类配置文件使用特殊 merge 驱动

.gitattributes:

# 自定义对 JSON 的合并方式
*.json merge=jsonmerge

.gitconfig:

[merge "jsonmerge"]
  name = custom json merge
  driver = json-merge-driver %O %A %B
  • json-merge-driver 是你自定义的脚本,负责把 base(%O)、current(%A)、other(%B) 合并成一个结果文件。

示例:对生成的锁文件使用 ours 策略(只保留当前分支版本)

# package-lock.json 冲突时总是优先使用当前分支版本
package-lock.json merge=ours

3.4 filter 相关属性(clean/smudge)#

1)filter、filter=NAME 的含义

  • filter=NAME
    • 告诉 Git 对匹配的文件启用名为 NAME 的过滤器
  • 过滤器是 Git 的 clean/smudge 机制:
    • clean:工作区 -> Git 对象库(提交/添加之前)时的过滤
    • smudge:Git 对象库 -> 工作区(checkout)时的过滤

Git LFS 就是一个典型的 filter 使用案例:提交时把大文件转成“指针文本”,checkout 时再从 LFS 服务器拉回实际大文件。

简单例子(概念):

.gitattributes:

# 自动格式化 JSON(假设有一个 filter 叫 formatjson)
*.json filter=formatjson

.gitconfig:

[filter "formatjson"]
  clean = json-format-cli --normalize
  smudge = cat
  • clean:提交之前用 json-format-cli 把 JSON 规范化
  • smudge:checkout 时原样输出(不做处理)

3.5 导出相关(git archive)#

1)export-ignore

  • 当执行 git archive(用于打包发布)时,被标记 export-ignore 的文件不会被包含进去
  • 常用于忽略:
    • 开发脚本
    • 测试数据
    • CI 配置文件等非发布内容

2)export-subst

  • 允许在归档时对特定占位符做替换,如 $Id$
  • 用得相对少,这里只做概念了解

示例:

# 打包发布时忽略以下文件和目录
.gitignore      export-ignore
.gitattributes  export-ignore
tools/**        export-ignore
tests/**        export-ignore
docs/dev/**     export-ignore

四、Git LFS 场景重点讲解#

4.1 Git LFS 的基本原理(简要)#

1)LFS 做了什么

  • Git LFS(Git Large File Storage)并没有修改 Git 的内部核心机制,而是通过:
    • 在 Git 里存储“小的指针文件”(文本)
    • 把真正的大文件内容放在一个独立的 LFS 服务器或存储(可以是 git 服务器扩展)
  • 对开发者来说:
    • 提交/checkout 文件的流程基本一样
    • Git 里看到的是指针文件,但 Git LFS 在工作区自动替换成真正的大文件

2)为什么用 LFS

  • 避免大文件直接进入 Git 对象库:
    • 仓库体积会快速膨胀
    • clone/pull 非常慢
    • 历史清理极其困难(大文件一旦进了历史,很难完全清除)
  • LFS 的好处:
    • Git 历史中只记录小指针文件,体积大幅缩小
    • LFS 对大文件有专门的存储和传输机制,更适合大体积和多版本
    • clone 时可以选择是否下载所有 LFS 内容

4.2 .gitattributes 在 LFS 中的核心角色#

1)git lfs track 实际干了什么

当你执行:

git lfs track "*.psd"

Git LFS 会自动在 .gitattributes 中添加一行类似:

*.psd filter=lfs diff=lfs merge=lfs -text

也就是说,“LFS 管理哪些文件类型”,实际是通过 .gitattributes 来告知 Git 的。

2)典型规则格式逐项解释

以这行为例:

*.psd filter=lfs diff=lfs merge=lfs -text
  • *.psd
    • 匹配所有 Photoshop 文件
  • filter=lfs
    • 使用名为 lfs 的 filter
    • 这就是 Git LFS 注册的 clean/smudge 过滤器
    • clean:提交时,把真实的二进制 PSD 文件转换为一个 LFS 指针文本
    • smudge:checkout 时,根据指针文件从 LFS 存储拉回真实 PSD 到工作区
  • diff=lfs
    • 使用 LFS 的 diff 方式(通常不会显示具体二进制 diff,而是显示文件 ID 变化等)
  • merge=lfs
    • 使用 LFS 的 merge 方式(一般是“把文件当作整体二进制对象处理”,不做文本合并)
  • -text
    • 明确声明这些文件不做文本处理
    • 防止 Git 对它们做任何换行转换或默认文本 diff

3).gitattributes 对 LFS 的影响总结

  • .gitattributes 是 LFS 生效的关键:
    • 没有在 .gitattributes 中标记为 filter=lfs 的文件类型,Git LFS 就不会接管这些文件,它们会以普通方式进入 Git 仓库(这是很多人踩的坑)。
  • 所以:
    • LFS 类型配置 = 修改 .gitattributes
    • git lfs track/untrack = 帮你自动操作 .gitattributes

4.3 常见 LFS 配置示例(典型大文件项目)#

假设你有一个项目,包含:

  • 图片:*.png, *.jpg, *.jpeg
  • 音视频:*.mp3, *.wav, *.mp4
  • 压缩包:*.zip, *.tar.gz
  • 模型/权重:*.bin, *.h5, *.pt, *.onnx

可以这样配置:

# =======================
# Git LFS 管理的大文件
# =======================

# 图片资源(通常体积大且频繁修改)
*.png  filter=lfs diff=lfs merge=lfs -text   # 位图图片,如 UI 素材、纹理等
*.jpg  filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text

# 音频/音效资源
*.mp3  filter=lfs diff=lfs merge=lfs -text
*.wav  filter=lfs diff=lfs merge=lfs -text

# 视频资源
*.mp4  filter=lfs diff=lfs merge=lfs -text
*.mov  filter=lfs diff=lfs merge=lfs -text

# 压缩包、归档数据(导入数据包/资源包)
*.zip      filter=lfs diff=lfs merge=lfs -text
*.tar      filter=lfs diff=lfs merge=lfs -text
*.tar.gz   filter=lfs diff=lfs merge=lfs -text
*.tgz      filter=lfs diff=lfs merge=lfs -text

# 机器学习/游戏模型、权重文件
*.bin   filter=lfs diff=lfs merge=lfs -text  # 自定义二进制权重/参数文件
*.h5    filter=lfs diff=lfs merge=lfs -text
*.hdf5  filter=lfs diff=lfs merge=lfs -text
*.pt    filter=lfs diff=lfs merge=lfs -text  # PyTorch 模型
*.pth   filter=lfs diff=lfs merge=lfs -text
*.onnx  filter=lfs diff=lfs merge=lfs -text
*.pb    filter=lfs diff=lfs merge=lfs -text  # TensorFlow GraphDef
*.tflite filter=lfs diff=lfs merge=lfs -text

# 其他体积可能较大的二进制资源
*.iso   filter=lfs diff=lfs merge=lfs -text
*.dll   filter=lfs diff=lfs merge=lfs -text
*.so    filter=lfs diff=lfs merge=lfs -text
*.dmg   filter=lfs diff=lfs merge=lfs -text

说明:

  • 这些规则都表示“由 LFS 接管这些文件类型”,提交它们时,只会在 Git 中存一个小指针文件。
  • 正常开发时,你看的是完整资源文件(因为 checkout 时 LFS 帮你从存储拉回来了)。

4.4 Git LFS + .gitattributes 实战注意事项#

1)正确顺序:先配置 LFS,再提交大文件

正确流程:

  • 初始化 LFS(仓库内执行一次):
    git lfs install
  • 针对大文件类型执行 track(会更新 .gitattributes):
    git lfs track "*.png"
    git lfs track "*.mp4"
    git lfs track "*.pt"
  • 提交 .gitattributes(必须先提交 .gitattributes):
    git add .gitattributes
    git commit -m "Configure LFS for binary assets"
  • 再提交对应大文件:
    git add assets/*.png models/*.pt
    git commit -m "Add initial assets and models"

2)如果先提交大文件,再配置 LFS 会怎样

  • 已经提交的历史中的大文件,已经被存入 Git 对象库(而不是 LFS 存储)。
  • 此时你再 git lfs track + 提交,只会影响以后新提交的版本:
    • 旧的版本依然很大
    • 仓库体积不会自动缩小
  • 想要“把历史中的大文件迁移到 LFS”:
    • 需要使用 git lfs migrate 或者 git filter-repo(低版本用 filter-branch)等历史重写工具,风险和成本较高。
  • 因此强烈建议:新项目一开始就配置好 LFS。

3)不要随意手动删除或修改 LFS 相关 .gitattributes 规则

  • 如果有人删除了类似:
    *.png filter=lfs diff=lfs merge=lfs -text
  • 后果:
    • 删除规则之后新提交的 .png 文件就不再通过 LFS 管理,而是直接进入 Git 对象库。
    • 之前已经在 LFS 管理下的旧版本仍然在 LFS 中,导致历史不一致:
      • 某些版本的 .png 在 LFS,某些版本直接在 Git
      • clone 时行为可能令人困惑
  • 团队管理建议:
    • 把 .gitattributes 视为“仓库行为约定”,修改前要经过代码审查。
    • 在 MR/PR 中对 LFS 规则变更特别留意。

4)多人协作时的同步问题

  • .gitattributes 是版本文件,必须和代码一起被同步:
    • 当有人新增 LFS 规则后,其他人拉取最新代码,会同时获得 .gitattributes 更新。
    • 只有在每个开发者本地都安装了 Git LFS(git lfs install),LFS 规则才会真正生效。
  • 常见坑:
    • 某个开发者没装 Git LFS,结果他提交时,LFS 没有正确接管,可能导致指针文件被当作普通文本处理。
    • 团队规范建议:在项目 README 或贡献指南中,写明:
      • “此项目必须安装 Git LFS”
      • 第一次 clone 后先运行 git lfs installgit lfs pull

五、完整示例:带注释的 .gitattributes 文件#

5.1 示例 1:普通跨平台文本项目(不使用 LFS)#

场景:纯代码项目(例如 Web/后端应用),不使用 LFS,仅需要:

  • 统一换行符策略
  • 合理区分文本/二进制
  • 对部分文件设置特殊 diff/merge 或导出规则

示例 .gitattributes:

##############################################
# 通用规则:文本检测与换行符处理
##############################################

# 默认:所有文件自动按文本检测
* text=auto

# 源代码统一使用 LF 换行(推荐跨平台策略)
*.c     text eol=lf
*.h     text eol=lf
*.cpp   text eol=lf
*.hpp   text eol=lf
*.cc    text eol=lf

*.java  text eol=lf
*.kt    text eol=lf
*.scala text eol=lf

*.js    text eol=lf
*.ts    text eol=lf
*.jsx   text eol=lf
*.tsx   text eol=lf

*.py    text eol=lf
*.rb    text eol=lf
*.go    text eol=lf
*.php   text eol=lf

# 脚本同样统一 LF
*.sh    text eol=lf
*.bash  text eol=lf
*.zsh   text eol=lf

# Windows 专用脚本使用 CRLF(避免某些环境因 LF 失败)
*.bat   text eol=crlf
*.cmd   text eol=crlf
*.ps1   text eol=crlf

# 文本配置文件(统一 LF)
*.yml   text eol=lf
*.yaml  text eol=lf
*.json  text eol=lf
*.xml   text eol=lf
*.toml  text eol=lf
*.ini   text eol=lf
*.conf  text eol=lf
*.env   text eol=lf
*.properties text eol=lf

# 文档文件
*.md    text eol=lf
*.txt   text eol=lf
*.rst   text eol=lf
*.csv   text eol=lf

##############################################
# 二进制文件显式标记(避免误判)
##############################################

# 图片资源
*.png   binary     # 等价于 -text -diff
*.jpg   binary
*.jpeg  binary
*.gif   binary
*.ico   binary
*.svgz  binary

# 字体文件
*.ttf   binary
*.otf   binary
*.woff  binary
*.woff2 binary

# 压缩文件
*.zip   binary
*.gz    binary
*.bz2   binary
*.xz    binary
*.7z    binary
*.rar   binary

# 可执行/库文件
*.exe   binary
*.dll   binary
*.so    binary
*.dylib binary

##############################################
# diff & merge 自定义
##############################################

# Markdown 使用自定义 diff 驱动(需在 .gitconfig 定义 diff.markdown)
*.md    text diff=markdown

# 某些自动生成的锁文件,不做复杂合并,直接使用当前分支版本
package-lock.json merge=ours
yarn.lock          merge=ours
pnpm-lock.yaml     merge=ours

##############################################
# 导出相关(用于 git archive 打包发布)
##############################################

# 不把这些文件/目录打包到发布包中
.gitignore      export-ignore
.gitattributes  export-ignore
.github/**      export-ignore
.gitlab/**      export-ignore
tools/**        export-ignore
scripts/dev/**  export-ignore
tests/**        export-ignore
docs/dev/**     export-ignore

5.2 示例 2:使用 Git LFS 的项目(大量资源文件)#

场景:Web/游戏/数据项目,有大量图片、音视频、模型等大文件,需要:

  • 文本文件统一规范
  • 所有大资源文件交给 LFS 管理

示例 .gitattributes:

##############################################
# 文本文件:统一换行符与编码处理
##############################################

# 默认文本自动检测
* text=auto

# 源码统一 LF
*.js    text eol=lf
*.ts    text eol=lf
*.jsx   text eol=lf
*.tsx   text eol=lf
*.css   text eol=lf
*.scss  text eol=lf
*.html  text eol=lf
*.vue   text eol=lf

*.cs    text eol=lf
*.cpp   text eol=lf
*.h     text eol=lf

*.py    text eol=lf
*.go    text eol=lf
*.java  text eol=lf

# 配置与脚本
*.json  text eol=lf
*.yml   text eol=lf
*.yaml  text eol=lf
*.xml   text eol=lf
*.ini   text eol=lf
*.toml  text eol=lf
*.conf  text eol=lf
*.env   text eol=lf

*.sh    text eol=lf
*.bat   text eol=crlf
*.cmd   text eol=crlf
*.ps1   text eol=crlf

# 文档
*.md    text eol=lf
*.txt   text eol=lf
LICENSE text eol=lf

##############################################
# Git LFS 管理的资源文件
##############################################
# 注意:这些规则对应 `git lfs track` 命令生成的配置
#       修改前请确认了解影响

# 1. 图片资源(大分辨率、贴图、UI 素材)
*.png     filter=lfs diff=lfs merge=lfs -text
*.jpg     filter=lfs diff=lfs merge=lfs -text
*.jpeg    filter=lfs diff=lfs merge=lfs -text
*.tga     filter=lfs diff=lfs merge=lfs -text
*.psd     filter=lfs diff=lfs merge=lfs -text   # Photoshop 源文件
*.bmp     filter=lfs diff=lfs merge=lfs -text
*.tiff    filter=lfs diff=lfs merge=lfs -text

# 2. 音频和音效
*.mp3     filter=lfs diff=lfs merge=lfs -text
*.wav     filter=lfs diff=lfs merge=lfs -text
*.ogg     filter=lfs diff=lfs merge=lfs -text
*.flac    filter=lfs diff=lfs merge=lfs -text

# 3. 视频和过场动画
*.mp4     filter=lfs diff=lfs merge=lfs -text
*.mov     filter=lfs diff=lfs merge=lfs -text
*.avi     filter=lfs diff=lfs merge=lfs -text
*.webm    filter=lfs diff=lfs merge=lfs -text

# 4. 压缩包/导入数据包
*.zip     filter=lfs diff=lfs merge=lfs -text
*.rar     filter=lfs diff=lfs merge=lfs -text
*.7z      filter=lfs diff=lfs merge=lfs -text
*.tar     filter=lfs diff=lfs merge=lfs -text
*.tar.gz  filter=lfs diff=lfs merge=lfs -text
*.tgz     filter=lfs diff=lfs merge=lfs -text

# 5. 模型/权重/大数据文件
*.bin     filter=lfs diff=lfs merge=lfs -text   # 自定义二进制参数文件
*.h5      filter=lfs diff=lfs merge=lfs -text
*.hdf5    filter=lfs diff=lfs merge=lfs -text
*.pt      filter=lfs diff=lfs merge=lfs -text   # PyTorch 模型
*.pth     filter=lfs diff=lfs merge=lfs -text
*.onnx    filter=lfs diff=lfs merge=lfs -text
*.pb      filter=lfs diff=lfs merge=lfs -text   # TensorFlow 模型
*.tflite  filter=lfs diff=lfs merge=lfs -text
*.npz     filter=lfs diff=lfs merge=lfs -text   # 压缩 numpy 数组
*.npy     filter=lfs diff=lfs merge=lfs -text

# 6. 游戏引擎资源(例如 Unity/Unreal)
*.unity   filter=lfs diff=lfs merge=lfs -text
*.prefab  filter=lfs diff=lfs merge=lfs -text
*.asset   filter=lfs diff=lfs merge=lfs -text
*.fbx     filter=lfs diff=lfs merge=lfs -text
*.uasset  filter=lfs diff=lfs merge=lfs -text
*.umap    filter=lfs diff=lfs merge=lfs -text

# 7. 其他可能较大的二进制文件
*.iso     filter=lfs diff=lfs merge=lfs -text
*.dmg     filter=lfs diff=lfs merge=lfs -text
*.apk     filter=lfs diff=lfs merge=lfs -text
*.ipa     filter=lfs diff=lfs merge=lfs -text

##############################################
# 导出规则:打包时不需要的开发文件
##############################################

.gitignore      export-ignore
.gitattributes  export-ignore
.gitattributes.example export-ignore
.github/**      export-ignore
.gitlab/**      export-ignore
docs/dev/**     export-ignore
tools/**        export-ignore
scripts/dev/**  export-ignore
tests/**        export-ignore

六、最佳实践与常见坑总结#

1)新项目一开始就建立合适的 .gitattributes#

  • 原因:
    • 早期确定好文本/二进制规则和 LFS 管理范围,能避免后期历史重写。
    • 特别是 LFS,如果大文件一开始就没进 Git 正常对象库,你以后几乎不用考虑迁移/清理。
  • 建议流程:
    • 创建仓库初始时,就添加 .gitattributes。
    • 如果需要 LFS,立即做:
      • git lfs install
      • git lfs track “*.xxx”
      • 提交 .gitattributes 之后再添加资源文件。

2)跨平台项目的 text/eol 策略#

  • 推荐策略:
    • 仓库内部统一使用 LF。
    • 工作区尽量也用 LF(windows 端编辑器通常默认支持 LF,按需配置)。
  • 实战建议:
    • 在 .gitattributes 中明确设置:
      • * text=auto
      • 对源代码、配置、脚本使用 eol=lf
      • 对 .bat / .cmd / .ps1 使用 eol=crlf
    • 禁用 core.autocrlf(设置为 false),把换行控制尽量统一写到 .gitattributes 中,保证团队一致性。

3)使用 LFS 的团队规范#

  • 规范 1:必须安装 Git LFS
    • 在项目 README 中写清:
      • “克隆本仓库前,请先安装 Git LFS”
      • “首次使用:git lfs install(每台机器只需要执行一次)”
  • 规范 2:统一通过 git lfs track 修改 .gitattributes
    • 不要求所有人手写 LFS 规则,鼓励通过命令添加:
      • git lfs track "*.psd"
    • 这样可以避免属性书写错误(少写 diff=lfs / merge=lfs 等)
  • 规范 3:修改 LFS 规则要走正常审查流程
    • 特别是删除/放宽某些 LFS 规则时,一定要在 MR/PR 里说明原因和影响。

4)常见错误场景与解决思路#

(1)仓库突然异常庞大

  • 可能原因:
    • 大文件直接被提交到 Git(没有通过 LFS)
    • 经常更新的大文件(如日志、构建产物)被反复提交
  • 解决思路:
    • 查找大对象:
      • git count-objects -vH
      • 或使用 git-sizergit rev-list --objects --all | sort -k 2
    • 对已有的大文件:
      • 若希望迁移到 LFS:
        • 使用 git lfs migrate import --include="*.xxx" 重写历史(需要全员配合,更换远程等)
      • 或者用 git filter-repo 清理(同样是历史重写)
    • 防止再次发生:
      • 在 .gitattributes 中为这些文件类型配置 LFS 或标记为 binary,并在流程中避免提交生成文件。

(2)换行符混乱(CRLF/LF 交替)

  • 典型表现:
    • 某些文件每次被别人改动后,你这边总是显示整文件被修改(CRLF/LF 切换)。
  • 解决思路:
    • 在 .gitattributes 中明确该文件类型的 eol(通常 eol=lf)。
    • 同时建议每个人关闭 core.autocrlf,统一使用 .gitattributes 来做换行控制:
      • git config core.autocrlf false
    • 对已经混乱的文件,可以统一一次性转换:
      • 在一个 clean 的分支上用脚本统一转换换行,然后提交;后续由 .gitattributes 保持稳定。

(3)大文件未通过 LFS 管理

  • 典型情况:
    • 已经用了 LFS,但是某类新出现的大文件类型(如 .npz)忘记 track。
    • 几个月后发现仓库变大,才意识到这些文件走的是普通 Git。
  • 解决思路:
    • 先补上 .gitattributes 规则:
      • git lfs track "*.npz"
    • 新提交开始由 LFS 接管
    • 对于历史:视情况选择是否用 git lfs migrate / filter-repo 来迁移已有历史。
  • 预防:
    • 当引入新类型的大文件时,把“添加 LFS track”作为 code review 的检查项;
    • 或用 pre-receive hook / CI 检查提交中是否包含未 LFS 管理的大文件(超过某大小阈值)。

(4)LFS 文件 checkout 变成指针文本,无法使用

  • 现象:
    • 打开某个资源文件(png、mp4 等),看到的是类似:
      version https://git-lfs.github.com/spec/v1
      oid sha256:xxxx
      size 1234567
  • 可能原因:
    • 本地没有安装 Git LFS,或没有执行 git lfs install
  • 解决:
    • 安装 Git LFS(根据平台:brew / apt / choco 等)
    • 在仓库中执行:
      git lfs install
      git lfs pull
      git checkout HEAD -- .
    • 让 LFS 重新 smudge,把指针文件替换为真实大文件。

总结一句话:

  • .gitattributes 是 Git 里“按路径控制文件行为”的核心配置,负责文本/二进制识别、换行规范、diff/merge 策略、filter(包括 LFS)和导出规则。
  • 在 Git LFS 场景中,.gitattributes 是 LFS 生效的“入口”:哪些文件由 LFS 管理,完全靠它决定(filter=lfs diff=lfs merge=lfs -text 等)。
  • 只要从项目一开始就认真设计 .gitattributes,并在团队中把它当作“仓库行为规范”的一部分来管理,就能避免绝大多数大文件和换行相关的坑。
本文共 7705 字,创建于 Nov 19, 2025

相关标签: Git, DevOps, Linux, Shell, ByAI