跳至正文
来两杯美式
返回

Git 知识系列(六):仓库维护与排错

By 来两杯美式
发布于

常见错误: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> 恢复到 HEAD
  • git checkout <hash> -- <file> 恢复到 指定历史版本

.git 文件夹瘦身

由于 Git 是分布式版本控制工具,每个开发者通过 git clone 在本地机器上拷贝一个完整的 Git 仓库。随着项目年龄增长,.git 文件会越来越大。尤其是当 commit 了大文件(jar 包、打好的 war 包、音频视频等),哪怕后续已经把文件从项目里删除,Git 为了保证能随时还原历史,依旧在日志中保存这些大文件的二进制流,加重 .git 负担。

案例:误提交大文件

初始大小 180K:

项目初始大小

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

提交 jar 包后占用 8.6M

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

提交后占用 16M

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

删除后依旧占用 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 大小:

bfg 清理结果

最终占用 188K:

瘦身完成后 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>           # 拣选一段区间

典型场景:

拣选会生成新的 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 双远程配置,在维护自己版本的同时同步上游更新。


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
所属专题
Git
第 6 / 8 篇
查看系列全部文章
  1. 01.Git 知识系列(一):安装与基础配置
  2. 02.Git 知识系列(二):日常工作流
  3. 03.Git 知识系列(三):撤销与回退
  4. 04.Git 知识系列(四):分支管理与远程协作
  5. 05.Git 知识系列(五):标签、忽略与子模块
  6. 06.Git 知识系列(六):仓库维护与排错
  7. 07.Git 知识系列(七):Submodule 操作指南
  8. 08.Git 知识系列(八):多 Remote 双向同步

上一篇
Maven 入门到精通:依赖管理、生命周期与多模块项目
下一篇
Git 知识系列(五):标签、忽略与子模块