我們在開發專案時,不太可能一帆風順,永遠依循著 開發完 => 暫存 => 提交 的順序持續上版。
有時候手速一個太快,只是想暫存部分檔案,結果執行到 git add .
,但實際上並不是想暫存所有更動的檔案,有沒有辦法將部份內容移出暫存區?
像是一股腦把所有東西加到購物車,結帳前看到總金額嚇了一跳,想把部分商品移除購物車的行為。
又或者開發到一半發現改爛了,能不能一次捨棄編輯的資料,讓檔案回到原本的 commit 狀態?
就像是之前提玩遊戲下完存檔點後,繼續打 Boss 關卡,結果打輸了,回到存檔點重來的行為。
這些都是實際開發中可能會遇到的狀態,筆者也是在學會了這些操作後,實際使用在專案上的那一刻,由衷讚嘆有 Git 之後,真的很方便。
話不多說,開始來說明吧!
在說明檔案狀態的文章中,就有特別提到:只有尚未加入 commit 的資料,有辦法自由變換暫存與未暫存的狀態。
把新資料加到暫存區大家應該已經會了,就是 git add
指令。
而把暫存區的資料移出來,就是接下來要介紹的事情了。
老樣子,指令與 Fork GUI 都會說明。
這件事情有兩個指令可以達成:
git restore --staged
將檔案移出暫存區git restore --staged 檔名
restore
中文翻譯是 還原,它是一個用來還原工作目錄檔案的指令。
以上面指令來說,就是要把 暫存 (--staged
) 的狀態 還原 (restore) 成 未暫存 (unstage) 的狀態,也就代表著要把資料移出暫存區了。
這個指令不用特別記,它其實藏在 git status
中的訊息中:
git st
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage) # 藏在這~~
modified: index.html
實際使用起來長這樣:
git reset
將檔案移出暫存區git reset 檔案名稱
reset ,中文叫重新設置,是一個可以拿來重置 索引(註1) 或 工作目錄 的指令,預設情況它只會幫我們重置索引,不會重置到工作目錄的內容。
上面這個指令其實省略了一個關鍵字:
git reset HEAD 檔案名稱
沒錯,又是這個 HEAD
。
git reset
指令預設行為 (註2) 會重置「目前位置」的索引。
git reset 檔案
的意思,就好像是把目前購物車中的某個商品 reset 掉 (拿掉) 的感覺。
實際使用起來長這樣:
註1. 重申一次:我們在說的「暫存區」,對 Git 來說就是 「索引」,這兩個詞是完全一樣的概念,所以只要看到索引,請自動想成暫存區。
你要想成購物車其實也可以啦...註2. 如同上面的定義,
reset
搭配其他參數是會異動到工作目錄的,不過現在扯進來談會讓焦點失焦,暫時先不細談。
記得 加入暫存區 被我比喻成 加入購物車,那如果你想把商品「移出購物車」會怎麼做?
對,就是讓它不要出現在購物車!
所以在 Fork GUI 要把檔案移出暫存區,就是把加入暫存區的行為反過來執行。
這裡假設只想把 readme.txt
從暫存區移除:
這種事情其實很常發生,例如在做一些實驗性的開發,結果改失敗了,想把原本紀錄的資料重新還原到工作目錄中,或者是任何理由,想放棄更動的資料,都可以用接下來的操作來完成。
要放棄工作目錄中檔案的異動,第一件事情,必須確保檔案處於「未暫存」的狀態。
換言之,執行過 git add
的檔案,不適用 接下來的操作,請回到上一步查看怎麼把檔案移出暫存。
一樣有兩個指令可以做到這件事:
git restore
捨棄檔案更變git restore 檔案
上一步有提到,restore 用來把檔案還原到之前的狀態,有異動的檔案先前的狀態,就是把這些異動移除。
一樣不用特別記,指令藏在 git status
中:
git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory) # 藏在這~
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
順便帶大家看一個關鍵字 discard :丟棄,這是在 Git 世界中很常見的詞彙,不管指令或 GUI ,只要看到這個詞就代表要捨棄更變的意思。
實際使用起來長這樣:
git checkout --
捨棄檔案更變git checkout -- 檔案名稱
必須先說,這個指令加上分支名稱,就是「切換分支」的指令,這也是 checkout 最常使用的情境。
只不過如果 checkout 加上檔名,就會是把目前位置的檔案狀況,還原到工作目錄中。
而指令會加上 --
最重要的目的,就是避免檔案名稱等同於某個分支,這會直接造成指令變成「切換分支」的行為。
實際使用起來長這樣:
使用 Fork GUI 操作,一樣必須確保檔案處於「未暫存」的狀態。
接著可以對著檔案點選右鍵,點選 Discard Changes... 的選項來完成:
可能會有一種狀況,我們不只有要還原 單一檔案 異動,而是編輯了一堆檔案,想要一次讓資料還原。
一樣示範指令的做法,再來示範 GUI 的做法。
有三個指令可以做到這件事,其中兩個就是上面的 restore
跟 checkout
。
記得我們一次把檔案加到暫存區使用的參數嗎?
就是 git add
加上 .
。
所以在確保資料尚未加入暫存區時,想捨棄所有資料異動,可以挑一個指令執行:
git restore
捨棄所有檔案的更變git restore .
git checkout
捨棄所有檔案的更變git checkout .
最後一個指令就比較特別了,他有能力做到 無視資料是否在暫存區 一次捨棄所有異動資料,他就是 reset
。
3. 使用 git reset --hard
捨棄所有檔案的更變
git reset --hard
reset
指令能做到的功能真的很多,如果你是因為這篇文章才知道 reset
指令的人,很可能已經因為一開始說 reset
把資料 移出暫存區,現在又說 reset
可以一次把異動資料 捨棄 這兩個不同行為混淆了。
為了避免有人帶著懸念離開這篇文章,請先記得一件事:
reset
指令主要是拿來操作 HEAD 所指向的 commit,很少有人拿來操作單一檔案。
更詳盡的內容,之後會專門寫一篇詳細的 reset 使用說明文章 來跟大家講解。
ctrl
或 shift
把檔案全部選起來一般如果有說明多種方法的內容,筆者通常會建議挑自己喜歡的指令執行就好。
不過今天所介紹的內容,筆者會建議先 不要使用 兩個指令:
一個是 reset
,另一個是 checkout
。
在說明之前先強調一下,如果遇到文章中的情境,這兩個指令 不是不能用,他們還是能做到我們想執行的事情。
純粹只是下次在遇到這兩個指令時,介紹的情境會完全不同,這可能使新手對指令的看法有強烈的混淆,才會說不建議使用。
前面有提到,很少人會使用 reset
操作 檔案,至少我身邊的朋友幾乎都不會做這種事。reset
在 Git 中的主要用途,會是操作 HEAD
的指向位置。
所以當有需要用到 將檔案移出暫存區 的需求,建議使用 restore
指令:
git restore --staged 檔名 # 使用 restore 指令將檔案移出暫存區
一來, git status
指令打下去,就能看到 git restore --staged
擺在眼前,連單字都不用去記。
二來, 讓 restore
去處理檔案,讓 reset
去處理 HEAD
,能讓觀念區分得清楚一些。
相信有接觸過 Git 分支的人,已經知道他是「切換分支」的指令。
現在說這個指令可以拿來捨棄檔案異動,對新手來說實在不是很友善...
筆者建議,新手如果要讓檔案資料復原,不要 使用 checkout
指令,而是使用 restore
指令:
git restore 檔案 # 使用 restore 指令捨棄檔案異動
也是一樣的概念,如果不小心忘記指令或是單字怎麼拚,git status
執行下去,這個指令就跑出來給你看,根本不用去記憶。
此外,也能讓 checkout
專心去處理 「切換分支」 的需求,比較不會造成觀念混淆
以上內容,就提供大家參考吧!