Git实战宝典:从翻车现场到绝地求生
学了前四期,你已经掌握了Git的理论和最佳实践。但现实世界不是童话,翻车才是常态。
这一期,我们直面那些让新手崩溃、让老手头秃的真实场景。每个问题都有救,只要你知道正确的方法。
紧急救援手册
场景1:commit后发现写错了
情况A:还没push
# 修改最后一次提交
git commit --amend -m "正确的提交信息"
# 或者补充文件
git add forgotten-file.txt
git commit --amend --no-edit
情况B:已经push了
# 如果只有你在这个分支工作
git commit --amend -m "正确的提交信息"
git push --force-with-lease
# 如果有别人也在用这个分支,不要用amend!
# 而是创建新的修复提交
git revert HEAD
git push
场景2:提交到错误的分支
# 你在main分支提交了,但应该在feature分支
# 1. 撤销当前提交,保留改动
git reset HEAD~1 --soft
# 2. 切换到正确的分支
git checkout feature-branch
# 3. 重新提交
git commit -m "feat: xxx"
场景3:需要撤回多个提交
# 撤回最近3个提交,保留改动在工作区
git reset HEAD~3
# 撤回最近3个提交,保留改动在暂存区
git reset HEAD~3 --soft
# 撤回最近3个提交,丢弃所有改动(危险!)
git reset HEAD~3 --hard
场景4:误删分支
# 查找被删分支的最后一个提交
git reflog
# 找到类似这样的记录
# abc1234 HEAD@{5}: checkout: moving from feature-xxx to main
# 重建分支
git checkout -b feature-xxx abc1234
场景5:误删文件
# 删除了工作区文件,想恢复
git checkout -- deleted-file.txt
# 或
git restore deleted-file.txt
# 删除了文件并已提交
git checkout HEAD~1 -- deleted-file.txt
git commit -m "restore: 恢复误删文件"
场景6:需要撤销已push的提交
# 方法1:revert(推荐,不改写历史)
git revert
git push
# 方法2:reset + force push(危险,仅限个人分支)
git reset --hard
git push --force-with-lease
revert创建一个新提交来撤销改动,不改写历史,适合已push的提交。reset回退指针,会改写历史。
场景7:merge出错想重来
# 合并过程中发现冲突太多,想放弃
git merge --abort
# 已经合并完成但想撤销
git reset --hard HEAD~1
场景8:需要合并特定的几个提交
# 方法1:cherry-pick
git cherry-pick
# 方法2:交互式rebase
git rebase -i
# 在编辑器中把不需要的提交标记为drop
历史改写
修改历史提交信息
# 修改最近3个提交的信息
git rebase -i HEAD~3
# 把要修改的提交前面的pick改成reword
# 保存后Git会逐个让你编辑提交信息
合并历史提交
git rebase -i HEAD~3
# 把后面几个提交的pick改成squash
# 保存后编辑合并后的提交信息
删除历史提交
git rebase -i HEAD~5
# 把要删除的提交改成drop
从历史中删除敏感文件
# 使用BFG Repo-Cleaner(快)
bfg --delete-files secrets.yml
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force
# 使用git filter-repo(现代推荐)
pip install git-filter-repo
git filter-repo --path secrets.yml --invert-paths
# 使用git filter-branch(慢,已废弃)
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch secrets.yml' \
--prune-empty --tag-name-filter cat -- --all
重要:改写历史后通知所有协作者重新clone!
大文件处理
问题:仓库太大
# 查看哪些文件占用空间
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
sed -n 's/^blob //p' | \
sort --numeric-sort --key=2 | \
tail -20
解决方案1:Git LFS
# 安装Git LFS
brew install git-lfs # Mac
apt install git-lfs # Ubuntu
# 初始化
git lfs install
# 跟踪大文件
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/**"
# 查看跟踪规则
git lfs track
# 提交跟踪规则
git add .gitattributes
git commit -m "chore: 配置Git LFS"
解决方案2:清理历史
# 从历史中彻底删除大文件
git filter-repo --path huge-file.zip --invert-paths
# 清理垃圾
git reflog expire --expire=now --all
git gc --prune=now --aggressive
性能优化
clone太慢
# 浅克隆(只克隆最近的历史)
git clone --depth 1 https://github.com/user/repo.git
# 单分支克隆
git clone --single-branch --branch main https://github.com/user/repo.git
# 后续获取完整历史
git fetch --unshallow
仓库太大
# 清理无用对象
git gc
# 更激进的清理
git gc --aggressive --prune=now
# 清理reflog
git reflog expire --expire=now --all
git gc --prune=now
status太慢
# 禁用文件监控(大仓库)
git config core.fsmonitor false
git config core.untrackedCache false
子模块
添加子模块
git submodule add https://github.com/user/lib.git libs/lib
克隆包含子模块的仓库
# 方法1:克隆时自动初始化
git clone --recursive https://github.com/user/repo.git
# 方法2:克隆后手动初始化
git clone https://github.com/user/repo.git
cd repo
git submodule init
git submodule update
更新子模块
# 更新到最新
git submodule update --remote
# 更新所有子模块
git submodule foreach git pull origin main
删除子模块
# 删除子模块(Git 1.8.3+)
git submodule deinit libs/lib
git rm libs/lib
rm -rf .git/modules/libs/lib
常见错误
error: Your local changes would be overwritten
# 情况1:想保留本地修改
git stash
git pull
git stash pop
# 情况2:丢弃本地修改
git reset --hard
git pull
fatal: refusing to merge unrelated histories
# 两个仓库历史不相关,需要强制合并
git pull origin main --allow-unrelated-histories
! [rejected] main -> main (non-fast-forward)
# 远程有新提交,先拉取
git pull --rebase origin main
git push origin main
fatal: Authentication failed
# 检查SSH密钥
ssh -T git@github.com
# 检查远程URL
git remote -v
# 切换到SSH
git remote set-url origin git@github.com:user/repo.git
# 或使用token
git remote set-url origin https://@github.com/user/repo.git
fatal: not a git repository
# 当前目录不在Git仓库中
# 初始化仓库
git init
# 或进入正确的目录
cd /path/to/repo
调试技巧
查看某行代码的修改历史
# 查看文件的每一行是谁在什么时候改的
git blame filename.txt
# 只看某几行
git blame -L 10,20 filename.txt
# 查看某行代码的完整历史
git log -p -S "特定代码" filename.txt
找出引入bug的提交
# 二分查找
git bisect start
git bisect bad # 当前版本有bug
git bisect good v1.0 # v1.0版本正常
# Git会自动跳到中间提交,你测试后标记
git bisect good/bad
# 重复直到找到
git bisect reset
查看reflog(操作日志)
# 查看所有操作记录
git reflog
# 找到误删的提交
git checkout
最佳实践总结
DO
- 频繁提交:小步快跑,每个逻辑改动一个提交
- 写好提交信息:让未来的自己和队友能看懂
- 先pull再push:避免不必要的冲突
- 使用分支:不要在main上直接开发
- 定期备份:重要分支推送到远程
DON’T
- 不要push --force:除非你确定自己在做什么
- 不要commit敏感信息:密码、密钥一旦push就很难彻底清除
- 不要在公共分支rebase:会改写历史,坑队友
- 不要忽略.gitignore:node_modules、.env必须忽略
- 不要提交大文件:使用Git LFS
Git速查表
# 初始化
git init # 初始化仓库
git clone # 克隆仓库
# 日常操作
git status # 查看状态
git add # 添加到暂存区
git commit -m "message" # 提交
git push # 推送
git pull # 拉取
# 分支操作
git branch # 创建分支
git checkout # 切换分支
git checkout -b # 创建并切换
git merge # 合并分支
git branch -d # 删除分支
# 撤销操作
git checkout -- # 撤销工作区修改
git reset HEAD # 撤销暂存
git commit --amend # 修改最后一次提交
git revert # 撤销提交(安全)
git reset --hard # 回退到指定提交(危险)
# 历史查看
git log --oneline --graph # 图形化日志
git blame # 查看每行修改记录
git reflog # 操作历史
# 暂存工作
git stash # 暂存当前工作
git stash pop # 恢复暂存
系列总结
恭喜你完成了整个Git系列!
第一期:Git基础 - 从安装到日常操作
第二期:分支管理 - 在平行宇宙中开发
第三期:团队协作 - Pull Request与代码审查
第四期:自动化 - 钩子、CI/CD与效率工具
第五期:实战宝典 - 从翻车到救命
你现在已经是Git高手了。但记住:Git只是工具,真正重要的是团队协作的规范和习惯。工具再好,用不好也是白搭。
保持学习,保持实践,保持分享。Git的世界还有很多宝藏等你探索。
进阶资源:
- Pro Git官方文档
- GitHub Skills
- Learn Git Branching(可视化学习)
- Oh Shit, Git!?!(救急手册)
恭喜!你已经掌握了Git的全部核心技能。去征服代码世界吧!
Views: 0
