在軟體開發過程中,有效管理和理解 Git 提交歷史至關重要。本文將深入探討如何使用 Git 查詢、簡化和修改提交歷史記錄,涵蓋 git log
、git cherry
、git rebase
、git filter-branch
和 git replace
等關鍵指令,並提供實際案例與注意事項,幫助開發者更有效地管理程式碼版本。透過這些技術,開發者可以更好地追蹤程式碼變更、簡化複雜的提交歷史,並在必要時修改錯誤或敏感資訊,提升團隊協作效率和程式碼函式庫的可維護性。
提交記錄查詢
git log
命令可以用於查詢提交記錄,並且提供了多種選項來自定義輸出。例如,--cherry
選項可以用於查詢重複提交,--notes
選項可以用於顯示提交註解。
提交排序
提交記錄可以按照不同的順序顯示,包括:
--date-order
:按照提交時間排序,先顯示最近的提交。--topo-order
:按照拓撲排序,先顯示父提交,然後顯示子提交。--reverse
:反轉提交順序,先顯示最舊的提交。
提交篩選
git log
命令也提供了多種選項來篩選提交記錄,包括:
--right-only
:只顯示右邊分支的提交。--no-merges
:不顯示合並提交。--cherry-mark
:標記重複提交。
歷史簡化
Git 也提供了多種選項來簡化提交記錄,包括:
--simplify-by-decoration
:簡化裝飾提交。--simplify-merges
:簡化合並提交。
相關命令
git cherry
:查詢重複提交。git notes
:管理提交註解。
Git Cherry
git cherry
命令可以用於查詢重複提交,標記那些已經被上游分支重複的提交。例如:
$ git cherry [-v] [upstream [head [limit]]]
這個命令可以用於查詢哪些提交已經被上游分支重複,並且標記那些重複提交。
Git 版本控制系統:檢視與編輯歷史記錄
Git Cherry 指令
Git 的 cherry
指令用於比較兩個分支之間的差異,特別是在提交記錄方面。它可以顯示哪些提交已經被應用到上游分支(upstream),以及哪些提交尚未被合並。
$ git cherry -v --abbrev
這個指令會顯示出提交記錄的差異,包括哪些提交已經被應用到上游分支,以及哪些提交尚未被合並。 -v
選項可以顯示更多詳細資訊,包括提交的簡短描述。
Git Shortlog 指令
Git 的 shortlog
指令用於總結提交記錄,根據作者對提交進行分組,並且可以應用郵件對映(mailmap)來重寫作者名稱或電子郵件地址。
$ git shortlog
這個指令會顯示出提交記錄的總結,包括作者名稱、提交數量以及提交的簡短描述。這可以幫助您快速瞭解提交記錄的概況。
編輯歷史記錄
在 Git 中,編輯歷史記錄是一個強大的功能,可以讓您修改提交記錄、移動分支、合並或分割儲存函式庫等。然而,這也需要謹慎,因為修改歷史記錄可能會導致提交記錄的不一致。
移動分支
您可以使用 git branch
指令來移動分支。例如,要將目前的分支移動到一個新的位置,您可以使用以下指令:
$ git branch -m oldbranch newbranch
合並儲存函式庫
您可以使用 git merge
指令來合並兩個儲存函式庫。例如,要將另一個儲存函式庫合並到目前的儲存函式庫,您可以使用以下指令:
$ git merge other-repo
系統性修改歷史記錄
您可以使用 git filter-branch
指令來系統性修改歷史記錄。例如,要將所有提交中的某個字串替換為另一個字串,您可以使用以下指令:
$ git filter-branch -f --msg-filter 'sed "s/oldstring/newstring/"' -- --all
這個指令會將所有提交中的 oldstring
替換為 newstring
。
圖表翻譯:
graph LR A[Git Cherry] --> B[Git Shortlog] B --> C[編輯歷史記錄] C --> D[移動分支] C --> E[合並儲存函式庫] C --> F[系統性修改歷史記錄]
這個圖表顯示了 Git 的 cherry
和 shortlog
指令之間的關係,以及編輯歷史記錄的不同方法。
Git 重構:重新定位分支
Git 重構是一種強大的工具,允許您修改提交歷史記錄。其中一個重要的功能是重新定位分支(rebase),它可以將一個分支從一個位置移動到另一個位置。這個過程涉及建立新的提交,具有相同的變更集和中繼資料,例如作者、提交者、時間戳等。
重構步驟
Git 在重構過程中遵循以下步驟:
- 識別要移動的提交:確定哪些提交需要被重新定位。
- 計算變更集:計算每個提交的變更集(patches)。
- 移動 HEAD:將 HEAD 指標移動到新的基礎位置(base)。
- 應用變更集:按照順序應用變更集,建立新的提交,並保留作者資訊。
- 更新分支參照:最終更新分支參照,指向新的提交頂點。
互動式重構
Git 還提供了互動式重構(interactive rebase)的功能,允許您在移動提交的過程中編輯提交。這可以透過 git rebase --interactive
(或 -i
)命令實作。
重構命令
最一般的重構命令是:
$ git rebase [--onto newbase] [upstream] [branch]
這意味著重新播放從 upstream
到 branch
的提交集,從 newbase
開始。預設值為:
upstream
:HEAD@{upstream}
(當前分支的上游,如果有)branch
:HEAD
(當前分支)newbase
:upstream
引數的預設值或使用者提供的值
例子
假設您有一個提交圖,如下所示:
A -- B -- C (master)
\
D -- E -- F (topic)
您可以使用以下命令將 topic
分支重新定位到 master
分支的頂部:
$ git rebase --onto master topic
這將建立新的提交 D'
、E'
和 F'
,具有相同的變更集和中繼資料,如下所示:
A -- B -- C -- D' -- E' -- F' (topic)
\
D -- E -- F (原始 topic 分支)
這個過程將 topic
分支的基礎從 B
改變為 C
,從而重新定位了分支。
預防措施
在使用重構之前,請確保您已經與團隊成員協調,並且所有人都瞭解重構的影響。重構可能會破壞其他人對提交歷史記錄的理解,尤其是如果他們已經根據原始提交進行了工作。因此,請謹慎使用重構,並確保您已經充分理解其後果。
Git 重構與合併:重新定位提交歷史
Git 的重構功能允許您修改提交歷史,從而使您的程式碼函式庫更有條理和易於管理。在這一章中,我們將探討 Git 重構的基本概念和技術,包括變基(rebase)和合併(merge)。
變基(Rebase)
變基是一種用於重新定位提交的技術。它可以幫助您將提交重新排序、合併或刪除,以保持提交歷史的清晰和簡潔。變基的基本步驟包括:
- 查詢要變基的提交範圍。
- 執行
git rebase
命令以啟動變基過程。 - Git 會暫時儲存您要變基的提交,並將分支指標移動到新的基礎提交上。
- 然後,Git 會嘗試將暫存的提交一個一個地應用到新的基礎提交上。
- 如果出現衝突,Git 會暫停變基過程,讓您解決衝突。
- 解決完衝突後,您可以使用
git rebase --continue
命令繼續變基過程。
復原變基
如果您需要復原變基操作,可以使用 git reflog
命令查詢原始提交點。然後,使用 git reset --hard
命令將分支指標重置到原始提交點。
例如,如果您執行了 git rebase
命令並想要復原它,您可以使用以下步驟:
- 執行
git reflog
命令查詢原始提交點。 - 找到原始提交點的雜湊值(例如
e3a1d5b0
)。 - 執行
git reset --hard e3a1d5b0
命令將分支指標重置到原始提交點。
合併(Merge)
合併是另一種用於整合程式碼的技術。它可以幫助您將不同的分支合併成一個單一的分支。合併的基本步驟包括:
- 切換到目標分支。
- 執行
git merge
命令以啟動合併過程。 - Git 會嘗試將來源分支的提交合併到目標分支中。
- 如果出現衝突,Git 會暫停合併過程,讓您解決衝突。
- 解決完衝突後,您可以使用
git commit
命令提交合併結果。
Mermaid 圖表:Git 重構過程
graph TD A[原始提交] --> B[變基過程] B --> C[暫存提交] C --> D[新的基礎提交] D --> E[應用提交] E --> F[解決衝突] F --> G[繼續變基] G --> H[完成變基]
圖表翻譯:
上述 Mermaid 圖表展示了 Git 重構過程的基本步驟。從原始提交開始,Git 會啟動變基過程,暫存提交,並將分支指標移動到新的基礎提交上。然後,Git 會嘗試將暫存的提交一個一個地應用到新的基礎提交上。如果出現衝突,Git 會暫停變基過程,讓您解決衝突。解決完衝突後,您可以繼續變基過程,直到完成變基。
匯入 Git 倉函式庫的歷史記錄
當您想要將一個 Git 倉函式庫(B)中的內容匯入另一個 Git 倉函式庫(A)時,您可以使用 git remote
和 git fetch
命令來完成。這個過程可以保留 B 倉函式庫的歷史記錄和內容。
匯入斷開的歷史記錄
最簡單的方法是將 B 倉函式庫的整個提交圖匯入 A 倉函式庫,而不連線兩個倉函式庫的歷史記錄。這意味著 A 倉函式庫將會有多個根提交(root commit),每個提交都沒有父提交。
以下是匯入 B 倉函式庫到 A 倉函式庫的步驟:
- 切換到 A 倉函式庫:
cd A
- 新增 B 倉函式庫為遠端倉函式庫:
git remote add B URL
- 從 B 倉函式庫抓取提交圖:
git fetch B
- Git 會警告您沒有共同的提交:
warning: no common commits
- 使用
git for-each-ref
命令生成指令碼,建立新的本地分支:git for-each-ref --shell --format='git branch --no-track %(refname:short) %(refname:short)' 'refs/remotes/B/*' | sed -e 's:/:-:' | sh -x
- 刪除遠端倉函式庫 B:
git remote rm B
結果
現在,A 倉函式庫中已經包含了 B 倉函式庫的整個提交圖,包括所有分支和提交記錄。您可以使用 git branch
命令檢視新的分支。
注意
如果您想要連線兩個倉函式庫的歷史記錄,您需要使用其他方法,例如使用 git merge
或 git rebase
命令。
內容解密:
# 切換到 A 倉函式庫
cd A
# 新增 B 債券為遠端倉函式庫
git remote add B URL
# 從 B 債券抓取提交圖
git fetch B
# 使用 git for-each-ref 命令生成指令碼,建立新的本地分支
git for-each-ref --shell \
--format='git branch --no-track %(refname:short) %(refname:short)' \
'refs/remotes/B/*' | sed -e 's:/:-:' | sh -x
# 刪除遠端倉函式庫 B
git remote rm B
這段程式碼使用 git for-each-ref
命令生成指令碼,建立新的本地分支,並使用 sed
命令重寫分支名稱。最終,使用 sh
命令執行生成的指令碼。
圖表翻譯:
graph LR A[A 倉函式庫] -->|新增遠端倉函式庫|> B[B 倉函式庫] B -->|抓取提交圖|> A A -->|生成指令碼|> C[指令碼] C -->|執行指令碼|> D[新的本地分支] D -->|刪除遠端倉函式庫|> E[最終結果]
這個圖表展示了匯入 B 倉函式庫到 A 倉函式庫的過程,包括新增遠端倉函式庫、抓取提交圖、生成指令碼、執行指令碼和刪除遠端倉函式庫。最終結果是 A 倉函式庫中包含了 B 倉函式庫的整個提交圖。
Git 中的倉函式庫合併與歷史修改
Git 是一個強大的版本控制系統,允許使用者管理和合併不同的程式碼倉函式庫。在某些情況下,需要將一個倉函式庫的歷史合併到另一個倉函式庫中。這個過程可以使用 Git 的命令來完成。
合併兩個倉函式庫的歷史
如果需要合併兩個倉函式庫的歷史,可以使用 git fetch
和 git branch
命令。首先,需要將另一個倉函式庫新增為臨時遠端倉函式庫,然後使用 git fetch
將其分支抓取到本地。接著,可以使用 git branch
命令建立新的分支,然後使用 git merge
命令合併兩個分支的歷史。
匯入線性歷史
如果需要匯入線性歷史,可以使用 git format-patch
和 git am
命令。這些命令可以將另一個倉函式庫的提交格式化為補丁,然後應用到當前倉函式庫中。例如,以下命令可以匯入另一個倉函式庫中 foo 分支的歷史:
$ cd A
$ git --git-dir /path/to/B/.git format-patch --root --stdout foo | git am
這個命令會將另一個倉函式庫中 foo 分支的提交格式化為補丁,然後應用到當前倉函式庫中。
匯入非線性歷史
如果需要匯入非線性歷史,可以使用 git rebase
命令。這個命令可以將另一個倉函式庫的提交重新根據當前倉函式庫的提交。例如,以下命令可以匯入另一個倉函式庫中 isis 分支的歷史:
# Add the source repository as a temporary remote
$ git remote add B /path/to/B.git
$ git fetch B
$ git rebase --onto master B/isis
這個命令會將另一個倉函式庫中 isis 分支的提交重新根據當前倉函式庫的 master 分支。
圖表翻譯:
graph LR A[Git Warehouse A] -->| git fetch |> B[Git Warehouse B] B -->| git branch |> C[New Branch] C -->| git merge |> D[Merged History] D -->| git rebase |> E[Rebased History]
這個圖表展示了 Git 中的倉函式庫合併與歷史修改過程。
Git遠端分支合併
當您需要從遠端倉函式庫合併特定分支的變更到您的本地倉函式庫時,Git提供了一系列的命令來幫助您完成這個過程。以下是步驟,示範如何將遠端分支的變更合併到您的本地分支中。
步驟1:新增遠端倉函式庫
首先,您需要將遠端倉函式庫新增到您的本地Git組態中。這可以透過git remote add
命令完成。假設遠端倉函式庫的URL為URL
,您可以使用以下命令:
git remote add temp URL
這裡,temp
是您為遠端倉函式庫指定的名稱。
步驟2:從遠端倉函式庫抓取分支
接下來,您需要從遠端倉函式庫抓取您想要合併的分支。假設您想要抓取的分支名稱為isis
,您可以使用以下命令:
git fetch temp isis
這個命令會從遠端倉函式庫抓取isis
分支的最新版本,並將其儲存在您的本地倉函式庫中。
步驟3:建立本地分支
為了方便管理,您可能想要建立一個新的本地分支來儲存抓取下來的變更。您可以使用以下命令建立一個新的本地分支:
git branch import FETCH_HEAD
這裡,import
是您為新分支指定的名稱,FETCH_HEAD
是Git用於標記最近一次抓取操作的頭部的特殊參照。
步驟4:重演本地分支
現在,您需要將import
分支上的提交重演到您的當前分支上。這可以透過git rebase
命令完成:
git rebase --preserve-merges --root --onto HEAD import
這個命令會將import
分支上的所有提交重演到您的當前分支上,同時保留合並提交。
步驟5:切換到主分支並合併
完成重演後,您需要切換回您的主分支(通常是master
),然後合併import
分支:
git checkout master
git merge import
這會將import
分支上的所有變更合併到您的主分支中。
步驟6:刪除臨時分支和遠端倉函式庫
最後,您可以刪除臨時建立的分支和遠端倉函式庫,因為它們已經完成了任務:
git branch -d import
git remote remove temp
這樣,您就成功地將遠端分支的變更合併到了您的本地倉函式庫中。
圖表翻譯:
以下是上述過程的Mermaid流程圖:
flowchart TD A[新增遠端倉函式庫] --> B[抓取遠端分支] B --> C[建立本地分支] C --> D[重演本地分支] D --> E[切換到主分支並合併] E --> F[刪除臨時分支和遠端倉函式庫]
這個圖表展示了從新增遠端倉函式庫到刪除臨時分支和遠端倉函式庫的整個過程。
Git 歷史編輯:使用 git replace
進行提交手術
在 Git 中,提交(commit)是不可變的,但在某些情況下,您可能需要修改提交歷史。例如,您可能需要更正提交者的名稱或電子郵件地址。這種情況下,您可以使用 git replace
進行提交手術。
提交手術的需求
在進行提交手術之前,您需要了解提交的結構。每個提交都有一個唯一的 ID,稱為 SHA-1 雜湊值。提交也包含了父提交的參照,這使得 Git 能夠建立提交之間的關係。
當您需要修改一個提交時,您不能直接修改它,因為這會影響到後續的提交。相反,您需要建立一個新的提交,然後使用 git replace
將原始提交替換為新的提交。
使用 git replace
進行提交手術
以下是使用 git replace
進行提交手術的步驟:
- 檢查出您想要修改的提交:
git checkout <submit-id>
- 修改提交:
git commit --amend --reset-author -C HEAD
- 建立一個新的提交:
git commit -m "新的提交訊息"
- 使用
git replace
將原始提交替換為新的提交:git replace <original-submit-id> <new-submit-id>
例子
假設您想要修改一個提交的作者名稱。您可以按照以下步驟進行:
$ git log --format='%h %an'
...
0922daf4 Richard E. Silverman
6426690c Richard E. Silverman
03f482d6 Bozo the Clown
27e9535f Richard E. Silverman
78d481d3 Richard E. Silverman
...
您可以看到提交 03f482d6
的作者名稱是錯誤的。您可以按照以下步驟修改它:
$ git checkout 03f482d6
Note: checking out '03f482d6'.
You are in 'detached HEAD' state...
$ git commit --amend --reset-author -C HEAD
[detached HEAD 42627abe] add big red nose
...
現在,您可以使用 git replace
將原始提交替換為新的提交:
$ git replace 03f482d6 42627abe
傳回到原始分支後,您可以看到提交歷史已經被修改:
$ git log --format='%h %an'
...
0922daf4 Richard E. Silverman
6426690c Richard E. Silverman
03f482d6 Richard E. Silverman
27e9535f Richard E. Silverman
78d481d3 Richard E. Silverman
...
Git 歷史編輯
Git 的歷史編輯功能允許使用者修改已經提交的 commit 記錄。其中,git replace
命令可以用來替換特定的 commit 物件,而 git filter-branch
命令則可以用來重寫整個 commit 歷史。
Git Replace
git replace
命令可以用來替換特定的 commit 物件。它會在 refs/replace
名稱空間中建立一個新的參照,該參照指向被替換的 commit 物件。當 Git 操作時,它會檢查替換列表,如果找到匹配的物件,就會默默地替換其內容。
例如,假設我們想要替換一個 commit 的作者姓名,可以使用以下命令:
$ git replace -f <commit_id> <new_commit_id>
這會建立一個新的參照 refs/replace/<commit_id>
,指向新的 commit 物件 <new_commit_id>
。
Git Filter-Branch
git filter-branch
命令可以用來重寫整個 commit 歷史。它會遍歷指定的 commit 範圍(例如,當前分支),並套用使用者提供的過濾器對 commit 進行修改。
過濾器可以用來修改 commit 的內容,例如,修改作者姓名、電子郵件地址等。過濾器的字串引數會被傳遞給 shell,當過濾器執行時,環境變數中會包含以下與 commit 相關的變數:
GIT_COMMIT
(commit ID)GIT_AUTHOR_NAME
GIT_AUTHOR_EMAIL
GIT_AUTHOR_DATE
例如,假設我們想要修改所有 commit 的作者姓名,可以使用以下命令:
$ git filter-branch --commit-callback 'if [ "$GIT_AUTHOR_NAME" = "Old Name" ]; then export GIT_AUTHOR_NAME="New Name"; fi' -- --all
這會遍歷所有 commit,如果作者姓名為 “Old Name”,就會修改為 “New Name”。
工作流程
使用 git replace
和 git filter-branch
的常見工作流程是:
- 使用
git replace
來替換特定的 commit 物件。 - 使用
git filter-branch
來重寫整個 commit 歷史。 - 如果需要,將修改推播到其他倉函式庫。
注意,在使用 git replace
時,應該小心地操作,以免不小心修改了不應該修改的 commit。另外,在使用 git filter-branch
時,也應該小心地操作,以免不小心修改了不應該修改的 commit 或樹狀結構。
注意事項
git replace
隻影響被替換的 commit 物件,而不影響其子 commit。git filter-branch
可以用來修改 commit 的內容,但不會自動 ripple 效果到子 commit。如果需要這樣做,應該使用git rebase -i
來實作。git filter-branch
是一個強大的工具,但也可能導致不可預期的結果,因此應該小心地使用,並在必要時查閱檔案。
GIT filter-branch 指令解析
GIT filter-branch 是一個強大的指令,能夠用於修改 GIT 歷史記錄。它提供了多種過濾器(filter),可以用於修改提交的內容、作者、日期等資訊。
過濾器型別
GIT filter-branch 提供了以下幾種過濾器:
--env-filter
:修改提交的環境變數。--tree-filter
:修改提交的內容。--index-filter
:修改提交的索引。--parent-filter
:修改提交的父節點。--msg-filter
:修改提交的訊息。--commit-filter
:修改提交的過程。--tag-name-filter
:修改標籤的名稱。--subdirectory-filter
:修改提交的目錄結構。
過濾器使用範例
Expunging Files
如果您想要從 GIT 歷史記錄中刪除某些檔案,可以使用 --index-filter
過濾器。例如,以下命令可以刪除所有 *.orig
和 *.rej
檔案:
git filter-branch --index-filter 'git rm -q --cached --ignore-unmatch *.orig *.rej *~' -- --all
Shifting to a Subdirectory
如果您想要將 GIT 專案根目錄移到一個子目錄中,可以使用 --subdirectory-filter
過濾器。例如,以下命令可以將專案根目錄移到一個名為 sub
的子目錄中:
git filter-branch --subdirectory-filter sub -- --all
注意事項
使用 GIT filter-branch 時,需要注意以下幾點:
- GIT filter-branch 會修改 GIT 歷史記錄,因此需要小心使用。
- GIT filter-branch 會建立新的提交,因此需要更新分支參照。
- GIT filter-branch 可以使用
--original
選項來儲存原始提交記錄。
從技術架構視角來看,深入理解 Git 的內部機制,尤其是提交記錄的組織方式,對於有效運用其版本控制功能至關重要。本文詳細剖析了 git log
、git cherry
、git rebase
、git filter-branch
等核心指令,並闡述瞭如何查詢、簡化、修改提交歷史,以及合併不同倉函式庫的策略。這些技術的掌握能顯著提升團隊協作效率和程式碼函式庫的健壯性。然而,修改提交歷史的操作存在風險,尤其是在多人協作的環境中,不當操作可能導致程式碼函式庫的混亂。因此,建議在執行 git rebase
或 git filter-branch
等指令前,務必做好備份,並與團隊成員充分溝通,以確保操作的安全性。對於大型專案,更應制定嚴格的版本控制流程,規範歷史記錄的修改操作。展望未來,隨著 Git 的不斷演進,預計將出現更精細化、更安全的歷史記錄管理工具,進一步簡化開發流程,提升程式碼品質。玄貓認為,深入理解 Git 的底層原理並熟練掌握相關指令,是每位軟體開發者必備的核心技能。