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 drop 或 git 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 | 网络问题 | 检查网络、代理设置 |
小结
本章我们学习了:
- 常见错误解决:提交、分支、合并、远程、撤销等问题
- 诊断命令:status、log、reflog、fsck
- 数据恢复流程:使用 reflog 和 fsck 恢复丢失数据
- 预防措施:良好习惯、备份、理解命令影响
遇到问题时,首先保持冷静,使用诊断命令了解情况,然后选择合适的解决方案。