前言
在前篇文章:操作 Git 的變基與分支合併,我們已經掌握了如何通過變基(rebase)來重新編排、修改和移除提交。
透過變基,可以一次性地將提交壓縮(squash)、改寫(reword)、刪除(drop)提交的內容,確保分支的乾淨性與有效管理。
本篇文將要說明的回退(reset)
,可以在不更動檔案的情況下,使 HEAD 跳到所指定的提交上。從而進行更細微的分支、提交操作。
範例分支狀態
- (0b33..b535)新增 A.txt, B.txt, C.jpg 並進行獨立的提交。
- (0a573c34)然後同時修改 A.txt, B.txt 兩的檔案,並新增 FIX 字串,提交兩個檔案。
- (32e8ea75)最後又修改 A.txt,新增 FIX A2 字串在 A.txt 檔案內後再次提交。
如果要把所有的 A.txt 修改合併為一個提交,且與 B.txt 的修改獨立開來。則可以透過回退把提交拆開。
並結合變基、挑選(cherry-pick)進行結合,以實現更複雜、更細微的調整。
回退的三種模式
⚠️ 在進行以下操作前,請先確認所有變更已經提交至儲存庫,並且沒有任何暫存的內容。
git reset –mixed
在最後一個提交上新增一個 mixed 分支進行說明,可以看到我們目前 HEAD 指向 32e8ea75。
對著提交右鍵點選 Reset current branch to this Commit… (將目前分支回退至此提交…)。
可以再不改變當前所有檔案情況下,重新定位 HEAD 指標至所選定的提交。
可以看到的是,回退到先前提交,由於工作目錄下的檔案
與當前 HEAD 所指向的提交
不一致,所以檔案差異會變為變更狀態
。
git reset –soft
在最後一個提交上新增一個 soft 分支進行說明。
與 git reset –mixed 不同的是,回退後檔案會放在暫存區,等待再次提交。
如果我們進行 Unstage(git reset)把暫存中的變更回退到工作目錄,會發現與 git reset –mixed 一樣。
git reset –hard
這個非常好理解,就是拋棄所有當下全部變更,直接把 HEAD 移動到指定的提交。
⚠️ 注意,會連同暫存區一併清空。所以在操作前,請記得保存當下的狀態。
透過 reflog 復原失敗的操作
如果在回退操作中未事先建立新的分支來保存當前的提交點,則原先的提交會變為孤立提交(orphan commit)。孤立提交是指不再由任何分支指向的提交,這可能導致這些提交在未來的垃圾回收中被刪除。
在這種情況下,我們可以利用 git reflog
顯示所有 git 進行操作的行為,藉此來找回這些孤立提交。如果要查看 log 操作的精確時間,可以加上 --date=iso
選項。此外,如果想要簡要顯示每次提交的內容,則可以使用 --pretty=short
選項。或是使用重定向輸出為一個檔案。
|
|
由下面的影片中得知,操作失誤上進行了回退,而不想要回退了。由 reflog 中可以得知原先提交 ID 為 32e8ea7。
所以透過 git reset --hard 32e8ea7
便可拋棄當下變更,使 HEAD 回到 32e8ea7。
|
|
透過回退進行複雜的合併
有時候在整理提交,會發現提交中含有多個檔案,如果要一次性的合併,該如何進行?
例如以下分支,:Node → 提交 A 檔案 → 提交 B 檔案 → 提交 C 檔案 → 同時提交 A, B 檔案 → 更新 A 檔案
在最後一筆更新 A 檔案
上,已經確定完善且不需要先前所有 A 的提交,透過回退可以快速的實現整合、分離提交內容的檔案等…。
我們目標為:Node → 提交 A 檔案_withFixed → 提交 B 檔案_withFixed → 提交 C 檔案。
其中,可以在最後一筆直接回退至 Node 後,一筆一筆重新提交。然而所有提交時間都會遺失。
若要最大程度上的保留提交時間、作者等資訊,可以藉由兩個分支
與變基
來實現,其流程如下:
- 於最後一筆提交新增 test 分支,並切至該分支。
- mixed 回退至預期想要的節點,此時會保留工作區的狀態、但是 HEAD 回退了。
- 將不需要的檔案移除,如 B, C 檔案。
- 重新提交 A 檔案,這個 A 檔案已經包含 Fix 等相關修正。
- 切至
new_merge
分支。 - 對著
Merge_A
右鍵進行變基,途中遇到衝突,由於 A 檔案已包含修改,所以遇到的衝突要選擇保留本地端的狀態
。 - 變基後,進行 git diff 評估 new_merge 分支與 master 分支的差異。如果沒差異則不會輸出東西,代表 new_merge 與 master 是一致的。
- 如果有需要再進行操作,將 test 分支 hard 回退到 new_merge 上,重新進行步驟 2 ~ 7。
- (有需要的話)刪除 master 分支,將 new_merge 分支改名 master 後進行推送。推送後,其他人會因為找不到原本的父節點而發生衝突,此時其他人的修改需要變基到新的 master 分支上才能再次推送。這邊需要協調好團隊之間的行為。
講了這麼多,看下面的操作會比較詳細:
上述行為跟變基中的 edit 蠻像的,只不過 edit 是在變基的過程中停住,讓使用者回退上個提交,並重新再次提交。目的性大同小異。
我個人會更傾向於使用兩個分支 + 回退 + 變基的操作
。如果再過程中遇到錯誤,不知道怎麼解決衝突,都可以透過 ‵git rebase –abort‵ 隨時終止變基的行為。
總結
使用回退可以任意將 HEAD 指向某個特定的提交,藉由不同的模式有細微差異。
- mixed 會保留當前工作目錄下的檔案,移動 HEAD
- soft 會保留當前工作目錄下的檔案,並且放置暫存區後,移動 HEAD
- hard 會拋棄當前工作目錄下的所有檔案,將 HEAD 移動到指定的提交。
有任何進階的合併操作,可以透過兩個分支、回退、變基進行操作。
或是單純使用變基,並將想要的提交透過 edit 進行變基過程中的中斷、回退拆開提交、再次提交、繼續變基。