[Git] 如何進行版本回退

版本控制系統最重要的能力之一,就是能夠輕松地在項目的不同歷史版本之間切換。有時,你可能發現最近的修改引入了嚴重問題,或者需要回到之前的某個節點重新開始。這時,“版本回退”功能就派上用場了。

版本回退:反方向的鐘~~

Git 提供了強大的版本回退(或稱為“重置”)功能,讓你能夠將項目狀態恢復到歷史上的任意一個提交點。執行版本回退的命令是 git reset

要理解 git reset,關鍵在于認識到它主要做了兩件事(或者說,你可以控制它做哪幾件事):

  1. 移動分支指針: Git 的版本歷史是一個由 Commit 對象組成的鏈條,每個 Commit 對象都有一個唯一的 ID。分支(比如 mastermain)本質上只是一個指向最新 Commit 對象的指針。git reset 命令首先會讓你選擇一個歷史的 Commit 對象,然后把當前分支的指針移動到你指定的那個 Commit 對象上。這樣一來,從這個 Commit 之后的版本就不再是當前分支的“歷史”了(至少暫時是這樣)。
  2. 重置暫存區和工作區(可選): 在移動分支指針之后,git reset 還可以根據你指定的選項,進一步修改暫存區工作區的內容,讓它們也回退到目標 Commit 時的狀態。

git reset 命令的基本語法是:

git reset [--soft | --mixed | --hard] [目標版本]

這里有幾個重要的部分需要解釋:

  • [目標版本] 你想回退到哪個歷史版本?你可以用以下方式指定:
    • 完整的 Commit ID 或部分 ID: 最精確的方式。你可以從 git loggit reflog 里復制某個提交的完整 ID,或者只需要足夠區分該提交的前幾位 ID 即可(通常 7-8 位就夠了)。
    • HEAD 表示當前分支最新的一次提交(也就是你當前所處的版本)。git reset HEAD 實際上是撤銷 git add 操作,將暫存區的改動移回工作區(這是 --mixed 模式下的默認行為)。
    • HEAD^ 表示當前版本的上一個版本。一個 ^ 表示往前回退一級。
    • HEAD^^ 表示上上個版本。
    • HEAD~數字~ 加上數字表示往前回退多少個版本。例如 HEAD~1 是上一個版本,HEAD~2 是上上個版本,HEAD~0 是當前版本。這在回退多個版本時比用 ^ 更方便。
  • [--soft | --mixed | --hard] 這是決定回退后,暫存區工作區狀態的關鍵參數。
    • --soft
      • 版本庫: 回退到指定的歷史版本(移動分支指針和 HEAD)。
      • 暫存區: 不變。保留回退前暫存區的內容。
      • 工作區: 不變。保留回退前工作區的內容。
      • 效果: 相當于撤銷了回退目標版本之后的所有 commit 操作,但保留了這些修改在暫存區和工作區。你可以重新 commit 這些改動(比如合并提交或修改提交信息)。
    • --mixed** (默認選項):**
      • 版本庫: 回退到指定的歷史版本(移動分支指針和 HEAD)。
      • 暫存區: 重置為目標版本時的狀態。也就是說,回退目標版本之后的改動會從暫存區中移除。
      • 工作區: 不變。保留回退前工作區的內容。
      • 效果: 撤銷了回退目標版本之后的所有 commit 操作,并清空了暫存區。回退目標版本之后的所有改動都會回到工作區,成為未暫存(unstaged)的狀態。這是最常用的模式,適合想撤銷提交,但又想保留代碼改動、重新組織提交的場景。git reset [目標版本] (不帶參數)默認就是 --mixed
    • --hard
      • 版本庫: 回退到指定的歷史版本(移動分支指針和 HEAD)。
      • 暫存區: 重置為目標版本時的狀態。
      • 工作區: 重置為目標版本時的狀態。
      • 效果: 這是一個非常徹底的回退!它會丟棄回退目標版本之后的所有暫存區和工作區的改動。就像你的項目狀態真的坐上了“時光機”,完全回到了那個歷史版本。【重要警告】:使用 --hard 參數時要非常非常慎重!如果你的工作區有未提交的修改,git reset --hard永久丟棄這些修改,你將找不回來!請務必確認你不再需要這些改動,或者已經備份。

演示版本回退:從 version3 回到 version2

為了方便演示回退功能,我們先按照提供的例子,在 ReadMe 文件中添加內容并連續提交三個版本:

# 假設這是你的 gitcode 倉庫
zz@139-159-150-152:~/gitcode$ pwd
/home/zz/gitcode# 第一個版本內容并提交
zz@139-159-150-152:~/gitcode$ cat ReadMe
hello bit
hello git
hello world
hello version1
zz@139-159-150-152:~/gitcode$ git add ReadMe
zz@139-159-150-152:~/gitcode$ git commit -m"add version1"
[master cff9d1e] add version11 file changed, 1 insertion(+)# 第二個版本內容并提交
zz@139-159-150-152:~/gitcode$ cat ReadMe
hello bit
hello git
hello world
hello version1
hello version2
zz@139-159-150-152:~/gitcode$ git add ReadMe
zz@139-159-150-152:~/gitcode$ git commit -m"add version2"
[master 14c12c3] add version2 # 注意這里的 commit id 是 14c12c3...1 file changed, 1 insertion(+)# 第三個版本內容并提交 (當前最新版本)
zz@139-159-150-152:~/gitcode$ cat ReadMe
hello bit
hello git
hello world
hello version1
hello version2
hello version3
zz@139-159-150-152:~/gitcode$ git add ReadMe
zz@139-159-150-152:~/gitcode$ git commit -m"add version3"
[master d95c13f] add version3 # 注意這里的 commit id 是 d95c13f...1 file changed, 1 insertion(+)# 查看一下提交歷史,確認有這三個版本
zz@139-159-150-152:~/gitcode$ git log --pretty=oneline
d95c13ffc878a55a25a3d04e22abfc7d2e3e1383 (HEAD -> master) add version3 # 最新,HEAD 和 master 指向它
14c12c32464d6ead7159f5c24e786ce450c899dd add version2 # 上一個版本
cff9d1e019333318156f8c7d356a78c9e49a6e7b add version1 # 再上一個版本
... # 可能還有之前的其他提交

現在我們的倉庫歷史是:初始提交 -> version1 -> version2 -> version3 (當前)。HEAD 指針和 master 分支都指向 d95c13ffc878a55a25a3d04e22abfc7d2e3e1383 這個 Commit ID。

假設我們發現 version3 的內容有問題,想完全回到 version2 的狀態,并且工作區的文件內容也要變回 version2 時期。這時就需要使用 --hard 參數。

version2 是當前版本 (HEAD) 的上一個版本,所以我們可以用 HEAD^ 來指代 version2 這個版本。

# 我們想回退到 HEAD 的上一個版本 (version2),并且徹底重置工作區和暫存區
zz@139-159-150-152:~/gitcode$ git reset --hard HEAD^
HEAD is now at 14c12c3 add version2 # Git 告訴你 HEAD (和 master) 現在指向了這個 commit

或者,你也可以直接使用 version2 的 Commit ID 來指定目標版本(從 git log 輸出中找到 add version2 那一行的 ID):

# 回退到指定的 version2 的 commit id
# 替換成你自己的 version2 的 commit id
zz@139-159-150-152:~/gitcode$ git reset --hard 14c12c32464d6ead7159f5c24e786ce450c899dd
HEAD is now at 14c12c3 add version2

執行 git reset --hard 后,Git 會將當前分支指針和 HEAD 都移到目標版本 (version2),同時強行把暫存區和工作區的內容都替換成目標版本時的文件內容。

我們查看一下 ReadMe 文件的內容:

zz@139-159-150-152:~/gitcode$ cat ReadMe
hello bit
hello git
hello world
hello version1
hello version2

驚奇地發現,ReadMe 文件的內容已經回退到 version2 時刻的狀態了!version3 中添加的 hello version3 這一行已經不見了。

再用 git log 查看提交歷史:

zz@139-159-150-152:~/gitcode$ git log --pretty=oneline
14c12c32464d6ead7159f5c24e786ce450c899dd (HEAD -> master) add version2 # 最新,HEAD 和 master 指向它
cff9d1e019333318156f8c7d356a78c9e49a6e7b add version1 # 上一個版本
... # 可能還有之前的其他提交

注意看,git log 顯示的最新提交已經是 version2 了,那個 add version3 的提交仿佛從歷史中“消失”了!這是因為當前分支 (master) 的指針已經移回到了 version2 對應的 Commit,從這個分支看過去,version3 不再是它的歷史一部分。

這就是版本回退!通過移動分支指針,讓你的項目回到了之前的某個狀態。

哎呀,回退錯了怎么辦?找回“消失”的提交!

執行了 git reset --hard 回退版本后,你可能會遇到一個問題:如果我回退到 version2 后,又后悔了,想再回到 version3 怎么辦?

當你使用 git log 查看時,version3 的那個提交 ID ( d95c13f...) 似乎不見了,因為當前分支不指向它了。運氣好的話你能在終端的滾動記錄里找到它,運氣不好,你就可能覺得那個版本永遠丟失了。

別怕!Git 是一個強大的工具,它不會輕易丟掉你的提交。雖然 git log 顯示的是當前分支的歷史,但 Git 在本地還悄悄地記錄著你的每一次操作歷史,包括 HEAD 指針曾經指向的位置變化。這個歷史記錄可以通過 git reflog 命令查看。

git reflog:你的操作“流水賬”

git reflog 命令記錄了你的倉庫中 HEAD 的每一次移動,幾乎所有的 Git 操作(如 commit, reset, merge, rebase 等)都會在這里留下記錄。

zz@139-159-150-152:~/gitcode$ git reflog
14c12c3 (HEAD -> master) HEAD@{0}: reset: moving to 14c12c32464d6ead7159f5c24e786ce450c899dd # 最近一次操作:reset,移動到 version2
d95c13f HEAD@{1}: commit: add version3 # 上上次操作:commit version3
14c12c3 (HEAD -> master) HEAD@{2}: commit: add version2 # 再之前的操作:commit version2
cff9d1e HEAD@{3}: commit: add version1
94da695 HEAD@{4}: commit: add modify ReadMe file
23807c5 HEAD@{5}: commit: add 3 files
c614289 HEAD@{6}: commit (initial): commit my first file

看到了嗎?git reflog 清晰地列出了我執行過的操作,以及每次操作后 HEAD 指向的 Commit ID。即使 git log 看不到了,在這里我仍然能找到 add version3 那個提交的 ID (d95c13f)!

使用 git reflog 找回版本

既然在 git reflog 里找到了 version3 的 Commit ID,我們就可以再次使用 git reset --hard 命令,指定這個 ID,跳回到 version3 了!

# 使用 git reflog 里找到的 version3 的 commit id 來回退
# 這里使用了部分 commit id (d95c13f),通常只要部分 id 足夠唯一即可
zz@139-159-150-152:~/gitcode$ git reset --hard d95c13f
HEAD is now at d95c13f add version3 # Git 告訴你 HEAD (和 master) 又回到了 version3# 檢查工作區,內容回到了 version3
zz@139-159-150-152:~/gitcode$ cat ReadMe
hello bit
hello git
hello world
hello version1
hello version2
hello version3# 檢查 git log,分支指針也回到了 version3
zz@139-159-150-152:~/gitcode$ git log --pretty=oneline
d95c13ffc878a55a25a3d04e22abfc7d2e3e1383 (HEAD -> master) add version3
14c12c32464d6ead7159f5c24e786ce450c899dd add version2
cff9d1e019333318156f8c7d356a78c9e49a6e7b add version1
94da6950d27e623c0368b22f1ffc4bff761b5b00 add modify ReadMe file
23807c536969cd886c4fb624b997ca575756eed6 add 3 files
c61428926f3853d3229e278113095f115c302405 commit my first file # 注意這里的初始提交 ID 和前面 log 可能有差異,以你的實際輸出為準

成功了!我們又從 version2 跳回到了 version3

這個例子說明:版本回退(reset)本質上是移動 **HEAD** 指針和分支指針。Git 的所有歷史版本(Commit 對象)都還在對象庫里。**git log**** 查看的是當前分支能追溯到的歷史,而 **git reflog** 記錄的是你本地倉庫 **HEAD** 指針移動過的所有位置**。只要你想回到的那個版本的 Commit ID 還在 git reflog 里,你就可以回得去。

為什么 Git 回退這么快?

Git 版本回退速度非常快,特別是與一些中心化的版本控制系統不同。這是因為 Git 回退時,通常只是簡單地修改指針的指向(比如 refs/heads/master 文件里存儲的 Commit ID),而不是去刪除對象庫里已有的 Commit 對象或文件內容對象。

想象一下版本歷史是一條鏈子上的珠子,每個珠子是一個 Commit。分支指針(比如 master)和 HEAD 指針就像兩個環,套在其中一個珠子上,表示“我現在在這里”。

當你執行 git reset 回退時,比如從 version3 回到 version2,Git 只是把那個環從 version3 那個珠子上取下來,套到 version2 的珠子上。version3 那個珠子還在鏈子上,只是暫時沒有分支指針指向它了。

版本1 --- 版本2 --- 版本3 (HEAD, master)  <- reset --hard HEAD^版本1 --- 版本2 (HEAD, master)   版本3

(這與你提供的第二個圖片概念一致,HEAD和master指針從version3移到了version2)

只有在 Git 執行垃圾回收時,那些沒有任何指針(包括分支、標簽、或其他引用,以及 reflog 的過期記錄)指向的 Commit 對象和相關聯的對象,才可能被清理掉。所以,即使你 reset --hard 了,在一段時間內,那個被“回退掉”的版本數據仍然存在于你的 .git/objects 目錄中,git reflog 就是找到它們的救命稻草。

總結:謹慎使用 reset --hard

通過這部分的學習,我們掌握了 Git 版本回退的核心命令 git reset

  • git reset 主要通過移動分支指針和可選地修改暫存區工作區來回退版本。
  • --soft 只移動指針,保留暫存區和工作區。
  • --mixed (默認) 移動指針并重置暫存區,保留工作區。
  • --hard 移動指針,并徹底重置暫存區和工作區可能導致未提交的修改永久丟失,請務必謹慎使用!
  • 你可以使用 Commit ID、HEAD^HEAD~數字 等方式指定回退目標。
  • git log 查看的是當前分支的歷史,而 git reflog 查看的是本地倉庫 HEAD移動歷史,它是回退后找回丟失提交的“后悔藥”。

版本回退是一個強大的工具,可以幫助你修正錯誤的歷史。熟練掌握 git reset 的不同模式及其對工作區、暫存區和版本庫的影響,以及學會使用 git reflog 來找回提交,是安全使用 Git 的重要一環。

下一篇,我們將學習如何撤銷工作區和暫存區的修改。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/82085.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82085.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82085.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

易貝平臺關鍵字搜索技術深度解析

一、核心搜索機制 關鍵詞匹配原理 采用TF-IDF算法計算關鍵詞權重 支持同義詞擴展&#xff08;如"phone"匹配"cellphone"&#xff09; 標題權重 > 副標題 > 商品描述 搜索排序因素 # 搜索權重模擬計算 def calculate_rank(keyword, item): title…

深度剖析 MCP SDK 最新版:Streamable HTTP 模式

好記憶不如爛筆頭&#xff0c;能記下點東西&#xff0c;就記下點&#xff0c;有時間拿出來看看&#xff0c;也會發覺不一樣的感受. 目錄 一、概述 二、快速上手&#xff1a;開啟 Streamable HTTP 服務端開啟 客戶端連接 三、深入兩個核心參數 stateless_http json_resp…

樹莓派開箱上手教程(無需顯示器版)

樹莓派開箱上手教程&#xff08;無需顯示器版&#xff09; 硬件準備 名稱參數電源適配器5V電源適配器&#xff0c;至少需要3A的額定電流&#xff0c;配備USB Type-C輸出接頭microSD卡用來將樹莓派的操作系統安裝到上邊&#xff0c;至少需要8GB容量&#xff0c;一般建議16GB及以…

MySQL強化關鍵_015_存儲過程

目 錄 一、概述 1.說明 2.優點 3.缺點 二、存儲過程的操作 1.創建 2.調用 3.查看 4.刪除 三、變量 1.系統變量 &#xff08;1&#xff09;說明 &#xff08;2&#xff09;查看系統變量 &#xff08;3&#xff09;設置系統變量 2.用戶變量 &#xff08;1&…

動態規劃dp

這里寫目錄標題 動態規劃01背包完全背包多重背包混合背包二維費用的背包分組背包有依賴的背包背包問題求方案數背包問題求具體方案數位 DP狀壓 DP常用例題 動態規劃 01背包 有 n n n 件物品和一個容量為 W W W 的背包&#xff0c;第 i i i 件物品的體積為 w [ i ] w[i] w…

arcgis js統計FeatureLayer的橢球面積、平面面積

1、導入依賴 import FeatureLayer from arcgis/core/layers/FeatureLayer import { geodesicArea, planarArea, simplify } from arcgis/core/geometry/geometryEngine; import { project, load as projectionLoad } from arcgis/core/geometry/projection2、初始化project o…

2.2.1 05年T2

引言 本文將從一預習、二自習、三學習、四復習等四個階段來分析2005年考研英語閱讀第二篇文章。為了便于后續閱讀&#xff0c;我將第四部分復習放在了首位。 四、復習 方法&#xff1a;錯誤思路分析總結考點文章梳理 4.1 錯題分析 題目&#xff1a;26&#xff08;細節題&…

Java 連接并操作 Redis 萬字詳解:從 Jedis 直連到 RedisTemplate 封裝,5 種方式全解析

引言 在分布式系統和高并發場景中&#xff0c;Redis 作為高性能內存數據庫的地位舉足輕重。對于 Java 開發者而言&#xff0c;掌握 Redis 的連接與操作是進階必備技能。然而&#xff0c;從基礎的 Jedis 原生客戶端到 Spring 封裝的 RedisTemplate&#xff0c;不同連接方式的原…

談談對《加密算法》的理解

文章目錄 一、什么是加密算法&#xff1f;二、常見的加密算法有哪些&#xff1f;2.1 對稱加密2.2 非對稱加密2.3 哈希算法 三、加密算法代碼展示3.1 MD5加密3.2 秘鑰加密3.3 AES加密解密 四、加密算法的使用場景 一、什么是加密算法&#xff1f; 加密算法是一種通過數學方法將…

Fuzz 模糊測試篇JS 算法口令隱藏參數盲 Payload未知文件目錄

1 、 Fuzz 是一種基于黑盒的自動化軟件模糊測試技術 , 簡單的說一種懶惰且暴力的技術融合了常見 的以及精心構建的數據文本進行網站、軟件安全性測試。 2 、 Fuzz 的核心思想 : 口令 Fuzz( 弱口令 ) 目錄 Fuzz( 漏洞點 ) 參數 Fuzz( 利用參數 ) PayloadFuzz(Bypass)…

哈希表的實現(下)

目錄 前言 開散列概念 開散列實現 Insert 優化 Find Erase 前言 上一章節我們用閉散列實現了一下哈希表&#xff0c;但存在一些問題&#xff0c;比如空間浪費比較嚴重&#xff0c;如果連續一段空間都已經存放值&#xff0c;那么在此位置插入新值的時候就會一直挪動&…

再談Linux 進程:進程等待、進程替換與環境變量

目錄 1.進程等待 為什么需要進程等待&#xff1f; 相關系統調用&#xff1a;wait()和waitpid() wait(): waitpid(): 解析子進程狀態&#xff08;status&#xff09; 2.進程替換 為什么需要進程替換&#xff1f; 相關系統調用&#xff1a;exec函數家族 3.環境變量 ?…

基于深度學習的無線電調制識別系統

基于深度學習的無線電調制識別系統 本項目實現了一個基于深度學習的無線電調制識別系統&#xff0c;使用LSTM&#xff08;長短期記憶網絡&#xff09;模型對不同類型的 無線電信號進行自動分類識別。該系統能夠在不同信噪比(SNR)條件下&#xff0c;準確識別多種調制類型&#…

Python 爬蟲之requests 模塊的應用

requests 是用 python 語言編寫的一個開源的HTTP庫&#xff0c;可以通過 requests 庫編寫 python 代碼發送網絡請求&#xff0c;其簡單易用&#xff0c;是編寫爬蟲程序時必知必會的一個模塊。 requests 模塊的作用 發送網絡請求&#xff0c;獲取響應數據。 中文文檔&#xf…

隨機森林(Random Forest)學習

隨機森林是一種基于集成學習的機器學習算法&#xff0c;屬于Bagging&#xff08;Bootstrap Aggregating&#xff09;方法的一種擴展。它通過組合多個決策樹來提升模型的泛化能力和魯棒性&#xff0c;廣泛用于分類、回歸和特征選擇任務。 1.隨機森林核心思想 1.1少數服從多數 在…

從 0 到 1!Java 并發編程基礎全解析,零基礎入門必看!

寫在前面 博主在之前寫了很多關于并發編程深入理解的系列文章&#xff0c;有博友反饋說對博主的文章表示非常有收獲但是對作者文章的某些基礎描述有些模糊&#xff0c;所以博主再根據最能接觸到的基礎&#xff0c;為這類博友進行掃盲&#xff01;當然&#xff0c;后續仍然會接…

el-input寬度自適應方法總結

使用 style 或 class 直接設置寬度 可以通過內聯樣式或 CSS 類來直接設置 el-input 的寬度為 100%&#xff0c;使其自適應父容器的寬度 <template><div style"width: 100%;"><el-input style"width: 100%;" v-model"input">…

解決 Supabase “permission denied for table XXX“ 錯誤

解決 Supabase “permission denied for table” 錯誤 問題描述 在使用 Supabase 開發應用時&#xff0c;你可能會遇到以下錯誤&#xff1a; [Nest] ERROR [ExceptionsHandler] Object(4) {code: 42501,details: null,hint: null,message: permission denied for table user…

java每日精進 5.20【MyBatis 聯表分頁查詢】

1. MyBatis XML 實現分頁查詢 1.1 實現方式 MyBatis XML 是一種傳統的 MyBatis 使用方式&#xff0c;通過在 XML 文件中編寫 SQL 語句&#xff0c;并結合 Mapper 接口和 Service 層實現分頁查詢。分頁需要手動編寫兩條 SQL 語句&#xff1a;一條查詢分頁數據列表&#xff0c;…

linux taskset 查詢或設置進程綁定CPU

1、安裝 taskset larkubuntu&#xff1a;~$ sudo apt-get install util-linux larkubuntu&#xff1a;~$ taskset --help 用法&#xff1a; taskset [選項] [mask | cpu-list] [pid|cmd [args...]] 顯示或更改進程的 CPU 關聯性。 選項&#xff1a; -a&#xff0c; --all-tasks…