Git 實用小抄#
Git - the stupid content tracker.
在專案開發過程中,Git 是非常得力的版本控制管理工具。它使得開發者可以不分時間地點,高效協作。本文簡要說明 Git 的基本用法,並介紹提高效率的小 tips,以及 Git 工作流分支管理。
常用 Git 命令#
git status#
使用git status
查看緩衝區(index)和工作目錄(workspace)的狀態,你可以在這裡看到:
- 當前分支
- 未跟蹤的文件
- 已修改的文件
- 已暫存的文件
- 附加信息
勤用git status
能幫助你隨時掌握倉庫狀態。
git add#
使用git add
命令將指定文件提交到緩衝區(index)。建議搭配git status
,謹慎查看需要添加至緩衝區的修改,以防將不在 commit log 描述範圍內,或未被.gitignore
文件忽略的文件提交上去了。
git commit#
使用git commit
將修改提交到本地倉庫(repository)。Git 強制需要給每個 commit 添加描述說明。如果當你提交後,猛然一拍大腿,驚呼 “我提交錯了!”,這時還可以使用git reset
來恢復,或使用git commit --amend
來修改最新的提交。如果你不在乎新提交一個記錄,那麼也可以使用git revert
生成一個新的 commit 來撤銷你的更改。git commit
的信息應該規範填寫,最好一看 commit log 便知道這個 commit 做了何種修改。
git commit 規範#
規範要求 commit log 的意義在於可追溯專案歷史記錄,以及更新代碼時用最少的成本獲取提交信息 —— 這個更新做了哪些更改。在一個 commit log 裡盡量通過三個維度描述一個 commit:type(類型)、scope(範圍)、subject(簡介)。
type(必選項)#
type 代表一個 commit 的提交類型。後續追溯歷史記錄時,先通過 type 進行篩選,可以節約不少時間。
type(必須) | 英文 | 說明 |
---|---|---|
feat | feature | 新增功能 |
fix | fix | 修復缺陷 |
docs | documents | 文檔更新 |
style | style | 代碼格式 |
refactor | refactor | 代碼重構 |
perf | performance | 性能提升 |
test | test | 測試相關 |
build | build | 構建相關 |
ci | continuous integration | 持續集成 |
revert | revert | 回退代碼 |
chore | chore | 其他修改 |
scope(可選項)#
scope 代表一個 commit 修改的範圍,比如視圖層、控制層,模型層等等。
subject(必選項)#
subject 代表一個 commit 的說明,最好不超過 50 字。說明應該準確描述這次修改,方便追溯。為了達成目的,提交的顆粒度可以小一點,後續可以通過git rebase
合併多次提交,整理提交記錄。
好的歷史記錄,看著賞心悅目。其實更建議中國人寫 subject 用中文。。。
壞的歷史記錄,讓人眼前一黑。但人家是大佬,他這樣寫一定是有他的理由的(确信)
git pull#
使用git pull
將遠端更改同步到本地倉庫。建議時常pull
一下,保證自己正在修改的是最新的分支。如果你有未commit
的更改,則 git pull
命令的合併部分將失敗,而你的本地分支將保持不變。因此,在從遠程倉庫中提取新提交之前,你應該始終在分支中提交你的更改。
要完全理解git pull
的工作模式,我們需要了解兩個命令:git fetch
和git merge
git fetch#
使用git fetch
來更新倉庫中所有遠程倉庫的跟蹤分支。實際上沒有任何更改反映在任何本地工作分支上。這意味著只是你的本地倉庫感知到了遠端倉庫的更新,但還沒有做同步。IDEA
有自動fetch
的相關插件,建議安裝以及時感知到分支更新。
git merge#
使用git merge target-branch
將目標分支合併到當前分支上。merge
的合併策略有兩種:fast-forward 和三方合併:
- Fast-forward 合併 如果在兩個分支之間沒有任何變更,則 Git 會直接將目標分支指向源分支的提交對象,這就是所謂的 Fast-forward 合併。該操作不會創建新的提交對象,因為早期的提交已經包含了所有的變更。
-
三方合併 如果兩個分支之間存在變更衝突,則需要進行三方合併。在三方合併中,Git 會創建一個新的提交對象,該對象包含兩個分支之間的共同點和每個分支相對於共同點的變更。Git 還會嘗試將衝突解決為一致的變更。
git push#
使用git push
將修改上傳至遠程倉庫(remote)。建議時不時pull
一下代碼。
其他實用 Git 命令#
git diff#
使用git diff
查看工作空間還未被添加至緩衝區的修改;使用git diff --cached
查看緩衝區尚未被提交的修改;使用git diff branch1 branch2
查看兩個分支間的差異。
git stash#
git stash
系列命令帶來了一個新的區域 —— 暫存區(stash entry)。使用git stash push
將你尚未提交的修改壓進暫存區,你的工作空間和緩衝區將恢復成最新 commit 的修改。使用git stash pop
彈出暫存區栈頂的修改。使用git stash list
查看暫存區保存的修改列表。
git stash
經常用來提示不能 pull 的情況時,這通常代表工作空間或緩衝區還有修改沒有提交。這時可以先將修改壓進暫存區,恢復工作空間和緩衝區,待 pull 操作完成後,再彈出修改。
git stash
也很適合切換分支。例如當我們在自己負責的 feature 分支開發時,線上突然有 bug 需要你搞定,你需要立即切換分支到 master 新建 hotfix 分支修復。可你當前的修改還未提交,因為功能尚未完成,也不能提交。這時就可以使用git stash
命令將修改壓進暫存區,切換分支進行開發,完事再回到壓入暫存區時的分支彈出修改即可。
git rebase#
git rebase
系列命令使我們有機會重新整理我們的提交,使歷史記錄乾淨清爽,非常易於追溯歷史。對此,Vue 作者尤雨溪曾在知乎上提到:
攻擊性還是很強啊 2333
git rebase target-branch
的本質是:找到當前分支與目標分支的共同祖先,先將我當前分支的修改 “拋到一邊”,使我的當前分支與目標分支的歷史提交記錄一致,再將 “拋到一邊” 的 commit 應用回當前分支。這種合併分支的策略與git merge
的區別是:
git merge
會留下無用的 commit log 信息,污染歷史記錄。git rebase
為了整理歷史記錄,理所當然會修改當前分支的 commit 。所以在一條分支上,當你 rebase 後,這條分支的順序已經和之前不同了,倘若此時別人也在這條分支上,當他 push 時,可能會遇到錯誤,甚至可能會丟失更改。
所以建議git rebase
只用來修改還未 push 到遠端倉庫的 commit,或在只有你自己使用的分支上應用。
當然git rebase
除了拿來合併分支,還可以合併多個 commit 。我們使用git rebase -i [startpoint] [endpoint]
通過選項-i
—— 即--interactive
開啟交互式編輯界面。這裡[startpoint] [endpoint]
是一個左開右閉的區間,必須指明[startpoint]
,默認[endpoint]
為HEAD
指向的 commit 。例如我們通過git rebase -i HEAD~3
來編輯最新的 3 次提交。
pick 54f88ff 第一次提交
pick 41346f3 第二次提交
pick 3b9307e 第三次提交
# 變基 edb3a72..3b9307e 到 edb3a72(3 個提交)
#
# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但編輯提交說明
# e, edit <提交> = 使用提交,但停止以便修補提交
# s, squash <提交> = 使用提交,但擠壓到前一個提交
# f, fixup [-C | -c] <提交> = 類似於 "squash",但只保留前一個提交
# 的提交說明,除非使用了 -C 參數,此情況下則只
# 保留本提交說明。使用 -c 和 -C 類似,但會打開
# 編輯器修改提交說明
# x, exec <命令> = 使用 shell 運行命令(此行剩餘部分)
# b, break = 在此處停止(使用 'git rebase --continue' 繼續變基)
# d, drop <提交> = 刪除提交
# l, label <label> = 為當前 HEAD 打上標記
# t, reset <label> = 重置 HEAD 到該標記
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . 創建一個合併提交,並使用原始的合併提交說明(如果沒有指定
# . 原始提交,使用註釋部分的 oneline 作為提交說明)。使用
# . -c <提交> 可以編輯提交說明。
# u, update-ref <引用> = 為引用 <ref> 設置一個佔位符,以將該引用更新為此處的新提交。
# 此 <引用> 在變基結束後更新。
#
# 可以對這些行重新排序,將從上至下執行。
#
# 如果您在這裡刪除一行,對應的提交將會丟失。
#
# 然而,如果您刪除全部內容,變基操作將會終止。
註釋中詳細解釋了可用的命令。在此場景中,假設我們希望合併這三個 commit,我們如此修改:
r 54f88ff 第一次提交
f 41346f3 第二次提交
f 3b9307e 第三次提交
接下來我們修改 commit log 信息:
重複向控制台輸出“HelloWorld”
# 請為您的變更輸入提交說明。以 '#' 開始的行將被忽略,而一個空的提交
# 說明將會終止提交。
#
# 日期: Sat Mar 25 11:28:26 2023 +0800
#
# 交互式變基操作正在進行中;至 edb3a72
# 最後完成的命令(1 條命令被執行):
# reword 54f88ff 第一次提交
# 接下來要執行的命令(剩餘 2 條命令):
# fixup 41346f3 第二次提交
# fixup 3b9307e 第三次提交
# 您在執行將分支 'master' 變基到 'edb3a72' 的操作時編輯提交。
#
# 要提交的變更:
# 修改: HelloWorld.java
完成修改後查看git log
此時歷史記錄已成功合併:
通過git rebase
命令,我們可以在將 commit
push 上遠端倉庫前,對本地倉庫的 commit 進行編輯、重組、合併、編排,來獲得乾淨整潔,合理顆粒度的歷史記錄。切忌修改已經 push 過遠端倉庫的 commit !!!
git cherry pick#
使用git cherry pick
命令,你可以明確將某幾個 commit 應用到當前分支上。例如現在分支情況為:
現在我想直接將這個 commit 應用到 main 分支上。輸入:
git cherry pick C2(這個 commit 的 Hash 值)
這就已經生效了,這會在 main 分支上新建一個 commit
C2'。這兩個 commit 內容一樣,但 Hash 值是不一樣的。
Git 工作流#
最後簡要描述下 Git 工作流,Git 工作流工作流展示了一個專案為了隔離不同狀態的代碼,需要通過多種不同種類的分支配合,達成快速迭代的目的。目前流行的分支模型有特性分支開發模式、主幹開發模式等。
特新分支開發模式#
- 分支管理複雜;
- 開發周期相對較長;
- 視時長,與 master 分支的差距會越來越大,最終可能會導致難以解決衝突甚至不可能解決衝突。
主幹開發模式#
- 分支管理簡單;
- 開發周期可以很短,隨時都能簽出新版本的分支;
- 代碼質量要求高。
最後#
- 掌握 Git 工具,幫助提高開發效率和協作效率;
- 常用的幾大命令:
add
,commit
,push
,pull
,status
應該熟練掌握,並可通過[option]
可選項進行功能的增強; - commit log 規範是為了方便追溯歷史,以及記錄專案變更,更重要的是,可以讓人通過查看歷史記錄快速了解專案;
- 常 pull。經常 pull 保證你是在最新分支上修改,這可以通過頻繁 commit 保證。而最後 push 時,應該考慮是否可以通過 rebase 整理歷史記錄後再 push 上遠端倉庫;
- 使用 stash 暫存區保證緊急切換代碼時忘了 commit 導致可能的代碼污染;
- 根據實際使用的分支模型。一旦決定,就應該積極貫徹,否則由於破窗效應,良好的分支管理將被打破,專案質量與迭代速度也將得不到保障。