Git实战进阶教程

语言: CN / TW / HK

Git的功能是对文件做版本管理,其底层原理是一个对文件存取的系统,要想掌握Git,最重要的是理解commit tree,也就是提交记录历史,平时开发中我们都是通过分支来更新commit tree,Git的基础就是要学会分支操作,推荐阅读 图解Git分支和命令

在日常开发中,只掌握分支操作是不够的,还需要通过各种命令操作commit tree,本文我们选择5个典型场景,介绍一下 Git 的进阶技巧,如下所示,学会这些技巧,可以极大提高工作中Git使用的效率:

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

选择版本

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

^和~都匹配当前提交的父提交,^和~匹配父提交的父提交,^和~后面跟数字的时候意义是不同的,具体可以看下面的例子,假设有如下图所示的提交树:

image.png

如下的命令,可以看到^和~的区别:

bash $ 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,下面通过例子说明..,...和^的区别

bash $ 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 命令可以拿到这些记录

bash $ git reflog 0e94c5b [email protected]{0}: commit: 222 7e07aa7 [email protected]{1}: commit: 111 c5aba97 [email protected]{2}: commit: 000

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

bash $ git checkout 7e07aa7 # 或者git checkout [email protected]{1} $ git checkout -b test

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

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

bash $ 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

blame 时可以指定范围

bash $ git blame -L10,15 README.md # 查看10-15行的信息

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

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

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

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

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

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

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

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

重写历史

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

image.png 修改完后可以再次提交

bash $ git add . $ git commit --amend

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

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

image.png 假设你想重写 A1 和 A2

bash $ git rebase -i HEAD~2

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

image.png

重置揭秘

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

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

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

bash git reset xxx – file

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

image.png

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

bash git checkout xxx – file

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

image.png

高级合并

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

A:git 默认的冲突显示包括 our 和 their,如果想得到和 svn 那样包含 base+our+their 的代码,可以检出冲突

bash $ git checkout --conflict=diff3 hello.rb

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

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

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

bash $ git merge --abort

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

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

bash $ git reset --hard HEAD~

image.png

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

bash $ git revert -m 1 HEAD

image.png

总结

欢迎大家阅读本文,如果你觉得本文对你有帮助,那就点赞加关注作者吧,如果对本文有任何疑问,欢迎在评论区交流。

原创不易,求分享、求一键三连

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿