Git 實戰進階教程

Git 的功能是對文件做版本管理,其底層原理是一個對文件存取的系統,要想掌握 Git,最重要的是理解 commit tree,也就是提交記錄歷史,平時開發中我們都是通過分支來更新 commit tree,Git 的基礎就是要學會分支操作,推薦閱讀 《圖解 Git 分支和命令》。

在日常開發中,只掌握分支操作是不夠的,還需要通過各種命令操作 commit tree,本文我們選擇 5 個典型場景,介紹一下 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

blame 時可以指定範圍

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

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,如果想得到和 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

總結

歡迎大家閱讀本文,如果你覺得本文對你有幫助,那就點贊加關注作者吧,如果對本文有任何疑問,歡迎在評論區交流。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/yvjmKs5uu8mn6PHQjBYxkQ