前言
日常add、commit、push三连够用,但总有些场景超出基础操作范围:
- 合并冲突不知道怎么解
- commit信息写错了想改
- 代码搞乱了想回退
- 需要从别的分支复制某个提交
这篇整理Git进阶操作,覆盖这些场景的处理方法。
一、合并冲突
两个人改了同一个文件的同一个地方,Git不知道该保留哪个,就产生冲突了。
gitmerge feature# CONFLICT (content): Merge conflict in src/config.js打开文件,会看到这样的标记:
<<<<<<<HEADconstAPI='https://prod.api.com';=======constAPI='https://test.api.com';>>>>>>>feature<<<<<<<到=======是你当前分支的代码,=======到>>>>>>>是要合并进来的代码。
手动改成你想要的样子(把那些标记删掉):
constAPI=process.env.API_URL||'https://prod.api.com';然后:
gitaddsrc/config.jsgitcommit搞定。
偷懒的办法:如果你确定要用其中一边的:
gitcheckout --ours src/config.js# 用我的gitcheckout --theirs src/config.js# 用他的不想合并了:
gitmerge --abort回到合并前的状态。
二、修改提交
commit message写错了:
gitcommit --amend -m"正确的提交信息"忘了加某个文件:
gitaddforgotten.jsgitcommit --amend --no-edit注意:如果已经push了,amend之后要push --force,这个在团队项目里慎用。
三、撤销操作
分情况处理:
改了文件还没add
gitrestore src/config.js# 恢复单个文件gitrestore.# 恢复所有add了还没commit
gitrestore --staged src/config.js# 从暂存区撤出commit了还没push
# 撤销commit,代码还在gitreset HEAD~1# 撤销commit,代码也不要了(危险)gitreset --hard HEAD~1push了
这时候不能直接reset了(会影响别人),用revert:
gitrevert abc1234# abc1234是要撤销的commit hash这会创建一个新的commit来撤销之前的改动。
四、找回搞丢的代码
手残reset --hard之后发现代码没了?别慌。
gitreflog这个命令会显示你所有的操作历史:
abc1234 HEAD@{0}: reset: moving to HEAD~1 def5678 HEAD@{1}: commit: 重要的改动找到你想恢复的那个hash:
gitcheckout def5678# 或者gitbranch recover def5678reflog是救命的东西,只要提交过,都能找回来。
五、cherry-pick
从别的分支偷一个commit过来。
场景:在develop修了个bug,想把这个修复也应用到release分支。
# 先在develop上提交gitcheckout developgitcommit -m"fix: 修复登录问题"# 假设hash是abc1234# 然后cherry-pick到releasegitcheckout releasegitcherry-pick abc1234这个命令用得很多,尤其是多版本维护的时候。
六、rebase -i:整理提交历史
代码写完了,但提交历史很乱:
abc1234 Add feature def5678 Fix typo ghi9012 Fix another typo jkl3456 Actually fix it this time提PR之前整理一下:
gitrebase -i HEAD~4# 整理最近4个打开编辑器:
pick abc1234 Add feature pick def5678 Fix typo pick ghi9012 Fix another typo pick jkl3456 Actually fix it this time把那几个typo修复合并到第一个:
pick abc1234 Add feature fixup def5678 Fix typo fixup ghi9012 Fix another typo fixup jkl3456 Actually fix it this timefixup会把这个commit合并到前一个,并且丢弃commit message。
保存退出,4个commit变成1个干净的commit。
七、bisect:二分查找Bug
有个bug,不知道是哪个commit引入的,但你知道v1.0是好的,现在是坏的。
gitbisect startgitbisect bad# 标记当前版本有buggitbisect good v1.0# 标记v1.0是好的然后Git会自动checkout中间的版本,你测试一下:
gitbisect good# 这个版本没问题# 或gitbisect bad# 这个版本有问题几轮之后Git会告诉你是哪个commit引入的bug。
gitbisect reset# 结束如果有自动化测试更方便:
gitbisect start HEAD v1.0gitbisect runnpmtest# 自动跑测试八、stash:临时保存
写代码写到一半,突然要切分支修个紧急bug。
gitstash# 暂存当前修改gitcheckout hotfix# 切分支干活# 干完活...gitcheckout featuregitstash pop# 恢复之前的修改可以加描述,方便找:
gitstash push -m"正在做用户模块"gitstash list# 看列表九、实用配置
推荐配置:
gitconfig --global alias.st statusgitconfig --global alias.co checkoutgitconfig --global alias.br branchgitconfig --global alias.lg"log --oneline --graph --all"git lg看提交历史特别爽。
十、清理本地分支
时间长了本地会有很多已经合并的分支:
# 删除已合并的分支gitbranch --merged|grep-v"main\|develop"|xargsgitbranch -d# 清理远程已删除的追踪分支gitfetch --prune总结
Git进阶操作速查:
| 场景 | 命令 |
|---|---|
| 解决冲突 | 手动编辑 →git add→git commit |
| 修改最近提交 | git commit --amend |
| 撤销未add | git restore |
| 撤销已add | git restore --staged |
| 撤销已commit未push | git reset HEAD~1 |
| 撤销已push | git revert |
| 找回误删代码 | git reflog→git checkout |
| 复制单个提交 | git cherry-pick |
| 整理提交历史 | git rebase -i |
| 二分查Bug | git bisect |
| 临时保存 | git stash |
核心原则:
- 已推送的历史不要用reset,用revert
- reflog是后悔药,只要提交过就能找回
- rebase -i前最好备份分支
有问题评论区交流。