學習 Git (6) - 修改 commit 紀錄 part 2: Reset


Posted by Calon on 2022-05-16

如果我們想修改的不是最新的一筆紀錄,而是前幾筆,例如下面的紀錄:

1db41c6 (HEAD -> master) docs: 新增 helloWorld.txt 註解
05ca78e docs: 新增 test.txt
042ee26 docs: 新增道別規矩
dfaa0f3 初始版本

我們想修改的是 025e6f4 這筆紀錄,我們可以使用 git reset

$ git reset dfaa0f3

上面是使用 git reset 退回到「絕對」歷史紀錄,我們也可以使用「相對」的方式:

$ git reset 1db41c6^^^

^ 表示前一次,所以 1db41c6^^^ 會是從 1db41c6 往前三個紀錄,也就是會回到 dfaa0f3 這個版本紀錄,但如果要往前好幾筆紀錄需要打很多 ^,所以我們也可以寫成 1db41c6~3。
另外,在這裡 HEAD、master 也都指向 1db41c6,所以也可以將 1db41c6 寫成 HEAD~3 或是 master~3。

執行完 git reset 後,在來看一下歷史紀錄:

$ git log ---oneline
dfaa0f3  (HEAD -> master) 初始版本

會發現只剩 dfaa0f3 這一個紀錄,而我們的檔案內容並沒有跟著回到這一版本的狀態,所以可以放心的重新將內容 commit 上去。

Reset 參數:--mixed、--soft、--hard

在使用 reset 時可以加上 --mixed、--soft、--hard 參數,雖然我們在上面使用 git reset 時並沒有帶上參數,但預設參數為 --mixed,而這三個參數會影響到拆掉 commit 後的檔案狀態:

--mixed

使用 git reset 時如果不加上任何參數則會預設使用 --mixed。
而在使用此參數的時候,會將拆出來的檔案狀態回到加入暫存區前,我們對剛才的範例使用 git status 來檢查檔案追蹤狀態:

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

    modified: helloWorld.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
       test.txt

nothing added to commit but untracked files present (use "git add" to track/or "git commit -a")

兩個檔案都回到使用 git add 前的狀態。

--soft

當在使用時加上 --soft 參數時,會將拆出來的檔案回到使用 git add 的狀態,一樣使用同個範例來檢查狀態:

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
      modified: helloWorld.txt
      new file: test.txt

可以看到兩個檔案都是處在準備被 commit 的狀態。

--hard

使用此參數時,會直接將拆出來的檔案丟掉,也就是除了暫存區以外,工作目錄的檔案狀態也直接回到 dfaa0f3 的狀態。
我們用 git status 來檢查狀態前先使用 git log -p 來看一下哪些地方被新增、修改:

$git log -p
commit 1db41c66adf334829c309996a93bcec41c3fe6b4 (HEAD -> master)
Author: 使用者 <使用者 email>
Date:   Sun May 8 00:44:02 2022 +0800

    docs: 新增 helloWorld.txt 註解

diff --git a/helloWorld.txt b/helloWorld.txt
index 9b4edc2..0e5c9af 100644
--- a/helloWorld.txt
+++ b/helloWorld.txt
@@ -1,3 +1,5 @@
 Hello, world!

 You need to say hi to everyone in the morning. And say goodbye when you leave.
+
+Please follow the rules.

commit 05ca78e586618389fe56263c764526a4a1239645
Author: 使用者 <使用者 email>
Date:   Sun May 8 00:43:47 2022 +0800

    docs: 新增 test.txt

diff --git a/test.txt b/test.txt
new file mode 100644
index 0000000..836ac19
--- /dev/null
+++ b/test.txt
@@ -0,0 +1 @@
+keep alive

commit 042ee2612075318ab1c9171cdf03c688621818fb
Author: 使用者 <使用者 email>
Date:   Sun May 8 00:43:15 2022 +0800

    docs: 新增道別規矩

diff --git a/helloWorld.txt b/helloWorld.txt
index 56bbc32..9b4edc2 100644
--- a/helloWorld.txt
+++ b/helloWorld.txt
@@ -1,3 +1,3 @@
 Hello, world!

-You need to say hi to everyone in the morning.
+You need to say hi to everyone in the morning. And say goodbye when you leave.

上面的資訊 + 為新增,- 為刪除,這樣可以得到 helloWorld.txt 在 dfaa0f3 的內容為:

Hello, world!

You need to say hi to everyone in the morning.

在 1db41c6 時內容為:

Hello, world!

You need to say hi to everyone in the morning. And say goodbye when you leave.

Please follow the rules.

而 test.txt 則是在 05ca78e 新增,而內容為:

keep alive

接下來使用 git reset --hard 回到 dfaa0f3,在使用 git status 來檢查狀態

$ git reset --hard dfaa0f3
$ git status
On branch master
nothing to commit, working tree clean

會發現沒有任何檔案有備追蹤,接下來用 ls 來看一下工作目錄底下的檔案:

$ ls
helloWorld.txt

只剩 helloWorld.txt 這個檔案,test.txt 不見了。再來檢查一下 helloWorld.txt 裡面的檔案內容:

Hello, world!

You need to say hi to everyone in the morning.

會發現少了一個斷行以及 Please follow the rules. 這句話,而這就是使用 --hard 參數會把拆出來的檔案直接丟掉的意思。

使用 --hard 參數後的檔案還救得回來嗎?

首先我們要知道使用 git reset 把 commit 拆掉並不真的拆掉它,這是為了方便理解所使用的,而比較正確的解釋是要「前往」到某個指定的 commit 紀錄,所以即使使用了 --hard 參數也可以隨時把紀錄撿回來。

我們在上面介紹 --hard 參數時把 commit 往前回到了 dfaa0f3:

$ git log --oneline
dfaa0f3 初始版本

而我們在一開始有說 git reset 是「前往」指定的 commit,所以我們如果要回到文章一開始的紀錄的話只需要使用 git reset 回到 1db41c6:

$ git reset 1db41c6

這樣剛才使用 --hard 參數拆掉的 commit 就又回來了,如果在使用 --hard 參數後有做檔案的修改,但在那之後想回到使用 git reset --hard 之前並且不要那些修改紀錄時,可以在這裡使用 --hard 強迫放棄修改的檔案。

如果忘記 commit 值(SHA-1 值)

如果沒記下要回去的 commit 值時,我們可以用 git reflog 或是 git log -g 來查看變更紀錄,在這裡以剛才的範例為例:

$ git reflog
dfaa0f3 HEAD@{0}: reset: moving to dfaa0f3
1db41c6 HEAD@{1}: commit: docs: 新增 helloWorld.txt 註解
05ca78e HEAD@{2}: commit: docs: 新增 test.txt
042ee26 HEAD@{3}: commit: docs: 新增道別規矩

dfaa0f3 HEAD@{0}: reset: moving to dfaa0f3 這段是我們使用 git reset 回到 dfaa0f3 的變更紀錄,而下面的 1db41c6 HEAD@{1}: commit: docs: 新增 helloWorld.txt 註解 就是我們在使用 git reset --hard 前的變更紀錄。


參考資料
  • 高見龍,《為你自己學 Git》

#Git







Related Posts

React-[串接api篇]-註冊功能發送post ajax call

React-[串接api篇]-註冊功能發送post ajax call

TypeScript 函式定義字串型別相連和JavaScript String.prototype.concat() 的差異

TypeScript 函式定義字串型別相連和JavaScript String.prototype.concat() 的差異

Linkedin Java 認證題庫

Linkedin Java 認證題庫


Comments