在Git中撤消更改的六種方法總結(jié)
當(dāng)使用 Git 進(jìn)行項(xiàng)目代碼管理時(shí),難免會(huì)出現(xiàn)一些錯(cuò)誤操作或需求變更,需要對(duì)代碼進(jìn)行撤銷(xiāo)或修改。Git 提供了多種方式來(lái)撤消已有的更改。本文將介紹 Git 中常用的 6 種撤消更改的方法,幫助你更好地處理這些問(wèn)題!
在開(kāi)始示例之前,先來(lái)創(chuàng)建一個(gè)簡(jiǎn)單的 Git 倉(cāng)庫(kù),其中包含于一些提交:
git init && \ echo {} > package.json && git add . && git commit -m "Add package.json" && \ echo FOO=bar > .env && git add . && git commit -m "Add .env" && \ touch README.md && git add . && git commit -m "Add README" && \ touch .gitignore && git add . && git commit -m "Add .gitignore"
對(duì)于這些命令,實(shí)際上包含以下歷史操作:
* 4753e23 - (HEAD -> master) Add .gitignore (4 seconds ago) <AleksandrHovhannisyan> * 893d18d - Add README (4 seconds ago) <AleksandrHovhannisyan> * 2beb7c7 - Add .env (4 seconds ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (5 seconds ago) <AleksandrHovhannisyan>
1. 修改最近的提交
在創(chuàng)建并提交了 .gitignore
文件后不久,決定修改這個(gè)文件:
echo node_modules > .gitignore
但不想在 git 日志歷史記錄中添加一個(gè)對(duì)于如此微小更改的提交記錄。或者需要在最近的提交消息中糾正一個(gè)拼寫(xiě)錯(cuò)誤。
這兩種情況都是使用 git amend
命令的經(jīng)典用例:
git commit -a --amend
簡(jiǎn)單來(lái)說(shuō),git amend 命令用于在 git 中編輯 commit 和提交消息。這是 git 中撤銷(xiāo)更改的最基本方式之一。
當(dāng)運(yùn)行上述代碼時(shí),git 會(huì)打開(kāi)選擇的編輯器并顯示最近的提交,在其中加入更改以進(jìn)入暫存環(huán)境:
Add .gitignore # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Sun Oct 11 08:25:58 2022 -0400 # # On branch master # Changes to be committed: # new file: .gitignore #
保存并關(guān)閉文件,git 將修改最近的提交以包括新更改。也可以在保存文件之前編輯提交消息。
如果要做的只是更新提交消息本身,例如修正一個(gè)拼寫(xiě)錯(cuò)誤,那實(shí)際上并不需要進(jìn)入暫存環(huán)境。只需要運(yùn)行這個(gè)命令:
git commit --amend
在編輯器中更改提交消息并保存文件,關(guān)閉即可。
在修改了最近的提交后,日志將會(huì)看起來(lái)像這樣:
* 7598875 - (HEAD -> master) Add .gitignore (31 seconds ago) <AleksandrHovhannisyan> * 893d18d - Add README (79 seconds ago) <AleksandrHovhannisyan> * 2beb7c7 - Add .env (79 seconds ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (80 seconds ago) <AleksandrHovhannisyan>
現(xiàn)在,假設(shè)修改之前已經(jīng)將舊提交推送到了遠(yuǎn)程分支。如果運(yùn)行 git status
,就會(huì)被告知本地分支和遠(yuǎn)程分支有一個(gè)提交不同:
On branch master Your branch and 'origin/master' have diverged, and have 1 and 1 different commits each, respectively. (use "git pull" to merge the remote branch into yours)
這也是正常的,因?yàn)檫h(yuǎn)程分支有舊的提交,而本地分支有修改過(guò)的提交。它們的哈希值不同,因?yàn)樾薷奶峤粫?huì)更改其時(shí)間戳,這會(huì)強(qiáng)制 git 計(jì)算新的哈希值。要想用新的提交更新遠(yuǎn)程分支,就需要強(qiáng)制推送它:git push -f
。這將用本地分支覆蓋遠(yuǎn)程分支的歷史記錄。
需要注意,這里我們是在自己分支上進(jìn)行的強(qiáng)制推送,在實(shí)際工作中,我們不應(yīng)該強(qiáng)制推送到公共分支;如果這樣做,每個(gè)人的本地 master 副本都將與遠(yuǎn)程副本不同,并且任何基于舊 master 的新功能現(xiàn)在都將具有不兼容的提交歷史記錄。
2. 將分支重置為較舊的提交
目前為止,我們有如下提交歷史:
* 7598875 - (HEAD -> master) Add .gitignore (31 seconds ago) <AleksandrHovhannisyan> * 893d18d - Add README (79 seconds ago) <AleksandrHovhannisyan> * 2beb7c7 - Add .env (79 seconds ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (80 seconds ago) <AleksandrHovhannisyan>
我們?cè)賮?lái)向 master 添加一個(gè)提交:
touch file && git add . && git commit -m "Add a file"
現(xiàn)在提交歷史變成了這樣:
* b494f6f - (HEAD -> master) Add a file (5 seconds ago) <AleksandrHovhannisyan> * 7598875 - Add .gitignore (3 minutes ago) <AleksandrHovhannisyan> * 893d18d - Add README (4 minutes ago) <AleksandrHovhannisyan> * 2beb7c7 - Add .env (4 minutes ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (4 minutes ago) <AleksandrHovhannisyan>
幾分鐘后,出于某種原因,我們決定不再保留最近的提交。要想刪除它,只需在 HEAD 指針之前硬重置一次提交,該指針始終指向當(dāng)前分支上的最新提交:
git reset --hard HEAD~1
波浪號(hào) (~) 后跟一個(gè)數(shù)字告訴 git 它應(yīng)該從給定的提交(在本例中為 HEAD 指針)回溯多少次提交。由于 HEAD 總是指向當(dāng)前分支上的最新提交,這告訴 git 對(duì)最近提交之前的提交進(jìn)行硬重置。
輸出結(jié)果如下:
HEAD is now at 7598875 Add .gitignore
硬重置是撤消 git 更改的一個(gè)便捷方法,但這是一個(gè)破壞性過(guò)程——該提交中的所有更改都將丟失,找回它們的唯一方法是通過(guò) git reflog
命令(后面會(huì)詳細(xì)介紹)。
我們還可以重置為 HEAD~nth 提交,在這種情況下,該提交期間和之后的所有工作都將丟失:
git reset --hard HEAD~4
或者甚至是特定的提交,如果有它的哈希:
git reset --hard <hash-id>
當(dāng)然也不限于僅針對(duì)當(dāng)前分支中的提交進(jìn)行重置,還可以重置本地分支以指向另一個(gè)本地分支:
git reset --hard <someOtherBranch>
甚至到遠(yuǎn)程分支:
git reset --hard origin/master
這個(gè)就很有用,例如,如果不小心將內(nèi)容提交到本地 master 分支。假設(shè)應(yīng)該在一個(gè) feat/X 分支上進(jìn)行提交,但忘記了創(chuàng)建它,而且一直在向本地 master 提交代碼。
當(dāng)然,我們也可以使用 git cherry-pick
來(lái)解決這個(gè)問(wèn)題,但是如果有很多次提交怎么辦?這有點(diǎn)痛苦,而 reset 可以輕松搞定。
要解決此問(wèn)題,現(xiàn)在需要?jiǎng)?chuàng)建功能分支:
git checkout -b feat/X
并強(qiáng)行將本地 master 分支重置為遠(yuǎn)程 master 分支:
git checkout master && git reset --hard origin/master
并且不要忘記回到功能分支,這樣就不會(huì)重復(fù)同樣的錯(cuò)誤:
git checkout feat/X
軟重置分支
正如上面提到的,如果進(jìn)行硬重置,將丟失在該提交時(shí)或之后所做的所有工作。當(dāng)然,可以從那個(gè)狀態(tài)中恢復(fù)過(guò)來(lái),但需要額外的操作。相反,如果想在 git 的暫存環(huán)境中保留更改,可以進(jìn)行軟重置:
git reset --soft HEAD~1
同樣,可以只使用提交哈希而不是從 HEAD 指針回溯:
git reset --soft a80951b
該提交引入的所有更改以及它之后的任何提交都將出現(xiàn)在 git 的暫存環(huán)境中。在這里,可以使用 git reset HEAD file(s)
取消暫存文件,對(duì)已經(jīng)暫存的文件進(jìn)行所需的任何更改。然后,可以根據(jù)需要進(jìn)行任何新的提交。
用例:在一個(gè)提交中提交了文件 A 和文件 B,但后來(lái)意識(shí)到它們實(shí)際上應(yīng)該是兩個(gè)獨(dú)立的提交。可以執(zhí)行軟重置并選擇性地提交一個(gè)文件,然后單獨(dú)進(jìn)行另一個(gè)提交,所有這些操作都不會(huì)丟失任何工作內(nèi)容。
創(chuàng)建備份分支
我們可以將分支用作備份機(jī)制,以防你知道即將運(yùn)行的某個(gè)命令(例如 git reset --hard
)可能會(huì)損壞分支的提交歷史記錄。在運(yùn)行這些命令之前,可以簡(jiǎn)單地創(chuàng)建一個(gè)臨時(shí)備份分支(例如 git branch backup
)。如果出現(xiàn)任何問(wèn)題,就可以針對(duì)備份分支執(zhí)行硬重置操作:
git reset --hard backup
3. 交互式變基
Git 的交互式變基是其最強(qiáng)大、最靈活的命令之一,允許倒回歷史并進(jìn)行任何所需的更改。如果想要?jiǎng)h除舊的提交、更改舊的提交消息或者將舊的提交壓縮成其他的提交,那么這就是你需要使用的命令。所有交互式變基都始于 git rebase -i
命令,并且必須指定一個(gè)提交來(lái)重新設(shè)置當(dāng)前分支。
刪除舊提交
目前為止,我們有如下提交歷史:
* 7598875 - (HEAD -> master) Add .gitignore (20 minutes ago) <AleksandrHovhannisyan> * 893d18d - Add README (21 minutes ago) <AleksandrHovhannisyan> * 2beb7c7 - Add .env (21 minutes ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (21 minutes ago) <AleksandrHovhannisyan>
第二次提交看起來(lái)有點(diǎn)可疑,為什么要將本地環(huán)境變量 (.env) 檢查到 git 中? 顯然,我們需要?jiǎng)h除此提交,同時(shí)保留所有其他提交。為此,我們將針對(duì)該提交運(yùn)行交互式變基:
git rebase -ir 2beb7c7^
這將調(diào)出這個(gè)編輯器:
pick 2beb7c7 Add .env pick 893d18d Add README pick 7598875 Add .gitignore
要?jiǎng)h除 2beb7c7,需要將 pick 命令更改為 drop(或 d)并保持其他不變:
drop 2beb7c7 Add .env pick 893d18d Add README pick 7598875 Add .gitignore
現(xiàn)在關(guān)閉并保存文件,會(huì)得到如下確認(rèn):
Successfully rebased and updated refs/heads/master.
現(xiàn)在,如果執(zhí)行 git log
,將不再看到該提交:
* 11221d4 - (HEAD -> master) Add .gitignore (6 seconds ago) <AleksandrHovhannisyan> * 9ed001a - Add README (6 seconds ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (50 minutes ago) <AleksandrHovhannisyan>
注意,在已刪除提交之后的所有提交哈希都將被重新計(jì)算。因此,雖然根提交仍保持為 0beebfb
,但在它之后的所有哈希值都已更改。正如現(xiàn)在已經(jīng)看到幾次,如果之前將此分支推送到了遠(yuǎn)程倉(cāng)庫(kù)中,那么本地分支和遠(yuǎn)程分支現(xiàn)在將不同步。因此,只需要進(jìn)行一次強(qiáng)制推送即可更新遠(yuǎn)程分支:
git push -f
改寫(xiě)提交消息
我們?cè)賮?lái)添加兩個(gè)提交,提交消息是隨便寫(xiě)的:
* 094f8cb - (HEAD -> master) Do more stuff (1 second ago) <AleksandrHovhannisyan> * 74dab36 - Do something idk (59 seconds ago) <AleksandrHovhannisyan> * 11221d4 - Add .gitignore (3 minutes ago) <AleksandrHovhannisyan> * 9ed001a - Add README (3 minutes ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (53 minutes ago) <AleksandrHovhannisyan>
現(xiàn)在想修改這兩個(gè)提交消息。我們將從交互式變基開(kāi)始。在這里,我們將針對(duì)最后兩個(gè)提交:
git rebase -i HEAD~2
這將打開(kāi)編輯器:
pick 74dab36 Do something idk pick 094f8cb Do more stuff
現(xiàn)在,只需將任何要更改其消息的提交的 pick 替換為 r(或 reword):
reword 74dab36 Do something idk reword 094f8cb Do more stuff
關(guān)閉并保存文件,對(duì)于想要改寫(xiě)的每個(gè)提交,git 將打開(kāi)編輯器,就像正在修改該提交一樣,允許編輯消息。
也許在第一次提交時(shí)這樣做:
Update README with getting started instructions # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Sun Oct 11 09:17:41 2022 -0400 # # interactive rebase in progress; onto 11221d4 # Last command done (1 command done): # reword 74dab36 Do something idk # Next command to do (1 remaining command): # reword 094f8cb Do more stuff # You are currently editing a commit while rebasing branch 'master' on '11221d4'. # # Changes to be committed: # modified: README.md #
第二次這樣做:
Add name and author to package.json # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # interactive rebase in progress; onto 11221d4 # Last commands done (2 commands done): # reword 74dab36 Do something idk # reword 094f8cb Do more stuff # No commands remaining. # You are currently rebasing branch 'master' on '11221d4'. # # Changes to be committed: # modified: package.json #
我們會(huì)得到以下輸出確認(rèn):
[detached HEAD 665034d] Update README with getting started instructions Date: Sun Oct 11 09:17:41 2020 -0400 1 file changed, 5 insertions(+) [detached HEAD ba88fb0] Add name and author to package.json 1 file changed, 4 insertions(+), 1 deletion(-) Successfully rebased and updated refs/heads/master.
現(xiàn)在提交歷史看起來(lái)像這樣:
* ba88fb0 - (HEAD -> master) Add name and author to package.json (31 seconds ago) <AleksandrHovhannisyan> * 665034d - Update README with getting started instructions (53 seconds ago) <AleksandrHovhannisyan> * 11221d4 - Add .gitignore (6 minutes ago) <AleksandrHovhannisyan> * 9ed001a - Add README (6 minutes ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (56 minutes ago) <AleksandrHovhannisyan>
編輯舊提交
編輯提交就意味著在提交之后立即轉(zhuǎn)到歷史記錄中的那個(gè)點(diǎn)。這允許修改提交并包含(或刪除)想要的任何更改。
目前為止,我們有如下提交歷史:
* ba88fb0 - (HEAD -> master) Add name and author to package.json (31 seconds ago) <AleksandrHovhannisyan> * 665034d - Update README with getting started instructions (53 seconds ago) <AleksandrHovhannisyan> * 11221d4 - Add .gitignore (6 minutes ago) <AleksandrHovhannisyan> * 9ed001a - Add README (6 minutes ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (56 minutes ago) <AleksandrHovhannisyan>
假設(shè)要編輯根提交(0beebfb)并添加第二個(gè)文件:
touch .yarnrc
我們將針對(duì)該提交使用交互式變基,在這種編輯根提交的特殊情況下,需要使用 --root
選項(xiàng):
git rebase -i --root
這將打開(kāi)編輯器,按時(shí)間順序顯示提交:
pick 0beebfb Add package.json pick 9ed001a Add README pick 11221d4 Add .gitignore pick 665034d Update README with getting started instructions pick ba88fb0 Add name and author to package.json
我們需要做的就是為該列表中的第一個(gè)提交將 pick 替換為 edit :
edit 0beebfb Add package.json pick 9ed001a Add README pick 11221d4 Add .gitignore pick 665034d Update README with getting started instructions pick ba88fb0 Add name and author to package.json
關(guān)閉并保存文件,應(yīng)該從 git 中看到這條消息:
Stopped at 0beebfb... Add package.json You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue
下面來(lái)運(yùn)行這兩個(gè)命令:
git add .yarnrc && git commit --amend
現(xiàn)在只需要修改提交,編輯器應(yīng)如下所示:
Add package.json # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Sun Oct 11 08:25:57 2020 -0400 # # interactive rebase in progress; onto 666364d # Last command done (1 command done): # edit 0beebfb Add package.json # Next commands to do (4 remaining commands): # pick 9ed001a Add README # pick 11221d4 Add .gitignore # You are currently editing a commit while rebasing branch 'master' on '666364d'. # # # Initial commit # # Changes to be committed: # new file: .yarnrc # new file: package.json #
現(xiàn)在將該消息更改為 Initialize npm package
保存并退出?,F(xiàn)在,根據(jù) git 的建議,需要繼續(xù) rebase:
git rebase --continue
現(xiàn)在,提交歷史現(xiàn)在看起來(lái)像這樣:
* 436e421 - (HEAD -> master) Add name and author to package.json (6 seconds ago) <AleksandrHovhannisyan> * beb7c13 - Update README with getting started instructions (6 seconds ago) <AleksandrHovhannisyan> * 1c75f66 - Add .gitignore (6 seconds ago) <AleksandrHovhannisyan> * 69c997b - Add README (6 seconds ago) <AleksandrHovhannisyan> * 36210ec - Initialize npm package (56 seconds ago) <AleksandrHovhannisyan>
壓縮
壓縮可以將 n 個(gè)提交合并為一個(gè),使提交歷史更加緊湊。如果一個(gè)功能分支引入大量提交,并且只希望該功能在歷史記錄中表示為單個(gè)提交(稱(chēng)為 squash-and-rebase 工作流),這有時(shí)很有用。但是,如果將來(lái)需要,將無(wú)法恢復(fù)或修改舊的提交,這在某些情況下可能是不可取的。
同樣,作為參考,我們有如下提交歷史:
* 436e421 - (HEAD -> master) Add name and author to package.json (6 seconds ago) <AleksandrHovhannisyan> * beb7c13 - Update README with getting started instructions (6 seconds ago) <AleksandrHovhannisyan> * 1c75f66 - Add .gitignore (6 seconds ago) <AleksandrHovhannisyan> * 69c997b - Add README (6 seconds ago) <AleksandrHovhannisyan> * 36210ec - Initialize npm package (56 seconds ago) <AleksandrHovhannisyan>
下面來(lái)創(chuàng)建一個(gè)功能分支并添加一些提交:
git checkout -b feature && \ touch file1 && git add . && git commit -m "Add file1" && \ touch file2 && git add . && git commit -m "Add file2" && \ touch file3 && git add . && git commit -m "Add file3"
現(xiàn)在提交歷史如下:
* 6afa3ac - (HEAD -> feature) Add file3 (4 seconds ago) <AleksandrHovhannisyan> * c16cbc6 - Add file2 (4 seconds ago) <AleksandrHovhannisyan> * 0832e96 - Add file1 (4 seconds ago) <AleksandrHovhannisyan> * 436e421 - (master) Add name and author to package.json (12 minutes ago) <AleksandrHovhannisyan> * beb7c13 - Update README with getting started instructions (12 minutes ago) <AleksandrHovhannisyan> * 1c75f66 - Add .gitignore (12 minutes ago) <AleksandrHovhannisyan> * 69c997b - Add README (12 minutes ago) <AleksandrHovhannisyan> * 36210ec - Initialize npm package (12 minutes ago) <AleksandrHovhannisyan>
我們可以使用以下命令將所有這些壓縮為一個(gè):
git rebase -i master
這會(huì)將功能分支重新設(shè)置為 master 分支。請(qǐng)注意, master 是對(duì)特定提交的引用,就像其他任何提交一樣:
* 436e421 - (HEAD -> master) Add name and author to package.json (6 seconds ago) <AleksandrHovhannisyan>
這樣做也是一樣的:
git rebase -i 436e421
無(wú)論如何,一旦運(yùn)行了這些命令中的任何一個(gè),git 就會(huì)打開(kāi)編輯器:
pick 0832e96 Add file1 pick c16cbc6 Add file2 pick 6afa3ac Add file3
我們會(huì)將最后兩個(gè)提交壓縮到第一個(gè)提交中,所以將它們的 pick
命令更改為 squash
:
pick 0832e96 Add file1 squash c16cbc6 Add file2 squash 6afa3ac Add file3
保存并退出,git 將打開(kāi)編輯器,通知我們將要合并三個(gè)提交:
# This is a combination of 3 commits. # This is the 1st commit message: Add file1 # This is the commit message #2: Add file2 # This is the commit message #3: Add file3 # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Sun Oct 11 09:37:05 2020 -0400 # # interactive rebase in progress; onto 436e421 # Last commands done (3 commands done): # squash c16cbc6 Add file2 # squash 6afa3ac Add file3 # No commands remaining. # You are currently rebasing branch 'feature' on '436e421'. # # Changes to be committed: # new file: file1 # new file: file2 # new file: file3 #
現(xiàn)在可以將 Add file1 更改為 Add files 1、2 和 3,或者想要的任何其他提交消息。保存并關(guān)閉文件,現(xiàn)在提交歷史已經(jīng)很緊湊了:
* b646cf6 - (HEAD -> feature) Add files 1, 2, and 3 (70 seconds ago) <AleksandrHovhannisyan> * 436e421 - (master) Add name and author to package.json (14 minutes ago) <AleksandrHovhannisyan> * beb7c13 - Update README with getting started instructions (14 minutes ago) <AleksandrHovhannisyan> * 1c75f66 - Add .gitignore (14 minutes ago) <AleksandrHovhannisyan> * 69c997b - Add README (14 minutes ago) <AleksandrHovhannisyan> * 36210ec - Initialize npm package (15 minutes ago) <AleksandrHovhannisyan>
4. 還原提交
上面學(xué)習(xí)了兩種從 git 歷史記錄中刪除提交的方法:
在要?jiǎng)h除的提交范圍之前,將 HEAD 指針軟或硬重置為提交。
對(duì)不想保留的任何提交執(zhí)行交互式變基并更改
pick
為drop
。
不幸的是,這兩種方法都會(huì)重寫(xiě)提交歷史。以使用交互式變基從 master
分支中刪除 .env
文件為例。如果在現(xiàn)實(shí)中這樣做,在像 master
這樣的共享分支上刪除提交會(huì)導(dǎo)致一些麻煩,團(tuán)隊(duì)中的每個(gè)人都必須硬重置本地的 master
分支以匹配 origin/master
。
問(wèn)題出現(xiàn)在人們正在進(jìn)行功能分支上的工作時(shí),特別是如果他們是從舊的 master
分支切出來(lái)的——刪除的文件仍然存在。變基就是行不通的,因?yàn)樗赡軙?huì)重新引入在 master
分支上刪除的文件;同樣地,將 master
分支合并到功能分支中也不起作用,因?yàn)?git 沒(méi)有公共歷史可供解決:
fatal: refusing to merge unrelated histories
這就是 git revert
出現(xiàn)的原因。與通過(guò)變基或硬/軟重置刪除提交不同,revert 命令創(chuàng)建一個(gè)新提交以撤消目標(biāo)提交引入的任何更改:
git revert <hash-id>
假設(shè)在 master
分支上,想要用 beb7c13
的哈希恢復(fù)提交:
* 436e421 - (HEAD -> master) Add name and author to package.json (8 hours ago) <AleksandrHovhannisyan> * beb7c13 - Update README with getting started instructions (8 hours ago) <AleksandrHovhannisyan> * 1c75f66 - Add .gitignore (8 hours ago) <AleksandrHovhannisyan> * 69c997b - Add README (8 hours ago) <AleksandrHovhannisyan> * 36210ec - Initialize npm package (8 hours ago) <AleksandrHovhannisyan>
為此,執(zhí)行以下命令:
git revert beb7c13
Git 將打開(kāi)編輯器:
Revert "Update README with getting started instructions" This reverts commit beb7c132882ff1e3214dbd380514559fed0ef38f. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # Changes to be committed: # modified: README.md #
保存并關(guān)閉文件,然后運(yùn)行 git log
以查看歷史記錄:
* e1e6e06 - (HEAD -> master) Revert "Update README with getting started instructions" (58 seconds ago) <AleksandrHovhannisyan> * 436e421 - Add name and author to package.json (8 hours ago) <AleksandrHovhannisyan> * beb7c13 - Update README with getting started instructions (8 hours ago) <AleksandrHovhannisyan> * 1c75f66 - Add .gitignore (8 hours ago) <AleksandrHovhannisyan> * 69c997b - Add README (8 hours ago) <AleksandrHovhannisyan> * 36210ec - Initialize npm package (8 hours ago) <AleksandrHovhannisyan>
注意,原始提交仍然存在于歷史記錄中,并且其哈希值得以保留。唯一改變的是在分支的頂部添加了一個(gè)新的提交,以還原此前提交所引入的更改,就好像手動(dòng)刪除了最初引入的更改。顯然,使用 git revert 比手工操作更合理。
因此,與交互式變基或重置相比,撤銷(xiāo)提交會(huì)引入額外的一個(gè)提交,因此會(huì)更加混亂。但這并不是非常重要的問(wèn)題。而且,好處在于,它不會(huì)破壞公共分支。
5. 簽出文件
git checkout
命令是另一種撤消 git 更改的基本方法。它有三個(gè)目的:
創(chuàng)建新分支:
git checkout -b <newBranch>
。切換到分支或提交:
git checkout <existingBranch>
。恢復(fù)不同版本的文件。
這里我們將重點(diǎn)關(guān)注第三個(gè)目的。如果對(duì)本地文件進(jìn)行了未暫存的更改,則可以使用 checkout
命令輕松撤消這些更改:
git checkout <pathspec>
這里,<pathspec>
可以是任何有效的路徑說(shuō)明符,例如:.
對(duì)于當(dāng)前目錄、path/to/file
、file.extension
,甚至是正則表達(dá)式。
這將清除對(duì)指定文件的所有未暫存更改并恢復(fù)當(dāng)前分支的文件的未修改版本。此命令不會(huì)影響暫存文件——只會(huì)清除未暫存的更改。
例如,如果想清除當(dāng)前目錄中所有未暫存的更改并從頭開(kāi)始,最簡(jiǎn)單的方法是使用 git checkout
命令和 .
作為路徑規(guī)范:
git checkout .
我們也可以使用 git checkout
來(lái)恢復(fù)文件的本地或遠(yuǎn)程版本。例如,可以簽出遠(yuǎn)程 master
分支上的某個(gè)文件:
git checkout origin/master -- <pathspec>
這個(gè)命令的作用是將遠(yuǎn)程分支 origin/master
上指定的文件 <pathspec>
簽出到本地分支上。該命令會(huì)用遠(yuǎn)程分支上的文件覆蓋本地分支上的同名文件,即用遠(yuǎn)程分支的版本替換本地分支的版本,從而確保本地分支與遠(yuǎn)程分支保持同步。
同樣,也可以簽出另一個(gè)本地分支上的某個(gè)文件:
git checkout localBranchName -- <pathspec>
6. 使用 Git Reflog
可以將 reflog 視為 git 的 git — 就像一個(gè)內(nèi)部記錄保存系統(tǒng),可以跟蹤大部分操作。
reflog 代表“參考日志”:HEAD 指針隨時(shí)間的不同狀態(tài)的一系列快照。這意味著任何時(shí)候引入、刪除或修改提交,或者簽出新分支,或者重寫(xiě)舊提交的哈希,這些更改都將記錄在 reflog 中。我們將能夠回到過(guò)去撤消可能不需要的更改,即使它們看似不可逆轉(zhuǎn)。
查看 Git 倉(cāng)庫(kù)的 reflog
的方式如下:
git reflog
例如,在功能分支上可以簽出一個(gè)新分支,git 將記錄該活動(dòng):
git checkout -b feature2
Reflog:
b646cf6 (HEAD -> feature2, origin/feature, feature) HEAD@{0}: checkout: moving from feature to feature2
這是因?yàn)?HEAD 指針從功能分支的首端重定向到新分支 feature2 的首端。
如果深入挖掘 reflog
,還可以查看本文中的所有更改:
b646cf6 (HEAD -> feature2, origin/feature, feature) HEAD@{0}: checkout: moving from feature to feature2 b646cf6 (HEAD -> feature2, origin/feature, feature) HEAD@{1}: rebase -i (finish): returning to refs/heads/feature b646cf6 (HEAD -> feature2, origin/feature, feature) HEAD@{2}: rebase -i (squash): Add files 1, 2, and 3 f3def0a HEAD@{3}: rebase -i (squash): # This is a combination of 2 commits. 0832e96 HEAD@{4}: rebase -i (start): checkout 436e421 6afa3ac HEAD@{5}: commit: Add file3 c16cbc6 HEAD@{6}: commit: Add file2 0832e96 HEAD@{7}: commit: Add file1 436e421 (master) HEAD@{8}: checkout: moving from master to feature 436e421 (master) HEAD@{9}: rebase -i (finish): returning to refs/heads/master 436e421 (master) HEAD@{10}: rebase -i (pick): Add name and author to package.json beb7c13 HEAD@{11}: rebase -i (pick): Update README with getting started instructions 1c75f66 HEAD@{12}: rebase -i (pick): Add .gitignore 69c997b HEAD@{13}: rebase -i (pick): Add README 36210ec HEAD@{14}: commit (amend): Initialize npm package 04ba759 HEAD@{15}: rebase -i (edit): Add package.json 2bef9d4 HEAD@{16}: rebase -i (edit): Add package.json 666364d HEAD@{17}: rebase -i (start): checkout 666364da6703fc41e23515b1777de5ac84c8ad5e ba88fb0 HEAD@{18}: rebase -i (finish): returning to refs/heads/master ba88fb0 HEAD@{19}: rebase -i (reword): Add name and author to package.json 665034d HEAD@{20}: rebase -i (reword): Update README with getting started instructions 74dab36 HEAD@{21}: rebase -i: fast-forward 11221d4 HEAD@{22}: rebase -i (start): checkout HEAD~2 094f8cb HEAD@{23}: commit: Do more stuff 74dab36 HEAD@{24}: commit: Do something idk 11221d4 HEAD@{25}: rebase -i (finish): returning to refs/heads/master 11221d4 HEAD@{26}: rebase -i (pick): Add .gitignore 9ed001a HEAD@{27}: rebase -i (pick): Add README 0beebfb HEAD@{28}: rebase -i (start): checkout 2beb7c7^ 7598875 HEAD@{29}: reset: moving to HEAD~1 b494f6f HEAD@{30}: commit: Add a file 7598875 HEAD@{31}: commit (amend): Add .gitignore 4753e23 HEAD@{32}: commit: Add .gitignore 893d18d HEAD@{33}: commit: Add README 2beb7c7 HEAD@{34}: commit: Add .env 0beebfb HEAD@{35}: commit (initial): Add package.json
可以通過(guò)檢查這些提交哈希來(lái)快速查看這些狀態(tài)中的任何一個(gè):
git checkout <hash-id>
或者,可以將分支重置為歷史記錄中的某個(gè)點(diǎn):
git reset --soft 7598875
這將當(dāng)前的 feature2
分支軟重置為以下歷史:
* 7598875 - (HEAD -> feature2) Add .gitignore (84 minutes ago) <AleksandrHovhannisyan> * 893d18d - Add README (85 minutes ago) <AleksandrHovhannisyan> * 2beb7c7 - Add .env (85 minutes ago) <AleksandrHovhannisyan> * 0beebfb - Add package.json (85 minutes ago) <AleksandrHovhannisyan>
甚至可以運(yùn)行另一個(gè) reflog
來(lái)查看更改:
7598875 (HEAD -> feature2) HEAD@{0}: reset: moving to 7598875
如果不希望本地分支被覆蓋,可以再次運(yùn)行 reflog
命令并將分支重置到在執(zhí)行該操作之前的 HEAD
:
git reset --hard b646cf6
這就回到了之前的狀態(tài):
* b646cf6 - (HEAD -> feature2, origin/feature, feature) Add files 1, 2, and 3 (13 minutes ago) <AleksandrHovhannisyan> * 436e421 - (master) Add name and author to package.json (26 minutes ago) <AleksandrHovhannisyan> * beb7c13 - Update README with getting started instructions (26 minutes ago) <AleksandrHovhannisyan> * 1c75f66 - Add .gitignore (26 minutes ago) <AleksandrHovhannisyan> * 69c997b - Add README (26 minutes ago) <AleksandrHovhannisyan> * 36210ec - Initialize npm package (27 minutes ago) <AleksandrHovhannisyan>
Git 的 reflog
命令很有用,以防進(jìn)行硬重置并丟失所有工作,只需查看 reflog 并重置到進(jìn)行硬重置之前的點(diǎn),就輕松搞定!
最后,如果出于某種原因想清理 reflog,可以使用以下方法從中刪除行:
git reflog delete HEAD@{n}
將 n
替換為要從 reflog
中刪除的任何行。HEAD@{0}
指的是 reflog 中的最新行,HEAD@{1}
指的是之前的一行,依此類(lèi)推。
總結(jié)
到此這篇關(guān)于在Git中撤消更改的六種方法總結(jié)的文章就介紹到這了,更多相關(guān)Git撤消更改內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VSCode讓終端默認(rèn)在當(dāng)前文件的路徑啟動(dòng)(方法推薦)
這篇文章主要介紹了VSCode中如何讓終端默認(rèn)在當(dāng)前文件的路徑啟動(dòng),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03VsCode配置ssh免密遠(yuǎn)程連接服務(wù)器的實(shí)現(xiàn)步驟
現(xiàn)在,可以在VSCode中直接通過(guò)SSH連接到服務(wù)器,而無(wú)需每次輸入密碼,本文主要介紹了VsCode配置ssh免密遠(yuǎn)程連接服務(wù)器的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08git 入門(mén)教程之本地倉(cāng)庫(kù)和遠(yuǎn)程倉(cāng)庫(kù)的本質(zhì)介紹
本地倉(cāng)庫(kù)和遠(yuǎn)程倉(cāng)庫(kù)在本質(zhì)上沒(méi)有太大區(qū)別,只不過(guò)一個(gè)是本地電腦,一個(gè)是遠(yuǎn)程電腦.這篇文章主要介紹了git 入門(mén)教程之本地和遠(yuǎn)程倉(cāng)庫(kù)的本質(zhì)介紹,需要的朋友可以參考下2020-08-08游戲開(kāi)發(fā)Unity2D圖片任意形狀破碎裂片效果展示
本篇文章屬于游戲開(kāi)發(fā)Unity進(jìn)階篇,主要介紹了游戲開(kāi)發(fā)Unity2D圖片任意形狀破碎裂片效果展示,有需要的朋友可以借鑒參考下,希望可以有所幫助2021-09-09vscode配置遠(yuǎn)程開(kāi)發(fā)與免密登錄的技巧
這篇文章主要介紹了vscode配置遠(yuǎn)程開(kāi)發(fā)與免密登錄的技巧,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04vs2022使用git同步報(bào)錯(cuò)以及解決每次推送要輸入密碼問(wèn)題
本文主要介紹了vs2022使用git同步報(bào)錯(cuò)以及解決每次推送要輸入密碼問(wèn)題,文中通過(guò)圖文示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-10-10Chrome瀏覽器中清除特定網(wǎng)站的Cookie數(shù)據(jù)三種方法
當(dāng)我們?cè)谑褂秒娔X瀏覽網(wǎng)頁(yè)時(shí),服務(wù)器會(huì)生成一個(gè)證書(shū)并將其返回給電腦,這個(gè)證書(shū)是cookie,也可以稱(chēng)為瀏覽器緩存,這篇文章主要給大家介紹了關(guān)于Chrome瀏覽器中清除特定網(wǎng)站的Cookie數(shù)據(jù)三種方法,需要的朋友可以參考下2023-10-10VScode內(nèi)接入deepseek包過(guò)程記錄
在VSCode中集成本地部署的DeepSeek-R1模型,通過(guò)擴(kuò)展商店下載并配置Continue插件,實(shí)現(xiàn)模型的添加、連接和使用,過(guò)程中包括配置文件的修改、模型的刪除以及注意事項(xiàng)的處理,本文介紹VScode內(nèi)接入deepseek包過(guò)程,感興趣的朋友一起看看吧2025-02-02