常见错误:untracked working tree files would be overwritten
场景
git pull / git checkout 时:
Updating 7c9e086..936acac
error: The following untracked working tree files would be overwritten by merge:
Common/HFHttpRequest/HFHttpRequestParameters.h
Common/HFHttpRequest/HFHttpRequestParameters.m
Please move or remove them before you can merge.
Aborting
意思是:远程有这些文件,但你本地有同名未跟踪文件(Git 担心 pull 会覆盖你的本地文件)。
解决方案
方式 1:放弃本地未跟踪文件(接受远程覆盖)
$ git clean -d -fx ""
参数详解:
| 参数 | 含义 |
|---|---|
-x | 删除被 .gitignore 忽略的文件 |
-d | 删除未被 git 跟踪的目录 |
-f | 强制运行(Git 出于安全默认拒绝) |
⚠️
-f是必须的——Git 默认拒绝git clean操作,必须-f才会真正执行。
方式 2:保留本地文件先 stash 起来
$ git stash --include-untracked # 把未跟踪文件也存到 stash
$ git pull # 再 pull(不再冲突)
$ git stash pop # 恢复 stash(可选,决定要不要覆盖回来)
方式 3:手动备份
$ mv Common/HFHttpRequest /tmp/ # 先备份
$ git pull # pull
$ # 再决定怎么处理 /tmp 里的备份
推荐:能用方式 2 就别用方式 1——
git clean是不可逆的删除,stash 至少能恢复。
远程分支切换到本地(精选)
适合刚 clone 一个项目,想在已有远程分支上继续开发的场景。
git branch -a 看远程分支
$ git branch -a
# * master ← 本地当前分支(带 *)
# remotes/origin/dev ← 远程分支(有 remotes/ 前缀)
# remotes/origin/master
错误做法(不推荐)
$ git checkout -b dev # 这种"本地新建 dev" 跟远程 dev **毫无关系**
只是恰好名字叫 dev,本地 dev 没有追踪远程 origin/dev。下次 push/pull 会报:
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
正确做法
# 方式 1:直接 checkout(Git 自动建立追踪)
$ git checkout dev
# 方式 2:显式指定来源
$ git checkout -b dev origin/dev
之后 push:
$ git push -u origin dev # -u 建立追踪关系
以后再 push/pull 不用再带 -u 和分支名。
为什么会有
remotes/origin/dev这种”远程跟踪分支”? 它只是本地对远程分支状态的快照缓存,不能直接提交——只能基于它创建本地工作分支。
文件级历史版本恢复
$ git checkout <hash> -- <file>
把指定文件恢复到某个历史提交的状态,不影响其他文件。
这就是
git checkout的”文件层”用法(见篇 3 的对比表)。它和git checkout -- <file>(撤销未提交修改)的区别在于:
git checkout -- <file>恢复到 HEADgit checkout <hash> -- <file>恢复到 指定历史版本
.git 文件夹瘦身
由于 Git 是分布式版本控制工具,每个开发者通过 git clone 在本地机器上拷贝一个完整的 Git 仓库。随着项目年龄增长,.git 文件会越来越大。尤其是当 commit 了大文件(jar 包、打好的 war 包、音频视频等),哪怕后续已经把文件从项目里删除,Git 为了保证能随时还原历史,依旧在日志中保存这些大文件的二进制流,加重 .git 负担。
案例:误提交大文件
初始大小 180K:

提交一个 8.5M 的 jar 包模拟文件,项目占用 8.6M:

git add + git commit 之后,整个 .git 占用涨到 16M(文件本身 8M + Git 历史中的二进制流 8M):

从项目中删除 jar 文件并提交,但 .git 里还残留 7.7M 的历史:

可以想象一下,本来几百 K 的项目多了几 M、几十 M 甚至几百 M,会严重影响同事 clone 仓库的效率。
BFG Repo-Cleaner 瘦身
BFG 是专门为删除 Git 历史大文件设计的工具(比 git filter-branch 快 10-1000 倍)。
# 1. 下载 BFG(jar 包)
$ wget -O bfg.jar https://repo1.maven.org/maven2/com/madisp/bfg/1.14.0/bfg-1.14.0.jar
# 2. 备份当前仓库
$ cp -r my-repo my-repo-backup
# 3. 删除大于 100M 的文件
$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo
# 4. 进入仓库清理
$ cd my-repo
$ git reflog expire --expire=now --all
$ git gc --prune=now --aggressive
清理后的 .git 大小:

最终占用 188K:

替代工具:git filter-repo
新版 Git 推荐用 git filter-repo(Python 工具)替代 BFG:
# 安装
$ pip install git-filter-repo
# 删除所有超过 5M 的 blob
$ git filter-repo --strip-blobs-bigger-than 5M
# 只保留指定目录
$ git filter-repo --path src/
⚠️ 重要:filter-repo / BFG 都是改写历史的操作,已经 push 给同事的提交会被重写为新 hash。需要所有协作者重新 clone 或
git fetch + git reset --hard origin/main同步。
高级工具
git cherry-pick:拣选单个提交
把某个分支上的一个(或几个)commit 单独应用到当前分支:
$ git cherry-pick <commit-hash> # 拣选单个 commit
$ git cherry-pick <hash1> <hash2> # 拣选多个
$ git cherry-pick <hash1>..<hash3> # 拣选一段区间
典型场景:
- 修复了一个 bug,想把这次 commit 应用到 main 和 release 分支,不需要整个分支合并。
- 某个 feature 分支中间有一个小修复,但整个分支还没完成不能合并。
拣选会生成新的 commit(hash 不同),不要在已经推送的公共分支上频繁 cherry-pick,会让历史变复杂。
git bisect:二分查找定位问题 commit
当某次提交引入了一个 bug,可以用 git bisect 二分定位到具体哪个 commit:
$ git bisect start
$ git bisect bad # 当前 commit 是有问题的(bad)
$ git bisect good <good-hash> # 某个之前没问题的 commit(good)
# Git 自动 checkout 一个中间 commit
# 你编译/测试,如果是坏的:
$ git bisect bad
# 如果是好的:
$ git bisect good
# 重复直到 Git 找到第一个 bad commit(引入 bug 的 commit)
# 退出 bisect
$ git bisect reset
自动化:
git bisect run <script>让脚本自动判断好坏(比如跑测试),全程不需要人工干预。
Git 系列到这里就完整覆盖了日常工作所需的全部命令与场景。掌握这 6 篇内容,远程分支切错、误提交大文件、合并冲突、撤销误操作这些高频踩坑场景都能从容应对。
下一篇:Git 知识系列(七):Submodule 操作指南,讲 submodule 的添加、初始化、更新与两层提交规则。
再下一篇:Git 知识系列(八):多 Remote 双向同步,讲 Fork + upstream 双远程配置,在维护自己版本的同时同步上游更新。