强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Git 完全指南 / 17 - 排错:冲突解决、损坏修复、大仓库优化

第十七章:排错

即使是最有经验的开发者也会遇到 Git 问题。本章是你的"急救手册"。


17.1 合并冲突深入

17.1.1 冲突类型

内容冲突(Content Conflict)

两个分支修改了同一文件的同一区域:

<<<<<<< HEAD
const config = {
    apiUrl: "https://api.production.com",
    timeout: 5000
};
=======
const config = {
    apiUrl: "https://api.staging.com",
    timeout: 10000
};
>>>>>>> feature

解决方法:

// 合并两者的最佳方案
const config = {
    apiUrl: "https://api.production.com",
    timeout: 10000  // 使用更长的超时
};

删除/修改冲突

一个分支删除了文件,另一个修改了它:

$ git merge feature
CONFLICT (modify/delete): src/legacy.js deleted in feature and modified in HEAD.

解决方法:

# 保留文件
$ git checkout HEAD -- src/legacy.js
$ git add src/legacy.js

# 删除文件
$ git rm src/legacy.js

重命名冲突

两个分支都重命名了同一文件:

CONFLICT (rename/rename): src/old.js renamed in both sides.

17.1.2 高级冲突解决

使用 diff3 风格(推荐)

# 启用 diff3 风格,显示共同祖先
$ git config --global merge.conflictstyle diff3
<<<<<<< HEAD
const timeout = 5000;
||||||| merged common ancestor
const timeout = 3000;
=======
const timeout = 10000;
>>>>>>> feature
// diff3 风格可以看到共同祖先是 3000,HEAD 改成了 5000,feature 改成了 10000

使用 rerere(Reuse Recorded Resolution)

# 启用 rerere,Git 会记住冲突解决方案
$ git config --global rerere.enabled true

# 以后遇到相同的冲突,Git 会自动应用之前的手动解决方案

批量解决冲突

# 接受所有当前分支的修改
$ git checkout --ours .

# 接受所有被合并分支的修改
$ git checkout --theirs .

# 对特定文件
$ git checkout --ours src/config.js
$ git checkout --theirs src/utils.js

# 使用合并工具
$ git mergetool

17.1.3 冲突预防策略

策略说明
频繁同步主分支git pull --rebasegit rebase main
小批量提交每次提交只做一件事
代码分区不同开发者负责不同模块
及时合并 PRPR 存活时间越长,冲突概率越大
通信协调修改共享文件前通知团队

17.2 仓库损坏修复

17.2.1 仓库完整性检查

# 检查仓库完整性
$ git fsck
Checking object directories: 100% (256/256), done.
Checking objects: 100% (1000/1000), done.

# 检查并显示详细信息
$ git fsck --full
$ git fsck --verbose

# 检查悬空对象(dangling objects)
$ git fsck --dangling
$ git fsck --unreachable

17.2.2 常见损坏问题

损坏的提交对象

# 错误信息
error: object file .git/objects/ab/cdef1234... is empty
fatal: loose object abc1234... is corrupt

# 修复步骤
# 1. 尝试从备份恢复
$ cp /backup/.git/objects/ab/cdef1234* .git/objects/ab/

# 2. 如果有远程仓库,重新克隆
$ git clone --mirror git@github.com:user/repo.git

损坏的 index 文件

# 错误信息
fatal: index file corrupt

# 修复方法
$ rm -f .git/index
$ git reset

# 或
$ git read-tree HEAD

损坏的 HEAD 引用

# 错误信息
fatal: bad default HEAD

# 修复方法
$ cat .git/HEAD
# 如果内容损坏
$ echo "ref: refs/heads/main" > .git/HEAD

17.2.3 恢复丢失的对象

# 1. 查找悬空对象
$ git fsck --unreachable | grep commit
unreachable commit abc1234...

# 2. 查看对象内容
$ git cat-file -p abc1234
tree def5678...
parent ghi9012...
author John <john@email.com> 1705123456 +0800
committer John <john@email.com> 1705123456 +0800

Some commit message

# 3. 创建分支引用
$ git branch recovered abc1234

# 4. 查看对象详情
$ git show abc1234

17.3 大仓库优化

17.3.1 浅克隆

# 只克隆最近一次提交
$ git clone --depth 1 https://github.com/user/repo.git

# 克隆最近 N 次提交
$ git clone --depth 100 https://github.com/user/repo.git

# 后续加深历史
$ git fetch --deepen=100

# 获取完整历史
$ git fetch --unshallow

17.3.2 Partial Clone(部分克隆)

# 只克隆 commit 历史,不下载 blob 对象
$ git clone --filter=blob:none https://github.com/user/repo.git

# 不下载大于指定大小的 blob
$ git clone --filter=blob:limit=1m https://github.com/user/repo.git

# 不下载树对象(最轻量级)
$ git clone --filter=tree:0 https://github.com/user/repo.git

# 按需下载 blob
$ git config remote.origin.promisor true
$ git config remote.origin.partialclonefilter "blob:limit=1m"

17.3.3 Sparse Checkout(稀疏检出)

# 初始化稀疏检出
$ git clone --no-checkout https://github.com/user/repo.git
$ cd repo
$ git sparse-checkout init --cone

# 设置要检出的目录
$ git sparse-checkout set src/api src/shared libs/core

# 检出
$ git checkout main

# 添加更多目录
$ git sparse-checkout add tests/unit

# 查看当前稀疏检出配置
$ git sparse-checkout list

# 禁用稀疏检出(检出所有文件)
$ git sparse-checkout disable

17.3.4 Git GC 优化

# 垃圾回收
$ git gc

# 激进 GC(更彻底但更慢)
$ git gc --aggressive

# 只做增量 repack
$ git gc --incremental

# 清理无用对象
$ git gc --prune=now

# 查看仓库大小
$ git count-objects -vH
count: 150
size: 2.50 MiB
in-pack: 5000
packs: 1
size-pack: 150.00 MiB
garbage: 0
size-garbage: 0 bytes

17.3.5 大文件优化

# 查找大文件
$ git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  sed -n 's/^blob //p' | \
  sort -rnk2 | \
  head -20

# 使用 BFG Repo-Cleaner 清理大文件
$ java -jar bfg.jar --strip-blobs-bigger-than 10M repo.git

# 使用 git filter-repo 清理历史
$ git filter-repo --strip-blobs-bigger-than 10M

17.4 常见错误排查

错误信息原因解决方案
fatal: not a git repository不在 Git 仓库目录cd 到正确目录或 git init
fatal: remote origin already exists已配置 origingit remote set-url origin <url>
error: failed to push some refs远程有本地没有的提交git pull --rebase 后再 push
fatal: refusing to merge unrelated histories仓库历史不相关git merge --allow-unrelated-histories
error: pathspec 'xxx' did not match文件名或分支名错误检查拼写,使用 Tab 补全
fatal: You have not concluded your merge上次合并未完成git merge --abort 或解决冲突
error: Entry 'xxx' not uptodate有未提交的修改git stashgit commit
fatal: unable to access SSLSSL 证书问题git config --global http.sslVerify false

17.5 数据恢复

17.5.1 恢复删除的分支

# 查找分支的最后提交
$ git reflog | grep "feature-branch"
abc1234 HEAD@{5}: checkout: moving from feature-branch to main

# 恢复分支
$ git branch feature-branch abc1234

17.5.2 恢复被 amend 覆盖的提交

# 误用了 --amend
$ git commit --amend -m "wrong message"

# 找到原始提交
$ git reflog
abc1234 HEAD@{1}: commit: original message

# 恢复
$ git reset --soft abc1234

17.5.3 恢复被 reset 删除的提交

# 危险操作:git reset --hard HEAD~3
# 找到原始 HEAD
$ git reflog
abc1234 HEAD@{0}: reset: moving to HEAD~3
def5678 HEAD@{1}: commit: last good commit

# 恢复
$ git reset --hard def5678

业务场景

场景推荐方案
合并冲突diff3 风格 + rerere + mergetool
误删分支/提交git reflog 恢复
大仓库克隆慢shallow clone + partial clone + sparse checkout
仓库文件过大git filter-repo + Git LFS
CI/CD 环境优化--depth 1 + --filter=blob:none
多人冲突频繁频繁 rebase + 小批量 PR

扩展阅读


🔗 上一章16 - GitLab 工作流 | 下一章18 - 最佳实践