場景化學習git
Linux 之父的第二件作品—— git 自從誕生後就改變了軟體生產和協作的面貌,gitlab、github、bitbucket、 gitbook、gerrit 等專案的出現都極大地豐富了現代化軟體工程實踐。業界介紹 git 各種用法的書已經是汗牛充棟,本文的特色在於通過問答化場景來串聯 git 實踐中的常用功能及背後原理。
如果你期望讓自己的 git 學習之旅更加有趣、記憶深刻,可以通過克隆樣例倉庫並 checkout 到每一節的 tag 來和我一起學習:
git clone git@github.com:faywong/master_git_by_scenes.git
cd master_git_by_scenes
git checkout {s1-s5} # check out 每一節標題中的 s1-s5 作為你練手的起點
重寫提交記錄(tag: s1)
- 場景:小明剛修改完一個 bug, commit 完才想起忘記寫明修復的 bug 的 url
- 解決方案:
git commit —amend
可以重寫最近一次提交的提交記錄 - 輸出:
重寫前:
commit 0bed9c296fc253b06b77faf3a6ce481389a0b736
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:13:16 2017 +0800
s1:01: fix bug #101
通過git commit --amend
重寫後:
commit fef0d8f4ac2fc0caccd65439bcae5154a94a46a3
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:13:16 2017 +0800
s1:01: fix bug #101, bug url: http://faywong.com/bugs/101
有心的童鞋一定發現 commit sha 值發生了變化,那這裡引申出了一個注意點:
git 中一個 commit 的 sha 值由提交記錄、提交者、提交日期、創作者、創作日期、提交時的檔案樹、父提交 生成,這些因素中任意一項發生改變,都會導致 sha 發生變化。其中父提交這個因素要注意下,它在後文場景”該用 rebase 還是 merge 同步程式碼”中也是重要的一個因子。
注:以上重寫提交記錄的技巧也可以用來解決 git user.email 不符合團隊規定時導致提交被拒的問題。
找回丟失的提交(tag:s2)
- 場景:小明剛為 test.c 新增了一個函式 some_func,結果在一次錯誤的 git reset HEAD^ —hard 後不小心弄丟了這個提交,想要找回先前的提交
- 解決方案:
git reflog
可以讓你回溯最近的 git 操作記錄,從而找回丟失的提交 -
輸出:
sid-macbookpro:master_git_by_scenes faywong$ git reflog fef0d8f HEAD@{0}: reset: moving to HEAD^ 29c8ead HEAD@{1}: commit: s2:01: add some_func function fef0d8f HEAD@{2}: commit (amend): s1:01: fix bug #101, bug url: http://faywong.com/bugs/101 0bed9c2 HEAD@{3}: commit (amend): s1:01: fix bug #101 4ca0df1 HEAD@{4}: commit: bugfix: fix bug #101 220e514 HEAD@{5}: reset: moving to HEAD^ c040ff7 HEAD@{6}: commit: bugfix: fix bug #101 220e514 HEAD@{7}: clone: from git@github.com:faywong/master_git_by_scenes.git
可見29c8ead
便是小明想要恢復的提交。到底如何恢復呢,請接著看下一節
注:git reflog 依賴於 git 倉庫的 gc 狀況,並不一定總是能幫上忙,所以無論何時丟失後及時 git reflog 都是明智之舉。
追加單個提交(tag:s3)
- 場景:小明想要追加
已存在
但並不在當前工作分支上的提交29c8ead
- 解決方案:
git cherry-pick {target_commit}
即可將target-commit
追加到當前分支 -
輸出:
sid-macbookpro:master_git_by_scenes faywong$ git cherry-pick 29c8ead [master 6b108a7] s2:01: add some_func function Date: Thu Mar 2 19:31:30 2017 +0800 1 file changed, 5 insertions(+) sid-macbookpro:master_git_by_scenes faywong$ git log commit 6b108a7e584abf3fc9fea6fe3d9715caae0535bb Author: faywong <philip584521@gmail.com> Date: Thu Mar 2 19:31:30 2017 +0800 s2:01: add some_func function ......
該用 rebase 還是 merge 同步程式碼(tag:s4)
- 場景:小明所在的組,有 2 位其他同學和他一起開發一個 c 語言專案,有的同學喜歡在從 master 分支同步程式碼時用 master,有的同學喜歡用 rebase,這讓小明很痛苦,莫衷一是,到底應該如何抉擇呢?
- 解決方案:
讓我們先來看下 rebase vs merge 各自對程式碼 commit 鏈的影響:
### rebase 前 master 分支的狀態
commit 8f919e54656f6b3e42932db689b7f7358555777f
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:50:49 2017 +0800
s4:03:新增來自master分支的修改
commit f2c45e9fcd9715324ac576a862dc8ef08160b0b2
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:31:30 2017 +0800
s2:01: add some_func function
commit fef0d8f4ac2fc0caccd65439bcae5154a94a46a3
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:13:16 2017 +0800
s1:01: fix bug #101, bug url: http://faywong.com/bugs/101
commit 220e514ebe5fa1d74cd322e1d13f37bde30e4f09
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 18:59:32 2017 +0800
Initial commit
### rebase 前 base 分支的狀態
git log base
sid-macbookpro:master_git_by_scenes faywong$ git log
commit 0a3b7ab9c04387a5f8ac69a44415de87d5077efb
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 20:02:17 2017 +0800
s4:02:來自base分支的修改——新增檔案test2.c
commit 2f8f3fc27eb938a63b1c7caae73a28e555f18310
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:55:32 2017 +0800
s4:01:來自base分支的修改——新增檔案test1.c
commit f2c45e9fcd9715324ac576a862dc8ef08160b0b2
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:31:30 2017 +0800
s2:01: add some_func function
commit fef0d8f4ac2fc0caccd65439bcae5154a94a46a3
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:13:16 2017 +0800
s1:01: fix bug #101, bug url: http://faywong.com/bugs/101
commit 220e514ebe5fa1d74cd322e1d13f37bde30e4f09
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 18:59:32 2017 +0800
Initial commit
### 進行 rebase 操作:
站在 master 分支 rebase base 分支
git checkout master
git rebase base
### rebase 後 master 分支的狀態
commit 345447ca7f65c4f134bb2496092e4881cec2c66e
Author: faywong <philip584521@gmail.com> ### 這是來自 master 分支的修改 03
Date: Thu Mar 2 19:50:49 2017 +0800
s4:03:新增來自master分支的修改
commit 0a3b7ab9c04387a5f8ac69a44415de87d5077efb ### 這是來自 base 分支的修改 02
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 20:02:17 2017 +0800
s4:02:來自base分支的修改——新增檔案test2.c
commit 2f8f3fc27eb938a63b1c7caae73a28e555f18310 ### 這是來自 base 分支的修改 01
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:55:32 2017 +0800
s4:01:來自base分支的修改——新增檔案test1.c
commit f2c45e9fcd9715324ac576a862dc8ef08160b0b2 ### 分支 master & base 的共同祖先
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:31:30 2017 +0800
s2:01: add some_func function
commit fef0d8f4ac2fc0caccd65439bcae5154a94a46a3
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:13:16 2017 +0800
s1:01: fix bug #101, bug url: http://faywong.com/bugs/101
commit 220e514ebe5fa1d74cd322e1d13f37bde30e4f09
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 18:59:32 2017 +0800
Initial commit
可見在分支 master 上 rebase 分支 base 的實際操作是:
- 分從 master 分支的 HEAD(最新的一個提交)和 base 分支的 HEAD(最新的一個提交)各自往 parent 方向回溯找到兩者的共同 parent commit: f2c45e9fcd9715324ac576a862dc8ef08160b0b2
- 將當前程式碼回撤到 f2c45e9fcd9715324ac576a862dc8ef08160b0b2
- 應用共同 parent commit 之後 base 分支的改動:2f8f3fc27eb938a63b1c7caae73a28e555f18310 和0a3b7ab9c04387a5f8ac69a44415de87d5077efb
- 應用共同 parent commit 之後 master 分支的改動:345447ca7f65c4f134bb2496092e4881cec2c66e
其中 2、3、4 步是一個三方合併過程。
同時也可以看出 master 分支上的修改:s4:03:新增來自master分支的修改 對應的 sha 由先前的:
8f919e54656f6b3e42932db689b7f7358555777f 變成了 345447ca7f65c4f134bb2496092e4881cec2c66e,這是由於它的父提交發生了改變導致的。
對於 git 來說,只認識這些生硬的 sha 值,並不知道真正的修改是否完全一樣,而這一點需要人來辨別。
所以可以看出 rebase 別的分支會導致本分支上在中間強行插入一些 commit 從而提交記錄不是在尾部單向追加,本分支上尾部的 commit 的 sha 值會“被動”改變。基於這個特徵,可以總結出選擇 rebase 還是merge 時的黃金法則
選擇 rebase 或 merge 的黃金法則
- 上游分支合併下游分支用 merge
- 下游分支頻繁同步(比如每天,分支間差異較小)上游分支改動用 rebase
- 下游分支不頻繁同步(比如一年,分支間差異極大)上游分支改動用 merge
- 作為團隊工作起點的主幹分支,不要 rebase 其他分支
壓縮一系列提交(tag:s5)
- 場景:近期進修過《程式設計師的自我修養》後的小明最近在修復一個極其頑固的線上閃退,結果一共提交了 3 次才 code review 通過,小明想將這三個 commit 合併為一個 commit,因為這個三個 commit 邏輯上屬於一個 commit。這該怎麼實現呢?
小明的三次提交如下:
commit faf8990aa1c5ffd99b98ad8e44e98385116ee19c
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 20:33:24 2017 +0800
s5:03: 第三次 fix bug #102
commit 3cc7e48c8a38665c81be64c995e79bf4dcad344f
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 20:32:45 2017 +0800
s5:02: 第二次 fix bug #102
commit 129978226e2bc06a6544b12646cd8def834f6959
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 20:31:39 2017 +0800
s5:01: 第一次 fix bug #102
commit 345447ca7f65c4f134bb2496092e4881cec2c66e ### git rebase -i 的起點
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:50:49 2017 +0800
s4:03:新增來自master分支的修改
- 解決方案:git rebase -i {起點}
起點選擇你想修改的最老的(時間上先)提交的 parent。在小明的場景下就是 345447ca7f65c4f134bb2496092e4881cec2c66e
-
命令:
git rebase -i 345447ca7f65c4f134bb2496092e4881cec2c66e
-
輸出(git editor 以 vim 為例):
pick 1299782 s5:01: 第一次 fix bug #102 pick 3cc7e48 s5:02: 第二次 fix bug #102 pick faf8990 s5:03: 第三次 fix bug #102 # Rebase 345447c..faf8990 onto 345447c (3 command(s)) # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit`s log message # x, exec = run command (the rest of the line) using shell # d, drop = remove commit #
此時我們將第二次,第三次 commit 前的 pick 改為 s 或 squash,意思即為將該 commit 壓縮至前一個提交。
改完,鍵入:wq儲存並退出當前編輯,會來到下一個編輯最終提交記錄的介面:
# This is a combination of 3 commits.
# The first commit`s message is:
s5:01: 第一次 fix bug #102
# This is the 2nd commit message:
s5:02: 第二次 fix bug #102
# This is the 3rd commit message:
s5:03: 第三次 fix bug #102
我們將它改為:
s5:04: fix bug #102
最終的提交記錄變成了:
commit 16d0f0ae8d9a3f1f27115c7640f823976d58ea99 ### 之前的三個提交壓縮成了這個提交
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 20:31:39 2017 +0800
s5:04: fix bug #102
commit 345447ca7f65c4f134bb2496092e4881cec2c66e
Author: faywong <philip584521@gmail.com>
Date: Thu Mar 2 19:50:49 2017 +0800
s4:03:新增來自master分支的修改
相關文章
- git常用使用場景Git
- zookeeper學習03 使用場景
- WEBGL學習【十二】滑鼠操作場景Web
- git常用命令和場景Git
- 效能最佳化之通俗易懂學習requestAnimationFrame和使用場景舉例requestAnimationFrame
- git常用命令與使用場景Git
- git學習Git
- v-model 使用場景和原始碼學習原始碼
- git學習之git rebaseGit
- git分支管理和工作流規範:不同場景細化和演示Git
- SharePreference原始碼學習和多程式的場景原始碼
- 從 0 開始學習 Three.js : 場景搭建JS
- Java學習之反射機制及應用場景Java反射
- git的學習Git
- Git學習整理Git
- Git 學習一Git
- git學習地址Git
- git學習心得Git
- Git學習1:初識GitGit
- 【git學習一】git的原理Git
- DeepMind開源強化學習環境,場景靈活介面簡單,推特高贊強化學習
- git工作常用命令列應用場景Git命令列
- 多模態學習,帶來AI全新應用場景?AI
- AI落地場景探索:醫學報告單結構化AI
- 【git學習五】git基礎之git分支Git
- 類的初始化場景
- PostgreSQL秒殺場景優化SQL優化
- GIT學習筆記一git initGit筆記
- Git簡單學習Git
- Git的初步學習Git
- Git&SourceTree學習Git
- git 學習筆錄Git
- git worktree學習Git
- git學習筆記Git筆記
- 學習Source Generators之瞭解Source Generators的應用場景
- 工作中使用 Git 解決問題的場景Git
- git(一):瞭解、學習、安裝gitGit
- Git學習3 --- Git命令列基本操作Git命令列