Git stash後新檔案還在:先分清未追蹤與忽略檔案

準備切分支,先跑git stash,工作目錄裡的新檔卻還在。先看這個檔案是否未追蹤:預設stash處理已追蹤檔案的本地修改,不會把所有看得到的檔案都收進去。若新檔還受.gitignore忽略,使用-u之後也可能仍然留在原處。

先分成三類,不要只看資料夾

git status --short --ignored

 M tracked.txt
?? new.txt
!! cache.log

本例tracked.txt已經有提交紀錄,現在內容被修改;new.txt是未追蹤檔案;cache.log由.gitignore忽略。M、??與!!代表不同狀態。只用檔案總管看三個檔案都在同一個資料夾,無法知道stash會處理哪一個。

普通git status通常不列出忽略項,所以這裡加–ignored來讀取狀態。若要再核對索引中的追蹤檔案,也可用git ls-files。這些只讀命令先協助盤點,不會替你建立stash或刪除檔案;檢查清楚後再決定暫存範圍。

用自己的小儲存庫做對照

建立案例時先提交乾淨的起始版本,讓Git知道什麼是base,再修改已追蹤檔案。若儲存庫連第一個提交都沒有,stash的前提與本例不同;先不要把指令失敗誤認成-u不支援新檔。真實專案也先核對當前HEAD與工作狀態,知道暫存會以哪個提交作參考。

本次使用Git 2.47.1的新隔離儲存庫,沒有遠端,也沒有對產品儲存庫執行命令。起始提交包含.gitignore與tracked.txt,後者內容是base。接著把它改成changed,再新增new.txt與被忽略的cache.log。所有檔案內容都是測試文字,不是實際網站設定。

重現時在新的測試目錄操作,先確認目前路徑與git status。不要把本篇命令直接貼到用途不明的專案裡,尤其是專案已有別人的未完成修改時。stash會修改工作目錄,所以它不是與status一樣的只讀檢查。

預設stash只收本例已追蹤的修改

git stash push -m "default-case"
git status --short --ignored

執行後tracked.txt回到base,new.txt仍在,cache.log也仍在。狀態剩下?? new.txt與!! cache.log。這次stash成功不代表工作目錄裡每個檔案都被保存;它只保存了本例已追蹤檔案的修改。

用git diff檢查已追蹤的未暫存差異,這時沒有tracked.txt的變更。但diff本身也不會把未追蹤新檔當成一般已追蹤差異,所以還要和status一起看。只看到空白diff就宣稱「全部乾淨」或「所有檔案已備份」,都超出這個命令所提供的資訊。

如果你要確認某一個重要新檔是否真的被保存,不要先把它刪掉來測。先記錄原路徑與內容,再看所選stash的範圍,最後在可控的工作目錄中還原並比較。同名檔案還在不等於同一份內容;版本管理核對的是資料與差異,不只是檔名。

git stash list
git stash apply "stash@{0}"

本次apply成功後,tracked.txt再次是changed,new.txt與cache.log仍保持原內容。apply保留stash項目,便於繼續核對;它不是pop,也不會在成功後自動移除該項。本篇的兩個stash都留在隔離案例裡,沒有示範清除歷史。

加-u後,新檔才一起收進去

git stash push -u -m "untracked-case"
git status --short --ignored

這次tracked.txt回到base,new.txt從工作目錄消失,cache.log仍在。狀態只剩!! cache.log,這正是-u包含未追蹤項但沒有包含忽略項的本例結果。檔案消失是收存與工作目錄清理的結果,不應在這時另外重建同名檔案,否則後續還原可能遇到衝突。

git stash apply "stash@{0}"
git status --short --ignored

最新stash現在是untracked-case。apply後,tracked.txt恢復changed,new.txt重新出現且內容是new,cache.log仍是ignored。檔案存在與內容都核對過,才能說這兩類修改在本例成功恢復;不能只看stash list有一行名字就下結論。

stash@{0}指最新項目,每新增一次stash,較早項目的編號就會移動。實際工作先讀git stash list,確認要取的是哪一個,再執行apply。尤其是不同分支交錯建立多份stash時,不要把昨天記下的編號當成今天仍然對應同一項。

Windows PowerShell下,把stash@{0}用引號括住,避免shell把其中符號當成其他語法;本文命令也已加引號。這裡的引號保護命令列參數,不會改變Git的stash編號規則。若shell先報錯,應先核對命令是否完整傳給Git,而不是直接換成刪除命令。

忽略檔案與還原衝突要另處理

Git文件另有–all,用來把忽略項也包含進去,但這篇沒有執行它。忽略區裡可能有大量依賴、產生檔或敏感設定,不能為了讓目錄看起來空白就不看範圍地收存。先決定哪些內容需要保留,以及是否應用獨立備份方式處理,再選命令。

還原到不同提交或已經有新修改的工作目錄,可能產生衝突。這篇apply是在同一個base與可還原的起始條件上成功,不保證任意分支都能無衝突。遇到apply失敗時先讀status與錯誤,保留stash,不要接著drop或clear讓可核對的來源消失。

若你還希望恢復暫存區原來的狀態,需另外核對–index等選項的用途。本例tracked.txt只作未暫存修改,沒有把「暫存與未暫存兩層都完全恢復」算作實測。先保存原status,再驗證自己的具體需求,避免把內容恢復與索引狀態恢復混為一談。

正在解衝突或有特殊索引狀態的儲存庫,未必符合這張小表的起始條件。先把未合併路徑與目前操作查清楚,不要把stash當成任何狀態都能無條件收起的萬用按鈕。本例沒有合併、子模組或大型依賴目錄,這些都需要另外核對。

驗收用狀態、檔案與內容三項一起看

這次預設stash後的清單是兩個檔案仍在,-u後是一個忽略檔仍在;兩次apply後都回到三類原狀態。這個小對照比只記「加-u就好」更有用,因為它也說明-u沒有替你保存忽略檔,更沒有把工作目錄當成完整備份。

完成還原後,確認tracked.txt的差異、new.txt的實際內容與cache.log是否保持預期,再決定是否繼續切分支或提交。stash只是暫時收存本地工作,沒有推到遠端,也不代表其他人能取得這份內容。本例沒有設定遠端,沒有宣稱任何外部備份已完成。

若忽略檔其實是自己的重要設定,可以另外保留有範圍的本地備份。不要為了讓它被stash包含,就臨時把秘密設定加入追蹤並提交;是否應進入版本管理與是否需要保存,是兩個不同決定。這次case只使用普通測試文字,不示範任何秘密檔案處理。

下一次看到stash後新檔仍在,先查??或!!,再選擇處理範圍。能說清楚「哪類檔案被保存、哪類留在工作目錄、如何核對還原」,才算真的掌握這次stash做了什麼。

參考資料

原文鏈接:https://wntheme.com/git-stash-untracked-files-scope/,轉載請註明出處。
0

評論0

顯示驗證碼
沒有帳號?註冊  忘記密碼?