Git 隐身术:exclude、clean、fetch/pull 与 LFS,一次讲透本地工作区

不想产生 diff 的私人目录、不敢跑的 clean、总搞混的 fetch 和大文件坑——这篇全搞定

May 22, 2026·7 min read·Yimin
#Git#Git LFS#工作流#开发工具#版本控制

你在仓库里放了一套本地调试工具,不想提交、不想改 .gitignore 产生 diff,还想 git status 干干净净——.git/info/exclude 就是为此存在的。但 Git 工作区远不止 ignore 这一件事:清文件用错命令会删光 .venvfetchpull 搞混会在错误时机 merge,大文件没走 LFS 会把仓库拖垮。这篇把这几块串成一张地图。

🎯 一张地图:Git 工作区你到底在管什么?

日常开发里,和 Git 打交道其实就四类诉求:

诉求你在问的问题对应机制
看不见某些文件/目录不要出现在 git statusignore 体系
清干净构建产物、临时文件、误生成的目录怎么删clean / restore / reset
同步远程远程有新提交了,我该怎么拉fetch / pull
管大文件模型、数据集、大二进制要不要进仓库Git LFS

很多人把这几件事混为一谈——比如以为 git clean 能撤销已修改的代码,或者以为 .gitignore 能隐藏已经 tracked 的文件。下面按层拆开讲。


🏛️ 忽略文件的四层体系

Git 决定「某个路径要不要出现在未跟踪列表里」,会依次叠加多条规则。语法完全一样,差别在于谁生效、会不会进仓库

┌─────────────────────────────────────────────────────────┐
│  ① 全局 exclude                                         │
│     ~/.gitignore_global  (core.excludesfile)              │
│     → 所有仓库、仅本机                                   │
├─────────────────────────────────────────────────────────┤
│  ② 仓库 .gitignore                                      │
│     → 团队共享,会 commit,clone 后人人都有               │
├─────────────────────────────────────────────────────────┤
│  ③ 本机 .git/info/exclude                               │
│     → 仅当前仓库、仅本机,不进版本库                     │
├─────────────────────────────────────────────────────────┤
│  ④ 已 tracked 的文件                                    │
│     → 以上规则全部无效,必须 git rm --cached 才能「脱钩」 │
└─────────────────────────────────────────────────────────┘

三层 ignore 怎么选?

规则放哪里是否 commit典型场景
全局 core.excludesfilemacOS 的 .DS_Store、编辑器 swap 文件——所有项目通用
.gitignore__pycache__/.venv/、构建产物——团队统一
.git/info/exclude本地实验目录、私人脚本、还没决定是否提交的 devtools

配置全局 exclude 一次即可:

git config --global core.excludesfile ~/.gitignore_global

🧠 明星选手:.git/info/exclude

它是什么?

仓库里的 .git/info/exclude语法与 .gitignore 完全相同,但:

  • 不会git commit
  • 不会出现在 git diff
  • 只有你这台机器、这个 clone 生效

查看和编辑:

cat .git/info/exclude

每行一条规则,和 .gitignore 一样支持 */ 前缀、目录匹配等。

举个例子

你在 feature 分支上顺手建了一个 sandbox/ 目录:临时脚本、本地配置、还没定稿的小工具,自己用着顺手,也不影响 CI。

但团队还没拍板要不要纳入仓库——写进 .gitignore 会改 tracked 文件,PR 里多出一笔 diff;现在 commit 又显得太早。

解法:把目录写进 .git/info/exclude

sandbox/

效果:

  • 磁盘上文件照常在,命令照跑
  • git status 显示 working tree clean
  • 同事 clone 不会自动带上这些规则——符合「私人实验」预期

以后若要团队化:从 exclude 删掉 → 迁到 .gitignore + 部署侧的 ignore(如 Docker 镜像排除)→ 正常 commit。

exclude vs .gitignore:决策表

情况用哪个
全公司/全团队都要忽略.gitignore
只有我不想看见、不影响别人.git/info/exclude
所有项目都要忽略全局 core.excludesfile
文件已经被 commit 过git rm --cached,再 ignore——换哪个 ignore 文件都一样

⚠️ exclude 不能做的事

  • 不能让已 tracked 的文件「隐身」——ignore 只对 untracked 路径生效
  • 不是安全机制——只是不提交,文件仍在磁盘上,别用来「藏密钥」
  • 不能自动同步给同事——正式方案仍要进 .gitignore 或文档说明

🔍 工作区清理:restore、checkout、reset、clean 破坏性分级

「把工作区弄干净」至少有三种完全不同的操作,搞混后果差很多。

它们各管什么?

命令作用对象干什么
git restore <file>已跟踪文件的工作区丢弃未 stage 的本地修改
git restore . / git checkout .当前目录下已跟踪文件批量丢弃未 stage 的本地修改(后者是旧写法,效果相同)
git restore --staged <file>暂存区取消 stage,改动保留在工作区
git reset --hard <commit>工作区 + 暂存区全部回到某 commit,本地改动全没
git clean未跟踪文件/目录物理删除

一句话记忆:restore / checkout . / reset 动的是 Git 已知的文件;clean 动的是 Git 还不认识的文件。

git checkout . 老项目里很常见,和 git restore . 一样只清未 stage 的改动;已 git add 的内容还在暂存区,需要 git restore --staged .git reset 才能一并撤掉。

git clean 详解

# 先预览,养成习惯
git clean -fdn

# 删除未跟踪的文件和目录(仍尊重 ignore / exclude)
git clean -fd

# 连 ignore 规则下的也删——例如 .venv、node_modules
git clean -fdx
参数含义
-fforce,必须加,否则拒绝执行
-d包含未跟踪的目录
-ndry-run,只列出会删什么
-x连 ignored 文件一起删
-i交互模式,逐个确认

和 exclude 的关系:写在 .git/info/exclude.gitignore 里的路径,git clean -fd 不会删;只有加 -x 才会清掉。所以 exclude 里的 sandbox/ 既不会出现在 status,也不会被普通 clean 误伤。

破坏性一览(从低到高)

操作丢失什么能救吗
restore --staged无(只是 unstage)
restore <file> / checkout .该文件(或当前目录)未 stage 的改动难,除非 IDE 本地历史
reset --soft无文件内容,只动 commit 指针reflog 可救
reset --hard工作区 + 暂存区改动reflog 有限可救
clean -fd未跟踪文件基本不可救
clean -fdx未跟踪 + ignored(含 .venv基本不可救

git stash:暂时藏起来,不是删掉

临时切分支、又不想 commit 半成品时用:

git stash push -m "wip: feature x"
git stash list
git stash pop

和 clean 的区别:stash 有意保留内容,clean 是直接删。和 exclude 的区别:stash 针对已跟踪文件的改动,exclude 针对不想被 Git 看见的未跟踪路径

误操作后悔药:git reflog

reset --hard、误删分支之类,reflog 记录 HEAD 移动历史,短期内可以找回:

git reflog
git reset --hard HEAD@{2}

不是万能——clean 删掉的未跟踪文件,reflog 管不了。


🧠 远程同步:fetch 和 pull 到底差在哪?

这是被问最多的问题之一。核心就一句:

git fetch 只更新「远程长什么样」;git pull = fetch + 把远程合进你当前分支。

关键细节:pull 只作用于你当前检出的分支。 人在 feature/foo 上时,git pull 更新的是 feature/foo 对应的远程,不会顺带把 develop 拉新。

常见困惑:在别的分支,怎么把 develop 合进来?

很多人(包括我)一开始会这样干:

# 当前在 feature/foo,想把最新的 develop 合进来
git checkout develop
git pull                    # 更新本地 develop
git checkout feature/foo
git merge develop           # 把 develop 并进 feature

能跑通,但四步里有两步纯粹是为了「先更新 develop」。其实 pull 绑的是当前分支,不是「把远程所有分支都同步一遍」。

更省事的做法——不切分支

git fetch origin                              # 只更新 origin/* 指针
git merge origin/develop                      # 仍在 feature/foo 上,直接合远程 develop
# 或
git rebase origin/develop

fetch 之后 origin/develop 已经是最新的,本地 develop 分支是否落后并不重要;merge/rebase 读的是 remote-tracking 分支 origin/develop,不是必须先 checkout develop && pull

如果团队习惯「本地 develop 也要随时跟远程对齐」,checkout 再 pull 也没错——只是 feature 分支要拿新 develop 时,不必绕这一圈。

时序图

远程 origin/main 多了 3 个 commit
              │
    ┌─────────┴─────────┐
    │                   │
git fetch           git pull(当前在 main 上)
    │                   │
更新 origin/main      更新 origin/main
指针(remote-tracking)   +
    │               merge/rebase 到本地 main
本地 main 不变            │
    │               本地 main 前进(或冲突)
    │                   │
可以先看 diff、           直接改你正在写的分支
再决定怎么合              (在 feature 上 pull 只动 feature)

对比表

git fetchgit pull
改当前分支工作区
更新 origin/* 指针
可能产生 merge 冲突
典型用法「先看看远程变了啥」「我要立刻合进来继续干」

更稳妥的工作流:先 fetch,再决定

git fetch origin

# 看本地比远程落后多少
git log HEAD..origin/main --oneline

# 确认后再合——merge 或 rebase 二选一
git merge origin/main
# 或
git rebase origin/main

git pull 本质是帮你把上面几步打包执行;在多人协作、或当前分支有未 push 的 commit 时,拆开做更可控

pull 的两种口味

方式命令历史形状
merge(默认常见)git pull多一个 merge commit
rebasegit pull --rebase线性,把你的 commit「接到」远程后面

团队若有规范,跟规范走;个人分支 rebasing 通常历史更干净。

fetch 不会做的事

  • 不会删你本地的分支
  • 不会改你工作区里未 commit 的文件
  • 不会自动解决冲突

所以 fetch 是相对安全的「只读远程」操作——这也是 CI、IDE 后台同步常用 fetch 的原因。


⚙️ Git LFS:大文件该这样进仓库

问题从哪来?

Git 把每个版本的文件内容都存进对象库。一个 500MB 的模型权重 commit 十次,仓库体积就膨胀几个 GB,clone 和 fetch 都变慢——而且 Git 设计上更适合文本 diff,不适合大二进制。

Git LFS(Large File Storage) 的做法:Git 里只存轻量指针文件,真实 blob 放在 LFS 服务器上。

普通 Git commit:
  tree → blob(完整 500MB 文件内容)

LFS commit:
  tree → pointer 文本(~130 字节,含 oid、size)
         ↓
  LFS 服务器 → 真实 500MB 文件

什么时候用 LFS?

文件类型建议
源码、配置、小 JSON/YAML普通 Git
模型权重、音频/视频、大型 PDF、数据集切片Git LFS
本地调试产物、构建 outputignore / exclude,不进仓库
敏感凭证绝不进 Git 或 LFS,用环境变量/密钥管理

和 exclude 的决策树:

这个文件需要版本管理吗?
├─ 否 → .gitignore / .git/info/exclude
└─ 是 → 体积大吗?
         ├─ 是 → Git LFS
         └─ 否 → 普通 Git

基本使用流程

# 安装并初始化(每个用户机器做一次)
git lfs install

# 指定哪些扩展名走 LFS
git lfs track "*.bin"
git lfs track "models/**"

# .gitattributes 会生成/更新,需要 commit
git add .gitattributes

clone 已有 LFS 的仓库后,若大文件显示成几行 pointer 文本而不是真文件:

git lfs pull

⚠️ LFS 常见坑

现象原因处理
文件只有 130 字节左右的文本只 clone 了 pointer,没拉 LFS 对象git lfs install + git lfs pull
CI 构建缺文件Runner 没装 git-lfs 或没 pullCI 步骤加 git lfs pull
仓库已经很大历史里普通 commit 过大文件需要 git lfs migrate 改写历史——另作专题
带宽/存储配额爆LFS 对象单独计费评估是否真的需要版本管理大文件

LFS 不是 ignore 的替代品:pointer 仍然进 Git 历史,只是大 blob 外置;不想版本管理的文件,应该 exclude 或 gitignore。


🚀 进阶一块:和本篇相关的几个邻居

单篇讲透主菜,这几项知道存在即可,需要时再深挖。

Git Worktree:同一仓库多个工作目录

一个 repo 同时检出多个分支到不同目录,共享 .git/objects/,各自独立 HEAD 和暂存区——适合 hotfix 与 feature 并行、或多个 AI Agent 各干各的分支。详见同目录下的 Git Worktree 专题

skip-worktree / assume-unchanged

对已 tracked 文件告诉 Git「本地改动别显示」——比 exclude 更 hack,适合极少数「必须改本地配置但永不提交」的场景,团队协作容易踩坑,优先用 exclude 管 untracked

Sparse Checkout

超大 monorepo 只 checkout 子目录,节省磁盘和 IDE 索引时间——和 ignore 无关,但同属「工作区瘦身」工具箱。


⚠️ 一张总坑清单

误区真相
ignore 了就能删 tracked 文件不行,要先 git rm --cached
exclude 可以藏 API Key不行,只是不 commit,磁盘上明文仍在
git pull 等于「安全更新」会改当前分支,可能冲突,先 fetch 更稳
git clean 能撤销代码修改不能,那是 restore / reset 的事
git clean -fd 会删 ignore 里的 .venv不会,要加 -x 才会
LFS 让大文件「不进 Git」pointer 进 Git,blob 外置;不想版本管理请 ignore
.gitignore 改完历史 commit 就变小历史仍在,要 filter-repo / BFG 等重写

📝 总结

你想…用这个
本机私人目录、零 diff、不污染团队.git/info/exclude
团队统一忽略规则.gitignore
所有仓库通用的忽略全局 core.excludesfile
删掉未跟踪的构建产物git clean -fdn 预览 → git clean -fd
丢弃已跟踪文件的本地改动git restoregit checkout .
只看远程更新、暂不合git fetch
拉远程并合进当前分支git pull(或 fetch + merge/rebase)
版本管理大二进制Git LFS
临时存半成品改动git stash

Git 工作区没有银弹,但有清晰分层:ignore 管「看不看见」,clean/restore 管「删不删」,fetch/pull 管「怎么同步,LFS 管「大文件怎么存」**。把 .git/info/exclude 放进工具箱——下次你有「本地要用、还不想产生 diff」的实验代码,就知道该往哪写了。