Git 重置 vs 恢復 vs Rebase
已發表: 2021-05-20在本文中,您將了解在 Git 中處理提交的不同方法。
作為一名開發人員,您可能會多次遇到這種情況,在這種情況下,您想要回滾到之前的提交之一,但不知道該怎麼做。 即使你知道像 reset、revert、rebase 這樣的 Git 命令,你也不知道它們之間的區別。 因此,讓我們開始並了解 git reset、revert 和 rebase 是什麼。
Git重置
Git reset 是一個複雜的命令,用於撤消更改。
您可以將 git reset 視為回滾功能。 使用 git reset,您可以在各種提交之間跳轉。 運行 git reset 命令有三種模式:–soft、–mixed 和–hard。 默認情況下,git reset 命令使用混合模式。 在 git reset 工作流中,git 的三個內部管理機制進入了畫面: HEAD 、暫存區(索引)和工作目錄。

工作目錄是您當前工作的地方,它是您的文件所在的地方。 使用 git status 命令,您可以查看工作目錄中存在的所有文件/文件夾。
暫存區(索引)是 git 跟踪和保存文件中所有更改的地方。 保存的更改反映在 .git 目錄中。 您可以使用 git add “filename” 將文件添加到暫存區。 和以前一樣,當您運行 git status 時,您將看到暫存區中存在哪些文件。
Git 中的當前分支稱為 HEAD。 它指向在當前結帳分支中發生的最後一次提交。 它被視為任何引用的指針。 一旦您結帳到另一個分支,HEAD 也會移動到新分支。
讓我解釋一下 git reset 在硬模式、軟模式和混合模式下是如何工作的。 硬模式用於轉到指向的提交,工作目錄填充有該提交的文件,並且暫存區被重置。 在軟重置中,僅將指針更改為指定的提交。 在重置之前,所有提交的文件都保留在工作目錄和暫存區中。 在混合模式(默認)下,指針和暫存區都被重置。
Git 硬重置
git hard reset 的目的是將 HEAD 移動到指定的提交。 它將刪除在指定提交之後發生的所有提交。 此命令將更改提交歷史記錄並指向指定的提交。
在這個例子中,我將添加三個新文件,提交它們,然後執行硬重置。
正如您從下面的命令中看到的,現在沒有什麼可以提交的。
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean現在,我將創建 3 個文件並向其中添加一些內容。
$ vi file1.txt $ vi file2.txt $ vi file3.txt將這些文件添加到現有存儲庫中。
$ git add file*當您重新運行 status 命令時,它將反映我剛剛創建的新文件。
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: file1.txt new file: file2.txt new file: file3.txt在提交之前,讓我告訴你,我目前在 Git 中有 3 次提交的日誌。
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test現在,我將提交到存儲庫。
$ git commit -m 'added 3 files' [master d69950b] added 3 files 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt如果我執行 ls-files,您將看到已添加新文件。
$ git ls-files demo dummyfile newfile file1.txt file2.txt file3.txt當我在 git 中運行 log 命令時,我有 4 個提交,並且 HEAD 指向最新的提交。
$ git log --oneline d69950b (HEAD -> master) added 3 files 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test如果我手動刪除 file1.txt 並執行 git status,它將顯示更改未暫存以進行提交的消息。
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) deleted: file1.txt no changes added to commit (use "git add" and/or "git commit -a")現在,我將運行硬重置命令。
$ git reset --hard HEAD is now at d69950b added 3 files如果我重新檢查狀態,我會發現沒有任何東西可以提交,我刪除的文件又回到了存儲庫中。 發生回滾是因為刪除文件後,我沒有提交,所以硬重置後,它又回到了以前的狀態。
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean如果我檢查 git 的日誌,這就是它的外觀。
$ git log commit d69950b7ea406a97499e07f9b28082db9db0b387 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 19:53:31 2020 +0530 added 3 files commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test硬重置的目的是指向指定的提交並更新工作目錄和暫存區。 讓我再給你看一個例子。 目前,我提交的可視化如下所示:

在這裡,我將使用 HEAD^ 運行命令,這意味著我想重置為上一次提交(返回一次提交)。
$ git reset --hard HEAD^ HEAD is now at 0db602e one more commit您可以看到頭指針現在已從 d69950b 更改為 0db602e。
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test 
如果查看日誌,d69950b 的提交已經消失,頭部現在指向 0db602e SHA。
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 Test如果您運行 ls-files,您可以看到 file1.txt、file2.txt 和 files3.txt 不再在存儲庫中,因為該提交及其文件在硬重置後已被刪除。
$ git ls-files demo dummyfile newfileGit 軟重置
同樣,現在我將向您展示一個軟復位的示例。 考慮一下,我已經如上所述再次添加了 3 個文件並提交了它們。 git 日誌將顯示如下。 你可以看到“軟重置”是我最新的提交,HEAD 也指向了這一點。
$ git log --oneline aa40085 (HEAD -> master) soft reset 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test可以使用以下命令查看日誌中提交的詳細信息。
$ git log commit aa400858aab3927e79116941c715749780a59fc9 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 21:01:36 2020 +0530 soft reset commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test現在使用軟重置,我想切換到 SHA 0db602e085a4d59cfa9393abac41ff5fd7afcb14 的舊提交之一

為此,我將運行以下命令。 您需要傳遞 SHA 的 6 個以上的起始字符,不需要完整的 SHA。
$ git reset --soft 0db602e085a4現在,當我運行 git log 時,我可以看到 HEAD 已重置為我指定的提交。
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test但這裡的區別在於,我添加了 3 個文件的提交 (aa400858aab3927e79116941c715749780a59fc9) 的文件仍在我的工作目錄中。 他們沒有被刪除。 這就是為什麼您應該使用軟重置而不是硬重置的原因。 在軟模式下沒有丟失文件的風險。
$ git ls-files demo dummyfile file1.txt file2.txt file3.txt newfileGit 還原
在 Git 中,revert 命令用於執行還原操作,即還原某些更改。 它類似於 reset 命令,但唯一的區別是您執行新的提交以返回到特定的提交。 簡而言之,可以公平地說 git revert 命令是一個提交。
Git revert 命令在執行還原操作時不會刪除任何數據。
假設我正在添加 3 個文件並為恢復示例執行 git commit 操作。
$ git commit -m 'add 3 files again' [master 812335d] add 3 files again 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt日誌將顯示新的提交。
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test現在我想恢復到我過去的提交之一,比方說——“59c86c9 新提交”。 我會運行下面的命令。
$ git revert 59c86c9這將打開一個文件,您將找到您嘗試恢復的提交的詳細信息,您可以在此處為新提交命名,然後保存並關閉該文件。
Revert "new commit" This reverts commit 59c86c96a82589bad5ecba7668ad38aa684ab323. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # Your branch is ahead of 'origin/master' by 4 commits. # (use "git push" to publish your local commits) # # Changes to be committed: # modified: dummyfile保存並關閉文件後,這就是您將獲得的輸出。
$ git revert 59c86c9 [master af72b7a] Revert "new commit" 1 file changed, 1 insertion(+), 1 deletion(-)現在進行必要的更改,與重置不同,revert 執行了一個新的提交。 如果再次查看日誌,會發現由於進行了還原操作,會出現新的提交。
$ git log --oneline af72b7a (HEAD -> master) Revert "new commit" 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test 
Git 日誌將包含所有提交的歷史記錄。 如果您想從歷史記錄中刪除提交,那麼 revert 不是一個好的選擇,但是如果您想在歷史記錄中維護提交更改,那麼 revert 是合適的命令而不是重置。
Git變基
在 Git 中,rebase 是一種將一個分支的提交移動或組合到另一個分支上的方式。 作為開發人員,我不會在真實場景中在 master 分支上創建我的功能。 我會在我自己的分支(一個“功能分支”)上工作,當我在我的功能分支中有一些提交並添加了該功能時,我想將它移動到主分支。
Rebase 有時會讓人有點難以理解,因為它與合併非常相似。 合併和變基的目標是從我的功能分支中獲取提交並將它們放在主分支或任何其他分支上。 考慮一下,我有一個看起來像這樣的圖表:

假設您與其他開發人員在一個團隊中工作。 在這種情況下,您可以想像這會變得非常複雜,因為您有一群其他開發人員在不同的功能分支上工作,並且他們一直在合併多個更改。 追踪變得混亂。
所以,這就是 rebase 將提供幫助的地方。 這一次,我將做一個 rebase,而不是執行 git merge,我想將我的兩個功能分支提交並將它們移動到 master 分支。 變基將從功能分支中獲取我的所有提交,並將它們移動到主分支提交的頂部。 因此,在幕後,git 正在復制主分支上的功能分支提交。

這種方法將為您提供一個乾淨的直線圖,其中包含連續的所有提交。

它可以輕鬆跟踪提交的內容。 你可以想像如果你在一個有很多開發人員的團隊中,所有的提交仍然是連續的。 因此,即使您有很多人同時在同一個項目上工作,也很容易遵循。
讓我向你展示這一點。
這就是我的主分支目前的樣子。 它有 4 次提交。
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test我將運行以下命令來創建並切換到一個名為 feature 的新分支,該分支將從第二次提交創建,即 59c86c9
(master) $ git checkout -b feature 59c86c9 Switched to a new branch 'feature'如果您檢查功能分支中的日誌,它只有 2 個來自 master(主線)的提交。
(feature) $ git log --oneline 59c86c9 (HEAD -> feature) new commit e2f44fc (origin/master, origin/HEAD) test我將創建功能 1 並將其提交到功能分支。
(feature) $ vi feature1.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 1' [feature c639e1b] feature 1 1 file changed, 1 insertion(+) create mode 100644 feature1.txt我將在功能分支中再創建一個功能,即功能 2 並提交它。
(feature) $ vi feature2.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 2' [feature 0f4db49] feature 2 1 file changed, 1 insertion(+) create mode 100644 feature2.txt現在,如果你檢查功能分支的日誌,它有兩個新的提交,我在上面執行過。
(feature) $ git log --oneline 0f4db49 (HEAD -> feature) feature 2 c639e1b feature 1 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test現在我想將這兩個新功能添加到 master 分支。 為此,我將使用 rebase 命令。 從功能分支,我將針對主分支進行變基。 這將做的是它將根據最新更改重新錨定我的功能分支。
(feature) $ git rebase master Successfully rebased and updated refs/heads/feature.現在我要繼續檢查主分支。
(feature) $ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits)最後,根據我的功能分支重新設置主分支。 這將在我的功能分支上執行這兩個新提交,並在我的主分支上重放它們。
(master) $ git rebase feature Successfully rebased and updated refs/heads/master.現在,如果我檢查 master 分支上的日誌,我可以看到我的 features 分支的兩個提交已成功添加到我的 master 分支。
(master) $ git log --oneline 766c996 (HEAD -> master, feature) feature 2 c036a11 feature 1 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test這就是關於 Git 中的 reset、revert 和 rebase 命令的全部內容。
結論
這就是關於 Git 中的 reset、revert 和 rebase 命令的全部內容。 我希望這個分步指南對您有所幫助。 現在,您知道如何使用文章中提到的命令根據需要處理提交。
