Git
章節 ▾ 第二版

2.4 Git 基礎 - 還原變更

還原變更

在任何階段,你都可能想要還原某些東西。在此,我們將檢視一些用於還原你所做的變更的基本工具。請小心,因為你並非總是能夠還原某些還原操作。這是 Git 中少數幾個如果你操作錯誤可能會遺失一些工作的領域之一。

其中一個常見的還原發生在你太早提交,並且可能忘記加入某些檔案,或你搞砸了提交訊息時。如果你想要重做該提交,請進行你忘記的額外變更,暫存它們,然後再次使用 --amend 選項提交

$ git commit --amend

此命令會使用你的暫存區域來進行提交。如果你自上次提交以來沒有進行任何變更 (例如,你在前次提交後立即執行此命令),那麼你的快照看起來將完全相同,而你只會變更你的提交訊息。

相同的提交訊息編輯器會啟動,但它已包含你前一次提交的訊息。你可以像往常一樣編輯訊息,但它會覆寫你前一次提交。

舉例來說,如果你提交後才意識到你忘記暫存你想要新增到此提交的檔案中的變更,你可以執行如下操作

$ git commit -m 'Initial commit'
$ git add forgotten_file
$ git commit --amend

你最後會得到單一提交,第二個提交會取代第一個提交的結果。

注意

務必了解,當你修改最後一次提交時,你並不是在修復它,而是完全用一個新的、經過改良的提交來取代它,這會將舊的提交推開,並將新的提交放置在它原先的位置。實際上,就像先前的提交從未發生過一樣,而且它不會顯示在你的儲存庫歷史記錄中。

修改提交的明顯價值在於對你最後一次提交進行微小的改進,而不會讓你的儲存庫歷史記錄充斥著「哎呀,忘記新增檔案」或「可惡,修正上次提交中的錯字」之類的提交訊息。

注意

請僅修改仍在本地且尚未推送到任何地方的提交。修改先前已推送的提交並強制推送分支將會為你的協作者帶來問題。如需深入了解當你這樣做時會發生什麼,以及如果你是接收方時如何復原,請閱讀變基的危險

取消暫存已暫存的檔案

接下來的兩個章節將示範如何處理暫存區和工作目錄的變更。好處是,您用來判斷這兩個區域狀態的指令,也會提醒您如何復原對它們的變更。例如,假設您變更了兩個檔案,並想將它們分別提交為兩個不同的變更,但您不小心輸入了 git add *,將它們都暫存了。要如何取消暫存其中一個檔案呢?git status 指令會提醒您

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md

在「待提交的變更」文字下方,它會說使用 git reset HEAD <file>…​ 來取消暫存。因此,我們來按照建議,取消暫存 CONTRIBUTING.md 檔案

$ git reset HEAD CONTRIBUTING.md
Unstaged changes after reset:
M	CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

這個指令有點奇怪,但它有效。CONTRIBUTING.md 檔案已被修改,但再次變回未暫存狀態。

注意

git reset 確實是一個危險的指令,特別是當您提供 --hard 旗標時。然而,在上述情境中,您工作目錄中的檔案並未被觸及,因此相對安全。

目前,您只需要知道關於 git reset 指令的這種神奇用法。我們將在重置解密中詳細說明 reset 的作用,以及如何掌握它來執行真正有趣的事情。

還原已修改的檔案

如果您意識到您不想保留對 CONTRIBUTING.md 檔案的變更呢?您要如何輕鬆地取消修改它,讓它還原到您上次提交(或最初複製,或無論您如何將它放入工作目錄)時的樣子?幸運的是,git status 也會告訴您如何做到這一點。在最後一個範例輸出中,未暫存的區域看起來像這樣

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

它非常明確地告訴您如何捨棄您所做的變更。我們來按照它說的做

$ git checkout -- CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

您可以看到變更已還原。

重要提示

重要的是要了解 git checkout -- <file> 是一個危險的指令。您對該檔案所做的任何本地變更都將消失,Git 只會將該檔案替換為上次暫存或提交的版本。除非您絕對確定您不想要那些未儲存的本地變更,否則千萬不要使用此指令。

如果您想保留您對該檔案所做的變更,但目前仍需要將它移開,我們將在Git 分支中介紹儲藏和分支;這些通常是更好的方法。

請記住,在 Git 中提交的任何內容幾乎總是都可以復原。即使是已刪除分支上的提交,或被 --amend 提交覆蓋的提交,也可以復原(請參閱資料復原)。但是,您遺失的任何未提交的內容,很可能永遠都找不回來了。

使用 git restore 復原

Git 2.23.0 版本引入了一個新的指令:git restore。它基本上是我們剛才介紹的 git reset 的替代方案。從 Git 2.23.0 版本開始,Git 將使用 git restore 來取代 git reset 進行許多復原操作。

讓我們重新回顧我們的步驟,並使用 git restore 而不是 git reset 來復原。

使用 git restore 取消暫存已暫存的檔案

接下來的兩個章節將示範如何使用 git restore 來處理暫存區和工作目錄的變更。好處是,您用來判斷這兩個區域狀態的指令,也會提醒您如何復原對它們的變更。例如,假設您變更了兩個檔案,並想將它們分別提交為兩個不同的變更,但您不小心輸入了 git add *,將它們都暫存了。要如何取消暫存其中一個檔案呢?git status 指令會提醒您

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   CONTRIBUTING.md
	renamed:    README.md -> README

在「待提交的變更」文字下方,它會說使用 git restore --staged <file>…​ 來取消暫存。因此,我們來按照建議,取消暫存 CONTRIBUTING.md 檔案

$ git restore --staged CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README

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:   CONTRIBUTING.md

CONTRIBUTING.md 檔案已被修改,但再次變回未暫存狀態。

使用 git restore 還原已修改的檔案

如果您意識到您不想保留對 CONTRIBUTING.md 檔案的變更呢?您要如何輕鬆地取消修改它,讓它還原到您上次提交(或最初複製,或無論您如何將它放入工作目錄)時的樣子?幸運的是,git status 也會告訴您如何做到這一點。在最後一個範例輸出中,未暫存的區域看起來像這樣

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:   CONTRIBUTING.md

它非常明確地告訴您如何捨棄您所做的變更。我們來按照它說的做

$ git restore CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README
重要提示

重要的是要了解 git restore <file> 是一個危險的指令。您對該檔案所做的任何本地變更都將消失,Git 只會將該檔案替換為上次暫存或提交的版本。除非您絕對確定您不想要那些未儲存的本地變更,否則千萬不要使用此指令。

scroll-to-top