# 前言

我們在開發專案時,不太可能一帆風順,永遠依循著 開發完 => 暫存 => 提交 的順序持續上版。

有時候手速一個太快,只是想暫存部分檔案,結果執行到 git add . ,但實際上並不是想暫存所有更動的檔案,有沒有辦法將部份內容移出暫存區?
像是一股腦把所有東西加到購物車,結帳前看到總金額嚇了一跳,想把部分商品移除購物車的行為。

又或者開發到一半發現改爛了,能不能一次捨棄編輯的資料,讓檔案回到原本的 commit 狀態?

就像是之前提玩遊戲下完存檔點後,繼續打 Boss 關卡,結果打輸了,回到存檔點重來的行為。

這些都是實際開發中可能會遇到的狀態,筆者也是在學會了這些操作後,實際使用在專案上的那一刻,由衷讚嘆有 Git 之後,真的很方便。

話不多說,開始來說明吧!

# 加入暫存區的檔案,想將它移出暫存區

說明檔案狀態的文章中,就有特別提到:只有尚未加入 commit 的資料,有辦法自由變換暫存與未暫存的狀態。

把新資料加到暫存區大家應該已經會了,就是 git add 指令。
而把暫存區的資料移出來,就是接下來要介紹的事情了。

老樣子,指令與 Fork GUI 都會說明。

# 用指令將檔案移出暫存區

這件事情有兩個指令可以達成:

# 1. 使用 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

實際使用起來長這樣:

# 2. 使用 git reset 將檔案移出暫存區

git reset 檔案名稱

reset ,中文叫重新設置,是一個可以拿來重置 索引 (註 1) 或 工作目錄 的指令,預設情況它只會幫我們重置索引,不會重置到工作目錄的內容。

上面這個指令其實省略了一個關鍵字:

git reset HEAD 檔案名稱

沒錯,又是這個 HEAD
git reset 指令預設行為 (註 2) 會重置「目前位置」的索引。
git reset 檔案 的意思,就好像是把目前購物車中的某個商品 reset 掉 (拿掉) 的感覺。

實際使用起來長這樣:

註 1. 重申一次:我們在說的「暫存區」,對 Git 來說就是 「索引」,這兩個詞是完全一樣的概念,所以只要看到索引,請自動想成暫存區。 你要想成購物車其實也可以啦…

註 2. 如同上面的定義, reset 搭配其他參數是會異動到工作目錄的,不過現在扯進來談會讓焦點失焦,暫時先不細談。

# 用 Fork GUI 將檔案移出暫存區

記得 加入暫存區 被我比喻成 加入購物車,那如果你想把商品「移出購物車」會怎麼做?

對,就是讓它不要出現在購物車!

所以在 Fork GUI 要把檔案移出暫存區,就是把加入暫存區的行為反過來執行。

這裡假設只想把 readme.txt 從暫存區移除:

  1. 點選檔案,再點 Unstage 按鈕:

  1. 直接把檔案拖曳到 Unstaged 區域:

# 想捨棄檔案修改的內容

這種事情其實很常發生,例如在做一些實驗性的開發,結果改失敗了,想把原本紀錄的資料重新還原到工作目錄中,或者是任何理由,想放棄更動的資料,都可以用接下來的操作來完成。

要放棄工作目錄中檔案的異動,第一件事情,必須確保檔案處於「未暫存」的狀態。

換言之,執行過 git add 的檔案,不適用 接下來的操作,請回到上一步查看怎麼把檔案移出暫存。

# 使用指令捨棄檔案更變

一樣有兩個指令可以做到這件事:

# 1. 使用 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 ,只要看到這個詞就代表要捨棄更變的意思。

實際使用起來長這樣:

# 2. 使用 git checkout -- 捨棄檔案更變

git checkout -- 檔案名稱

必須先說,這個指令加上分支名稱,就是「切換分支」的指令,這也是 checkout 最常使用的情境。
只不過如果 checkout 加上檔名,就會是把目前位置的檔案狀況,還原到工作目錄中。
而指令會加上 -- 最重要的目的,就是避免檔案名稱等同於某個分支,這會直接造成指令變成「切換分支」的行為。

實際使用起來長這樣:

# 使用 Fork GUI 捨棄檔案更變

使用 Fork GUI 操作,一樣必須確保檔案處於「未暫存」的狀態。

接著可以對著檔案點選右鍵,點選 Discard Changes… 的選項來完成:

# 一次捨棄所有檔案的更變

可能會有一種狀況,我們不只有要還原 單一檔案 異動,而是編輯了一堆檔案,想要一次讓資料還原。

一樣示範指令的做法,再來示範 GUI 的做法。

# 用指令捨棄所有檔案的更變

有三個指令可以做到這件事,其中兩個就是上面的 restorecheckout

記得我們一次把檔案加到暫存區使用的參數嗎?
就是 git add 加上 .

所以在確保資料尚未加入暫存區時,想捨棄所有資料異動,可以挑一個指令執行:

  1. 使用 git restore 捨棄所有檔案的更變
git restore .

  1. 使用 git checkout 捨棄所有檔案的更變
git checkout .


最後一個指令就比較特別了,他有能力做到 無視資料是否在暫存區 一次捨棄所有異動資料,他就是 reset
3. 使用 git reset --hard 捨棄所有檔案的更變

git reset --hard

reset 指令能做到的功能真的很多,如果你是因為這篇文章才知道 reset 指令的人,很可能已經因為一開始說 reset 把資料 移出暫存區,現在又說 reset 可以一次把異動資料 捨棄 這兩個不同行為混淆了。

為了避免有人帶著懸念離開這篇文章,請先記得一件事:
reset 指令主要是拿來操作 HEAD 所指向的 commit,很少有人拿來操作單一檔案。

更詳盡的內容,之後會專門寫一篇詳細的 reset 使用說明文章 來跟大家講解。

# 用 Fork GUI 一次捨棄所有檔案的更變

  1. ctrlshift 把檔案全部選起來
  2. 點右鍵
  3. Discard Changes…

# 補充

一般如果有說明多種方法的內容,筆者通常會建議挑自己喜歡的指令執行就好。

不過今天所介紹的內容,筆者會建議先 不要使用 兩個指令:
一個是 reset ,另一個是 checkout

在說明之前先強調一下,如果遇到文章中的情境,這兩個指令 不是不能用,他們還是能做到我們想執行的事情。

純粹只是下次在遇到這兩個指令時,介紹的情境會完全不同,這可能使新手對指令的看法有強烈的混淆,才會說不建議使用。

# 建議不要使用 reset 將檔案移出暫存區

前面有提到,很少人會使用 reset 操作 檔案,至少我身邊的朋友幾乎都不會做這種事。
reset 在 Git 中的主要用途,會是操作 HEAD 的指向位置。

所以當有需要用到 將檔案移出暫存區 的需求,建議使用 restore 指令:

git restore --staged 檔名   # 使用 restore 指令將檔案移出暫存區

一來, git status 指令打下去,就能看到 git restore --staged 擺在眼前,連單字都不用去記。
二來, 讓 restore 去處理檔案,讓 reset 去處理 HEAD ,能讓觀念區分得清楚一些。

# 建議不要使用 checkout 捨棄目前檔案

相信有接觸過 Git 分支的人,已經知道他是「切換分支」的指令。
現在說這個指令可以拿來捨棄檔案異動,對新手來說實在不是很友善…

筆者建議,新手如果要讓檔案資料復原,不要 使用 checkout 指令,而是使用 restore 指令:

git restore 檔案   # 使用 restore 指令捨棄檔案異動

也是一樣的概念,如果不小心忘記指令或是單字怎麼拚, git status 執行下去,這個指令就跑出來給你看,根本不用去記憶。

此外,也能讓 checkout 專心去處理 「切換分支」 的需求,比較不會造成觀念混淆

以上內容,就提供大家參考吧!