codecamp

起底Git Git进阶

这是起底Git系列的第六篇,本篇我们来介绍一下Git的进阶技巧。

  • 选择版本
  • 搜索调试
  • 重写历史
  • 重置揭秘
  • 高级合并

选择版本

假设当前版本库如下图所示,有时我们可能先找到当前提交的父提交和祖先提交,^和~可以满足我们的需求

^和~都匹配当前提交的父提交,^^和~~匹配父提交的父提交,^和~后面跟数字的时候意义是不同的,具体可以看下面的例子

$ git log HEAD^ 
A2
$ git log HEAD^^ 
A1
$ git log HEAD^2 
B1
$ git log HEAD~ 
A2
$ git log HEAD~~
A1
$ git log HEAD~2 
A1

有时候我们可能会想选择一个区间,比如A1,A2,A3,下面通过例子说明..,…和^的区别

$ git log master..test
C0 C1
$ git log ^master test
C0 C1
$ git log master…test
A1 A2 A3 C0 C1

搜索调试

A:设想这样一种情况,摸个分支test,开发完后被删除了,怎么找回这个分支呢?

其实git会在本地记录每次HEAD的变化,通过reflog命令可以拿到这些记录

$ git reflog
0e94c5b HEAD@{0}: commit: 222
7e07aa7 HEAD@{1}: commit: 111
c5aba97 HEAD@{2}: commit: 000

比如111是test分支最后一个提交,我们可以去111这个提交,然后在新建一个分支就ok了

$ git checkout 7e07aa7 # 或者git checkout HEAD@{1}
$ git checkout -b test

B:设想这样一种情况,某天你突然发现某行代码写错了,你想快速找到这个bug的始作俑者?

blame可以快速显示文件的每一行最后一次修改是谁

$ git blame README.md
f6ffa8f4 (yanhaijing 2016-08-03 19:54:42 +0800 1) 123
f6ffa8f4 (yanhaijing 2016-08-03 19:54:42 +0800 1) 456

C:设想这样一种情况,你想在Git的某个历史提交中进行搜索?

grep只能搜索工作目录,git grep可以在指定提交中进行搜索

$ git grep yanhaijing HEAD~27 fis-conf.js
HEAD~27:fis-conf.js: * @author yanhaijing.com

D:设想这样一种情况,你想在Git的整个历史中进行搜索?git log可以实现这个功能

$ git log -Syanhaijing --oneline
0a191c message aaa

E:设想这样一种情况,某一天你突然发现线上代码挂了,但你找不到原因,你想快速找到是哪一个版本引入的bug?

git bisect是一个非常有用的调试工具,它通过自动进行一个二分查找来找到哪一个特定的提交是导致 bug 或者问题的第一个提交

$ git bisect start # 开始
$ git bisect bad # 标记为好的
$ git bisect good # 标记为坏的
$ git bisect reset # 结束

重写历史

假设你提交完后发现忘记了一些东西,打算更改上次提交,在git中可以使用追加提交,假设现在仓库状态如下所示

修改完后可以再次提交

$ git add .
$ git commit --amend

就可以修改上次提交,需要注意的是上一次提交并没有被删除,只是没有分支引用,变成了游离状态,在未来的某个时间会被git自动回收

如果你进行了几次提交后后悔了,想重写之前的好几次提交,那就只能用rebase了,假设目前状态如下

假设你想重写A1和A2

$ git rebase -i HEAD~2

需要注意的是已经push到远端的提交,就不要再重写了,不然世界人民会恨你,因为你需要git push -f

重置揭秘

重置有两种方法,reset和checkout,这两个方法非常容易混淆,两个命令都分为全局模式和文件模式

reset全局模式可以用下图总结

reset的文件模式可以覆盖索引区,一个副作用就是用来取消暂存

git reset xxx – file

checkout的全局模式可以用下图总结

checkout的文件模式会覆盖索引区和工作区,可以用来丢弃修改,属于不可逆转的操作

git checkout xxx – file

其实下图就总结两个命令的区别

高级合并

合并分支时,很多人非常害怕遇到冲突,其实冲突并不可怕

A:git默认的冲突显示包括our和their,如下所示

xxx

如果想得到和svn那样包含base+our+their的代码,可以检出冲突

$ git checkout --conflict=diff3 hello.rb

B:如果在冲突时想想svn一样得到,base+our+their三个文件的代码

$ git show :1:xxx > xxx.base
$ git show :2:xxx > xxx.our
$ git show :3:xxx > xxx.their

C:合并冲突一团乱麻,想撤销合并

$ git merge --abort

D:合并后后悔了?想撤消合并?分为两种情况

假如还没推送到远端,可以reset掉

$ git reset --hard HEAD~

如果已经推动到远端,可以用revert

$ git revert -m 1 HEAD

总结

如果你有任何疑问的话,欢迎留言讨论;如果本系列文章对你有帮助的话,那我很荣幸,别忘了打赏哦,O(∩_∩)O哈哈~

最后感谢你的阅读,O(∩_∩)O哈哈~

继续学习

起底Git Git基础
起底Git Git开发流程
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }