工作中使用 Git 解決問題的場景

山頭人漢波 發表於 2022-07-07
Git

簡單來說,就這七點:

  • 使用 git rebase 讓提交記錄更加清晰可讀
  • 使用 git reflog + git reset 跳到任意 commit
  • 使用 git cherry-pick 獲取指定的 commit
  • 使用 git commit --amend 更改提交內容
  • 使用 git revert 回滾某次的提交
  • 使用 git stash 來暫存檔案
  • 配置 git alias 提升工作效率

使用 git rebase 讓提交記錄更加清晰可讀

rebase 基本用法

rebase 翻譯為變基,它的作用和 merge 相似,用於把一個分支的修改合併到當前分支

如下圖所示,經過 rebase 後提交歷史的變化情況

rebase

不明白單分支的好處,可以在看看知乎的這個問題:Git commits歷史是如何做到如此清爽的?

Vue 的作者尤雨溪就是說:多用 rebase

具體用法:

  • 基於 master 分支建立 feature 分支
  • 在 feature 分支上開發功能點
  • master 上也提交了commit
  • 在 feature 分支上執行 git rebase master,意為以 master 分支最後的提交作為基點,逐個應用 feature 的每個更改

git rebase VS git merge

合併分支有兩種,即 rebase 、merge

merge 翻譯為合併,即 git merge branchname,即合併分支程式碼,這種方法會儲存每次 commit 的,當你使用 gitk 檢視時就發現好幾條顏色的線

另一種是 rebase,即去除一系列的提交記錄,“複製”它們,然後在另一個地方逐個放下去

所以 rebase 的優勢就明瞭了,它能創造更清晰的提交記錄

但 merge 會保留你所有的 commit 的歷史時間,當開發人員一多,歷史記錄就會變得混亂

rebase 的互動模式

在開發中,通常會在一個分支上產生很多無效的提交,這種情況下使用 rebase 的互動模式可以把多次 commit 壓縮成一次提交,得到一個乾淨的提交歷史

# 先看提交
git log 
# f9f6f3b commit 3
# 2feb45f commit 2
# 07a3cb6 commit 1
# 我們要修改 2 的話,rebase 到它的下一個 commit,這裡是 1
git rebase 07a3cb6 -i
# 然後在開啟的對話方塊裡面修改,之後還要一個 rebase continue
git rebase -i <base-commit>
# 或者是 git rebase -i HEAD~2 對最近的兩次 commit 進行合併    
也有人稱之為後悔藥功能,即你無論寫什麼 commit,最後都可以修改,無論提交什麼,都可以合併,DIY性強

使用 git reflog + git reset 跳到任意 commit

換個說法叫時光機,即通過查詢所有分支的所有操作記錄(包括已經被刪除的 commit 記錄和 reset 的操作),通過 reset HEAD 跳到指定 commit

git reflog
#afa2f45 [email protected]{10}: checkout: moving from 今天 to 明天
#4abcda5 [email protected]{11}: commit: 打通1800處仙竅
#de42069 [email protected]{12}: commit: 真言輪經大成
git reset [email protected]{10}
# 或者 git reset --hard afa2f45

如此一來,就回到了 afa2f45 commit 處,熟悉「時間法則」、「時光機」的人都知道,這是回到過去

使用 git cherry-pick 獲取指定的 commit

意為“挑揀”提交,和 merge 合併一個分支的所有提交不同,它會獲取某個分支的單個提交,並作為一個新的提交接入到當前分支上

這個需要故事背景才容易理解

張三在分支上開發功能,每個功能點提交一次commit,共六個提交六個功能點(分別是 feature1~feature6),再回到第一個提交點,即他使用 git reset --hard feature1 跳轉第一個 commit,在此基礎上開發一個新功能,即 feature7,那麼如果把 feature7 合併到 feature6 上怎麼做?

git reflog
# git reflog 檢視所有分支的所有操作記錄(包括已經被刪除的 commit 記錄和 reset 的操作)
# 找到 feature7 的 commit 4c97ff3
# 回到 feature6 的 commit cd52afc
git reset --hard cd52afc
# 使用 cherry-pick 拿到 feature7 的程式碼
git cherry-pick 4c97ff3

具體可看小蝌蚪的這篇 小蝌蚪傳記:git時光穿梭機--女神的側顏 來體會一二

簡單來說,你的每一次 commit,就是一次記錄,可以合併到任意地方。所以開發功能點或者修復bug之類,儘量做到一個功能點一個commit,方便出錯時挑揀程式碼

使用 git commit --amend 更改提交內容

amend 的意思是修正

# 繼續改動你的檔案
git add . 
git commit --amend --no-edit
# 你這次的改動會被新增進最近一次的 commit 中

合併到上次的commit 中

git commit --amend:彈出讓你修改內容

git commit --amend --no-edit:保持上一次的commit內容

PS:假如你的程式碼已經 push 了的話,要慎用,因為會修改提交歷史。

使用 git revert 回滾某次的提交

上文提到一個回滾操作:git reset --hard xxx,能回到某次的 commit,除此之外,還有一種則是能撤銷某次 commit

# 先找到你想撤銷的那個 commit hash值
git log
git revert <commit-id>

這種做法會新建一條commit 資訊,來撤回之前的修改。

而 git reset 會直接提交記錄退回到指定的 commit 上。

所以就個人開發或個人 feature 分支而言,可以使用 git reset 來回滾程式碼,但在多人協作的整合分支上,git revert 更適合。這樣,提交的歷史記錄不會被抹去,可以安全地進行撤回

使用 git stash 來暫存檔案

顧名思義,就是把本地的改動暫存起來

先了解下 git 的四大工作區域

四大工作區域

Git工作區

  • Workspace(工作區):本地電腦所見的檔案和目錄
  • Index/Stage(暫存區):一般存放在 .git 目錄下,當你 git add 改動檔案,改動的檔案就放入在「暫存區」
  • Respository(本地倉庫):當你 git clone 地址,就將遠端倉庫克隆到本地倉庫。它是存在本地的版本庫,其中HEAD指向最新放入倉庫的版本。當你執行 git commit,檔案改動就到本地倉庫
  • Remote(遠端倉庫):類似Github、Gitlab、碼雲等放在程式碼託管平臺

常見的場景是你還在開發一個功能點的時候,突然有個線上 bug 需要你緊急修復,這次你可以git commit 提交到本地倉庫,後續通過 git commit --amend 繼續在原 commit 上修改內容。但這裡還有一種方法,即將程式碼存在暫存區,等 bug 修復完後,再從暫存區取出

基本命令如下:

git stash # 將本地的改動暫存
git stash save "message" # 執行儲存時,新增備註
git stash pop # 應用最近一次暫存,並刪除暫存記錄
git stash apply #恢復最近的儲存,但不會把儲存從儲存列表中刪除,某人使用第一個儲存,即 [email protected]{0},如果要使用其他,git stash apply [email protected]{$num}
git stash list # 檢視 stash 了哪些儲存
git stash clear #刪除所有快取的 stash
git ls-files --stage #檢視 index 暫存區

配置 git alias 提升工作效率

主要是為了簡化命令,它的基本用法是 git config --global alias.<簡化的字元> 原始命令

如下面的例子:

git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch

當然,另一種方法是在 .gitconfig 檔案中設定

[alias]
st = status -sb
co = checkout
br = branch
mg = merge
ci = commit
ds = diff --staged
dt = difftool
mt = mergetool
last = log -1 HEAD
latest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"
ls = log --pretty=format:\"%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green)[%cn]\" --decorate --date=short
hist = log --pretty=format:\"%C(yellow)%h %C(red)%d %C(reset)%s %C(green)[%an] %C(blue)%ad\" --topo-order --graph --date=short
type = cat-file -t
dump = cat-file -p
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
參考政採雲的配置

除此之外

還有一些不常見卻好用的命令

  • gitk:開啟git的圖形化工具
  • gitjk:撤消您剛剛在git中所做的操作
  • git help -g:展示幫助資訊
  • cat .git/HEAD:檢視分支檔案
  • git fetch --all && git reset --hard origin/master:回到遠端倉庫的狀態

    • 拋棄本地所有的修改,回到遠端倉庫的狀態
  • git push -f origin master:強行獲取遠端最新程式碼

參考資料