切到舊提交查看程式,Git顯示detached HEAD。若只是看歷史,這個狀態很正常;但如果你在這裡改了檔案並提交,先把新提交接到有名字的分支,再切回其他工作。提交已經產生,不代表原本main會跟著移到它。
HEAD直接指向提交,與跟著分支不同
平常HEAD跟著某個分支,新增提交時分支也會往前。detached HEAD時,你目前站在某個提交上,沒有讓既有分支一起前進。可以修改、測試與提交,但這些新工作需要另有名稱留住,才能方便下一次找到。
不要把這個狀態理解成檔案已經丟失或儲存庫壞掉。Git只是把「目前看的提交」與「目前所在的分支」分開了。先確認是不是刻意切到提交或標籤,再決定只看完離開,還是保存這裡的新工作。
git status
git branch --show-current
git log -1 --oneline
在detached狀態,branch –show-current沒有分支名稱輸出;log仍能看到當前提交。兩個命令回答不同問題,所以不要因為分支名稱空白就以為沒有HEAD,或因為log有提交就以為已經在main。
用新儲存庫建立真實對照
本次案例使用Git 2.47.1與新的隔離儲存庫,沒有遠端,也沒有在產品儲存庫做任何切換。main先有base與main second兩個提交,再切回base,進入detached狀態;這讓分支最新版本與當前查看的舊版本能清楚區分。
git switch --detach BASE_COMMIT
BASE_COMMIT是你已經核對的完整或可辨識提交hash,範例字樣要換成自己的提交,不要照字面執行。案例用rev-parse保存base的真hash再傳入命令,沒有假造一個hash。切換前也確認工作狀態,避免把尚未保存的其他修改混進這個實驗。
切換可能更新工作目錄與索引。Git通常會阻止導致本地修改丟失的切換,但不代表你應跳過盤點;本文沒有加–force或–discard-changes。若命令因修改而拒絕,先處理自己的工作,不要為了進入舊版本就強制丟棄它們。
在detached狀態提交,再立即建立分支
案例把note.txt改成detached work後,暫存並提交。新commit的hash在建立分支前就已保存;這一步讓我們知道接下來命名的究竟是不是剛產生的提交。不要只根據檔案看起來相同就判定分支接對了。
git add note.txt
git commit -m "detached work"
git rev-parse HEAD
git switch -c save-detached
-c建立新分支並切換到它,沒有另指定起點時以目前HEAD為起點。這次save-detached直接指向剛才的detached work提交,沒有重新提交或複製檔案。本次建立分支前後的HEAD hash完全相同,只有它現在也能由分支名稱找到。
選一個尚未存在的分支名稱。如果名稱已經存在,先核對它指向什麼,換一個清楚的名稱;不要為了避免錯誤把-c改成-C。大寫-C有重設已有分支的用途,範圍與這篇保留新工作不同,不能當作大小寫都一樣。
核對名稱、提交與歷史三者
讀歷史時可以先用三個名稱理解關係:base是共同起點,main second是原分支第二筆,detached work則是從base另走的一筆。新分支保存的是這條旁路,不會把兩筆新提交排序後拼成一條直線。想把新工作整合回main,是後續合併或其他整合操作,不能靠取分支名稱就視為完成。
git branch --show-current
git rev-parse HEAD
git log --oneline --decorate --all
本次show-current回傳save-detached,HEAD hash仍是detached work那筆;帶–all的紀錄同時看得到main的main second與新分支的detached work。兩條路都從base開始,這不是把main移回舊版本,也不是將兩條分支自動合併。
在案例裡,最後工作目錄乾淨,note.txt是detached work內容,分支名稱明確。若你自己的工作還有未提交修改,建立分支只是給目前提交命名,不會把那些修改自動變成提交;需要另外檢查status並決定下一步。
分支是本地名稱,不等於已經上傳到GitHub,也不等於完成部署。這個案例沒有任何遠端操作,所以只驗證本地提交可由save-detached找到。要分享時,應另核對遠端與推送權限,不把本地成功冒充外部保存。
如果已經切走,先查reflog再決定
Git的reflog記錄本地引用與HEAD曾經移動的位置,可以協助尋找剛才的提交。但它不是永久備份,紀錄與不可達物件有保留與清理規則。這篇沒有先切走再找回的實測,不能把它說成任意時間都能恢復任何工作。
如果已經離開detached狀態,先只讀git reflog與提交內容,確認哪一筆才是自己的工作,再按核對過的提交建立新分支。不要看到一個相近時間的hash就直接重設main,因為分支指向與恢復檔案內容是不同操作。已知提交hash時,給它另取名稱通常更容易核對。
未提交修改與已經提交的工作也要分開。reflog記的是引用移動,不是替每次鍵盤輸入保存完整歷史;沒有commit的內容不能直接沿用「找提交hash」的做法。回到編輯器或自己的備份核對範圍,別讓一種恢復命令承擔它沒有紀錄的資料。
把保存動作放在離開之前
如果只是打算試改,進入舊提交時也可以直接建立實驗分支,讓後續提交一開始就有名稱。本文先進入detached再建立分支,是為了重現已經發生的情況;不是要求每次看歷史都必須繞這條路。是否要改檔與是否要保留成果,決定了合適的入口。
也要確認分支名稱不是遠端名稱的混用。這份儲存庫只有本地main與save-detached,沒有origin或追蹤設定,所以命令的效果很清楚。真實專案若有工作樹或同名分支,先查現況;建立新名稱仍應以剛才核對的提交為準。
在舊版本做一次實驗,若沒有要保留的工作,可以只讀完再回到分支。若已經產生值得保留的新提交,就在仍站在它上面時建立新分支,並核對hash。這比離開後才猜測哪一筆是剛才的工作更直接,也不會把原分支拉到不相干的位置。
驗收這個操作不需要網路:分支名稱是save-detached,HEAD仍是原新提交,完整log也能看到原main未被替換。三項都核對過,才知道你保留了新工作而沒有順手改變原來的開發路線。這份隔離案例保存了每次命令與結果,讀者可以照相同順序在自己的小儲存庫重現。
最後把新分支名稱記到工作筆記,再決定是否要回到main或繼續編輯。detached HEAD本身不是錯誤,真正需要處理的是「新工作有沒有一個明確入口」。讓提交有名字接住,下一次查找與分享才會有可核對的起點。

評論0