日常add、commit、push三连确实够了。但总有那么几次,你会遇到:
- 合并冲突不知道怎么解
- commit写错了想改
- 把代码搞乱了想回退
- 想从别的分支偷一个提交过来
这篇把这些场景的处理方法都写一遍,下次遇到直接查。
合并冲突
两个人改了同一个文件的同一个地方,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--ourssrc/config.js# 用我的gitcheckout--theirssrc/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--stagedsrc/config.js# 从暂存区撤出commit了还没push
# 撤销commit,代码还在gitreset HEAD~1# 撤销commit,代码也不要了(危险)gitreset--hardHEAD~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-iHEAD~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--globalalias.st statusgitconfig--globalalias.co checkoutgitconfig--globalalias.br branchgitconfig--globalalias.lg"log --oneline --graph --all"git lg看提交历史特别爽。
清理本地分支
时间长了本地会有很多已经合并的分支:
# 删除已合并的分支gitbranch--merged|grep-v"main\|develop"|xargsgitbranch-d# 清理远程已删除的追踪分支gitfetch--prune这些操作平时可能用不到,但真遇到问题的时候能救命。建议收藏,到时候直接搜。