跳到主要内容

Git 故障排除指南

在使用 Git 的过程中,难免会遇到各种问题。本章汇总了常见的问题和解决方案,帮助你快速定位和解决问题。

常见错误及解决方案

提交相关

错误:提交后发现遗漏了文件

问题:提交后发现忘记添加某个文件,或者提交信息写错了。

解决方案

# 方案一:修改最后一次提交(如果还没推送)
git add forgotten-file.txt
git commit --amend --no-edit

# 修改提交信息
git commit --amend -m "新的提交信息"

# 方案二:如果已经推送,创建新提交
git add forgotten-file.txt
git commit -m "添加遗漏的文件"
git push

注意--amend 会改写历史,如果提交已经推送,需要使用 git push --force,这可能影响其他协作者。

错误:提交了敏感信息

问题:不小心提交了密码、密钥等敏感信息。

解决方案

# 方案一:如果还没推送,修改最后一次提交
git rm --cached sensitive-file.txt
echo "sensitive-file.txt" >> .gitignore
git add .gitignore
git commit --amend -m "移除敏感信息"

# 方案二:如果已经推送,使用 BFG 或 git-filter-repo 清理历史
# 安装 BFG: brew install bfg
bfg --delete-files sensitive-file.txt
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --force

# 方案三:使用 git filter-branch(不推荐,较慢)
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch sensitive-file.txt' \
--prune-empty --tag-name-filter cat -- --all
git push --force --all

重要:如果敏感信息已经推送,应立即修改相关密码或密钥,因为即使从 Git 历史中删除,仍可能被他人获取。

错误:提交了不应该提交的文件

问题:提交了 node_modules、.env 等不应该跟踪的文件。

解决方案

# 1. 添加到 .gitignore
echo "node_modules/" >> .gitignore
echo ".env" >> .gitignore

# 2. 从 Git 中移除但保留本地文件
git rm -r --cached node_modules/
git rm --cached .env

# 3. 提交更改
git add .gitignore
git commit -m "移除不应该跟踪的文件"

分支相关

错误:在错误的分支上做了提交

问题:在 main 分支上做了一些开发工作,应该在新分支上。

解决方案

# 1. 创建新分支(保留当前修改)
git checkout -b feature/new-feature

# 2. 切回 main 分支
git checkout main

# 3. 回退 main 分支到正确状态
git reset --hard origin/main

# 或者保留修改在工作区
git reset origin/main

错误:删除了错误的分支

问题:不小心删除了分支,想恢复它。

解决方案

# 方法一:使用 reflog 找回
git reflog

# 找到分支被删除前的提交,例如 abc123
git checkout -b recovered-branch abc123

# 方法二:如果知道分支的最后一个提交
git checkout -b recovered-branch <commit-hash>

错误:分支名拼写错误

问题:创建分支时名字写错了。

解决方案

# 重命名当前分支
git branch -m correct-name

# 重命名指定分支
git branch -m wrong-name correct-name

# 如果已推送,需要删除远程旧分支
git push origin --delete wrong-name
git push origin -u correct-name

合并相关

错误:合并冲突不知道如何解决

问题:合并时出现冲突,不知道如何处理。

解决方案

# 1. 查看冲突文件
git status

# 2. 查看冲突内容
git diff

# 3. 手动编辑冲突文件,选择保留的内容
# 冲突标记:
# <<<<<<< HEAD
# 当前分支的内容
# =======
# 要合并分支的内容
# >>>>>>> feature-branch

# 4. 标记冲突已解决
git add <conflicted-file>

# 5. 完成合并
git commit

# 或者使用合并工具
git mergetool

# 如果想取消合并
git merge --abort

错误:合并后想撤销

问题:合并后发现有问题,想撤销合并。

解决方案

# 如果还没推送,使用 reset
git reset --hard HEAD~1

# 如果已经推送,使用 revert
git revert -m 1 <merge-commit-hash>

# -m 1 表示保留第一个父提交(通常是主分支)

远程相关

错误:推送被拒绝(non-fast-forward)

问题:推送时提示 ! [rejected] main -> main (non-fast-forward)

解决方案

# 原因:远程有新的提交,本地没有

# 方案一:拉取并合并(推荐)
git pull origin main
# 解决可能的冲突
git push origin main

# 方案二:拉取并变基(保持线性历史)
git pull --rebase origin main
# 解决可能的冲突
git rebase --continue
git push origin main

# 方案三:强制推送(危险!只在确定必要时使用)
git push --force origin main
# 或更安全的强制推送(只有当远程是你预期的状态时才推送)
git push --force-with-lease origin main

错误:无法连接到远程仓库

问题fatal: unable to access 'https://github.com/...'

解决方案

# 检查网络连接
ping github.com

# 检查远程仓库配置
git remote -v

# 更新远程 URL
git remote set-url origin https://github.com/user/repo.git

# 使用 SSH 代替 HTTPS
git remote set-url origin [email protected]:user/repo.git

# 检查 SSH 密钥
ssh -T [email protected]

# 如果使用代理
git config --global http.proxy http://proxy.example.com:8080

错误:认证失败

问题fatal: Authentication failed for 'https://github.com/...'

解决方案

# 方案一:使用 SSH 密钥
git remote set-url origin [email protected]:user/repo.git

# 方案二:使用 Personal Access Token(GitHub)
# 1. 在 GitHub 生成 Token(Settings -> Developer settings -> Personal access tokens)
# 2. 推送时使用 Token 作为密码
git push
# Username: your-username
# Password: your-personal-access-token

# 方案三:配置凭证存储
git config --global credential.helper store
# 下次输入一次凭证后会保存

# macOS 使用钥匙串
git config --global credential.helper osxkeychain

# Windows 使用凭证管理器
git config --global credential.helper manager

撤销相关

错误:误执行了 git reset --hard

问题:执行 git reset --hard 后发现丢失了重要修改。

解决方案

# 使用 reflog 恢复
git reflog

# 找到 reset 之前的提交,例如 HEAD@{1}
git reset --hard HEAD@{1}

# 或者创建新分支保存
git branch recovered HEAD@{1}

错误:误删除了未提交的修改

问题:执行 git checkout .git restore . 后发现删除了未保存的工作。

解决方案

# 很遗憾,未提交的修改一旦被撤销,通常无法恢复
# 但可以尝试:

# 1. 检查 IDE 的本地历史(VS Code、IntelliJ 等有此功能)

# 2. 检查是否有 stash
git stash list

# 3. 检查编辑器的备份文件(如 .swp、.swo)

# 预防:养成经常提交的习惯,或使用 git stash 保存工作进度

错误:误删除了 stash

问题:执行 git stash dropgit stash clear 后想恢复。

解决方案

# 查找悬空提交
git fsck --no-reflogs --unreachable | grep commit

# 找到后恢复
git show <commit-hash> # 检查是否是你要的内容
git stash store <commit-hash>

其他常见问题

错误:仓库体积过大

问题:仓库克隆很慢,占用空间很大。

解决方案

# 查看仓库大小
git count-objects -vH

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

# 清理不需要的大文件(使用 BFG 或 git-filter-repo)
# 安装 git-filter-repo: pip install git-filter-repo
git filter-repo --path large-file.bin --invert-paths

# 清理后执行垃圾回收
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# 浅克隆(只克隆最近的提交)
git clone --depth 1 https://github.com/user/repo.git

错误:换行符问题(CRLF/LF)

问题:Windows 和 Unix 系统之间换行符不一致,导致每次都显示文件有修改。

解决方案

# Windows 用户设置
git config --global core.autocrlf true

# Linux/macOS 用户设置
git config --global core.autocrlf input

# 使用 .gitattributes 强制指定
echo "* text=auto" > .gitattributes
echo "*.sh text eol=lf" >> .gitattributes
echo "*.bat text eol=crlf" >> .gitattributes

# 重新规范化所有文件
git add --renormalize .
git commit -m "规范化换行符"

错误:文件名大小写问题

问题:在大小写不敏感的系统(Windows、macOS)上修改文件名大小写后,Git 没有检测到变化。

解决方案

# 配置 Git 区分大小写
git config core.ignorecase false

# 正确重命名文件
git mv oldname.txt NewName.txt

# 或者使用两步重命名
git mv file.txt temp.txt
git mv temp.txt File.txt

错误:detached HEAD 状态

问题You are in 'detached HEAD' state

解决方案

# 这通常发生在检出特定提交、标签或远程分支时
# Git 不在任何分支上,新提交会丢失

# 如果需要保存修改:
# 1. 创建新分支
git checkout -b new-branch-name

# 2. 或者回到之前的分支
git checkout main

# 如果已经在 detached HEAD 做了提交,想保存它们:
git checkout -b save-my-work

诊断命令

查看仓库状态

# 查看详细状态
git status

# 查看简短状态
git status -s

# 查看被忽略的文件
git status --ignored

查看提交历史

# 查看最近的历史
git log --oneline -10

# 查看所有分支的历史
git log --all --oneline --graph

# 查看特定文件的历史
git log --follow -- filename.txt

# 查看某次提交的详情
git show <commit-hash>

查看引用日志

# 查看 HEAD 的变化历史
git reflog

# 查看特定分支的历史
git reflog show branch-name

# 查看所有引用的变化
git reflog --all

检查仓库完整性

# 检查对象完整性
git fsck

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

# 检查悬空对象
git fsck --unreachable

查看配置

# 查看所有配置
git config --list

# 查看配置来源
git config --list --show-origin

# 查看特定配置
git config user.name

数据恢复流程

完整恢复流程

当你不小心丢失了提交或分支时,按以下步骤尝试恢复:

# 1. 停止任何操作,避免覆盖数据

# 2. 查看 reflog
git reflog --all

# 3. 找到丢失前的提交
# 记录提交哈希,例如 abc123

# 4. 恢复提交
# 创建新分支保存
git branch recovered-branch abc123

# 或直接重置
git reset --hard abc123

# 5. 如果 reflog 没有,尝试 fsck
git fsck --lost-found

# 查找悬空提交
git fsck --no-reflogs --unreachable | grep commit

# 6. 验证找到的提交
git show <found-hash>

# 7. 恢复数据
git branch recovered <found-hash>

预防措施

1. 养成良好的提交习惯

# 小步提交,每个提交只做一件事
git add -p # 交互式选择要提交的内容

# 写清晰的提交信息
git commit # 让编辑器打开,写详细说明

# 提交前检查
git diff --staged

2. 重要操作前先备份

# 创建备份分支
git branch backup-branch

# 或创建标签
git tag backup-tag

# 或使用 stash
git stash push -m "工作进度备份"

3. 理解命令的影响

命令影响范围是否可恢复
git add只影响暂存区是,git restore --staged
git commit创建新提交是,git reset
git reset --soft移动分支指针是,git reflog
git reset --hard丢弃工作区和暂存区部分,未提交的修改无法恢复
git push --force覆盖远程历史影响所有协作者
git clean -fd删除未跟踪文件
git gc --prune=now删除悬空对象

4. 配置保护性钩子

# 防止推送到 main 分支
# .git/hooks/pre-push
#!/bin/bash
protected_branch='main'
current_branch=$(git branch --show-current)
if [ "$current_branch" = "$protected_branch" ]; then
echo "禁止直接推送到 $protected_branch 分支"
exit 1
fi

错误代码速查

错误信息原因解决方案
non-fast-forward远程有新提交git pull 再推送
authentication failed认证失败检查凭证、使用 SSH 或 Token
detached HEAD不在任何分支创建新分支保存修改
merge conflict合并冲突手动解决冲突后提交
pathspec ... did not match文件或分支不存在检查拼写、确认存在
not a git repository不在 Git 仓库中初始化仓库或进入正确目录
permission denied权限不足检查文件权限、仓库权限
repository not found仓库不存在检查 URL、权限
unable to access网络问题检查网络、代理设置

小结

本章我们学习了:

  1. 常见错误解决:提交、分支、合并、远程、撤销等问题
  2. 诊断命令:status、log、reflog、fsck
  3. 数据恢复流程:使用 reflog 和 fsck 恢复丢失数据
  4. 预防措施:良好习惯、备份、理解命令影响

遇到问题时,首先保持冷静,使用诊断命令了解情况,然后选择合适的解决方案。

参考资料