[英]Revert to a specific commit on Gitlab
我對git
很陌生。
在Gitlab
上進行項目時,我在development
分支(綠色分支)中進行了一些更改,而不是從development
中簽出新分支,在新分支中進行更改,然后將其合並回development
。
同時,其他開發人員之一從開發分支中提取並開始工作( ARE-1195
)。 現在,我知道當開發人員嘗試將他的分支合並到development
分支時,這會產生很多合並沖突。 我怎樣才能避免這種情況? 我嘗試尋找可能的解決方案並遇到了兩個術語, revert
和reset
,但我對它們感到困惑。
我想將我的存儲庫恢復到其他開發人員創建ARE-1195
的提交,但仍然保留我之后所做的更改。
發布最新的 git graph 片段以供參考。
我嘗試尋找可能的解決方案並遇到了兩個術語,
revert
和reset
,但我對它們感到困惑。
你的困惑是恰當的。 不幸的是,Git 的作者 (Linus Torvalds) 為這兩個動作中的至少一個選擇了錯誤的動詞:調用revert的動作可能應該被稱為backout (就像在 Mercurial 中一樣)。
但是,為了理解這兩者,我們應該從提交是什么以及為您做什么開始。 您包含的圖像(我將在此處將其轉換為新的不同圖像)顯示了其中的一些:
B--C--D--E <-- ARE-1195
/
...--A-----F----G <-- development-ui-...
這里的每個大寫字母A
到G
都代表一個提交,就像原始圖像中的每個彩色點都代表一個提交一樣。 在我的繪圖中,較新的提交朝向右側,而在原始圖像中,較新的提交朝向頂部。 所以圖紙是不同的,但它們顯示的是相同的東西:
ARE-1195
提交是提交E
。development-ui
提交是 commit G
。 在 Git 中,每個提交都有一個唯一的數字,但這個數字很大——現在介於 1 和 1461501637330902918203684832716283019655932542975 之間,未來版本的 Git 會更高——而且看起來完全隨機(盡管不是)。 它通常以十六進制表示並且通常縮寫為例如a123456
。
這個數字——這個特定提交的唯一編號——在一個重要的意義上就是提交:在宇宙中任何地方的任何 Git 存儲庫中,沒有其他提交可以使用相同的數字,除非它實際上是相同的提交。 因此,兩個 Git 存儲庫可以隨時會面並比較它們的數量(盡管數量很大,但比提交本身要小得多)。 如果存儲庫R具有存儲庫S缺少的某個編號,則R可以將提交轉移到S ,現在它們都擁有它。
每個提交中的內容是:
每個文件的完整快照。 這些文件被壓縮,重要的是,去重,因此,如果您進行新的提交,主要重用舊提交中的大部分文件,新副本實際上不會占用空間。 但它們在邏輯上仍然是文件的副本,因此每次提交都有每個文件的完整副本。
一些元數據,或有關此特定提交的信息。 例如,元數據包括提交人的姓名,以及提交的日期和時間。 Git將提交哈希 ID 列表填充到此元數據中,該列表通常恰好是一個條目長。 這個單項列表給出了這個特定提交的父提交的提交號。
因此,在上圖中,提交G
在其元數據中列出了提交F
的數量。 這意味着只要 Git 能找到G
,它就能找到F
。 提交F
,作為一個提交,有快照和元數據,它的元數據列出了提交A
的哈希 ID 作為它的父級,所以 Git 可以從F
向后移動到A
。 A
也有元數據,其中列出了一些以前的提交(此處未顯示); 這不斷重復,當我們向后遍歷這個列表時,我們發現的提交,一次一個提交,是存儲庫中的歷史。 最終我們到達了第一個提交,它無法列出任何先前提交的哈希 ID,所以它的列表是空的,這就是我們停止向后退的地方。
同時,只要 Git 可以找到提交E
,它就可以使用它找到D
,找到C
,找到B
,找到A
,找到 A 之前A
任何內容,依此類推。 這就是從ARE-1195
看到的歷史。
請注意,沒有辦法前進。 如果我們在提交A
,我們可以向后工作到歷史的開始,但我們不能前進到B
或F
,因為提交A
不知道他們的提交哈希 ID。 為了讓神奇的編號系統正常工作,Git絕不能更改任何提交的任何部分,因此不可能向未來的提交添加前向鏈接——任何未來提交的哈希 ID 完全取決於進入該提交的每一位數據未來提交,包括某人提交的確切時間,所以我們不知道未來提交的哈希 ID 是什么。
這就是為什么歷史總是倒退的原因,也是git reset
的關鍵。
git reset
丟棄提交 git reset
命令又大又復雜。 它可以做許多不同的工作。 我們將忽略它們中的大多數,只專注於此時此地您感興趣的一項工作。
我們在上面提到,Git需要找到提交G
才能找到提交F
。 Git需要找到提交E
才能找到提交D
,它需要它才能找到C
,依此類推。 (不過,無論是F
還是B
都足以找到A
。)那么 Git 如何找到指定最近ARE-1195
提交的看似隨機的數字呢?
答案是名稱ARE-1195
本身包含提交哈希 ID 。 這就是分支名稱對你和 Git 的作用:它擁有一個提交哈希 ID。 (事實上,所有 Git 的引用都是這樣工作的。引用或引用包括分支名稱、標簽名稱、遠程跟蹤名稱和許多其他名稱。分支名稱有點特殊,因為.git/config
通常包含更多關於它們的信息,但我們會忽略這個答案。)
git reset
命令可以移動一個分支名稱。 從技術上講,它將移動當前分支名稱; 如果我們想移動一些不是當前的分支名稱,我們必須使用git branch -f
,或者切換到該分支,使其成為當前分支。 它還不僅僅是移動分支名稱,但我們現在再次忽略它所做的所有額外內容。
假設我們要移動名稱ARE-1195
以便它找到提交C
而不是提交E
。 也就是說,如果我們讓圖表看起來像這樣:
D--E ???
/
B--C <-- ARE-1195
/
...--A-----F----G <-- development-ui
Git 現在會先找到提交C
,然后返回到B
,然后到A
。 就好像提交D
和E
剛剛停止存在。 他們沒有——如果你記住了他們的原始哈希 ID,或者把它們寫在紙上,或者其他什么東西,你仍然可以找到這兩個提交(實際上你只需要保存E
的哈希 ID,因為E
讓你找到了D
)——但它們似乎已經消失了。
現在, git reset
,當在它移動分支名稱的模式下使用時,還會做兩件事——或者更確切地說,可選地做兩件事:
git reset
移動分支名稱。--soft
使其停止,否則git reset
會重置 Git 的index 。--hard
——但如果你使用--soft
或--mixed
,它們分別在步驟 1 或 2 處停止——它會重置你的工作樹。 我們沒有在這里解釋 Git 的索引和你的工作樹(並且不會因為空間原因)但是當你刪除這樣的提交時,你通常希望git reset --hard
:你希望提交D
和E
完全消失永遠,或者至少看起來是這樣,所以你想擺脫它們對 Git 的索引和你的工作樹的影響,這意味着使用git reset --hard
。
這就是git reset --hard
將為您做的:讓那些提交看起來像消失了。 不過這里有一個很大的缺陷。 如果您之前讓您的 Git 連接到其他 Git 軟件,並且讓您的 Git將提交D
和E
發送到其他 Git,那么其他 Git 存儲庫現在擁有這兩個提交。 Git 是為添加提交而不是刪除提交而構建的:這個git reset --hard
技巧需要大量的手工工作,但是 Git 的日常使用會自動添加新的提交,而無需大量的手工工作。 因此,如果您使用git push
將您的新提交發送給其他人,他們可以輕松地返回給您,就好像其他人已經做出了它們一樣。 1
如果您從未在其他任何地方發送過這些提交,使用git reset
可以使它們似乎從您的存儲庫中消失。 沒有其他人擁有它們,您的 Git 不會將它們交給其他 Git 存儲庫,因為您的 Git 不再看到它們。 因此,如果您還沒有使用git push
將它們發送出去,這是一種擺脫提交的安全方法。
1盡管 Git 在提交元數據中記錄了作者和提交者的姓名,但 Git 在貪婪地將新提交添加到您的存儲庫時不會查看這些名稱。 它只是說oooh 閃亮的新提交,必須添加它並添加它。
git revert
退出一個提交讓我們再次回到這張圖:
B--C--D--E <-- ARE-1195
/
...--A-----F----G <-- development-ui
讓我們進一步假設提交D
中的某些內容是壞的,但是提交D
和E
已經逃脫並且現在不在其他 Git 存儲庫中。 您可以使用git reset
完全丟棄D
和E
,但它們會回來並重新感染您的存儲庫,就像某種討厭的病毒一樣。 此外,您喜歡提交E
:一切都很好! 您想要做的是退出提交D
的效果。
首先運行git switch ARE-1195
(如果需要),然后:
git revert d789012
(如果那是D
的哈希 ID)
或者:
git revert HEAD~1
( ~1
因為我們希望 Git 倒數1
第一個也是唯一的父鏈接)或類似內容告訴 Git:找出D
中發生了什么變化,然后撤消該更改。 進行一個新的提交,其消息是revert <hash-of-D>
。 最終結果是:
B--C--D--E--∇ <-- ARE-1195
/
...--A-----F----G <-- development-ui
其中 new commit ∇
取消了在D
中所做的更改。
因為各地的 Git 存儲庫都渴望新的提交,所以他們會很樂意添加這個,從而撤銷一個提交的效果。 這不會撤消提交E
的效果; 如果你願意,你必須恢復兩個提交:
B--C--D--E--∃--∇ <-- ARE-1195
/
...--A-----F----G <-- development-ui
以相反的順序恢復提交通常是明智的:使用∃
撤消E
會使您的文件看起來與D
發生后的文件完全相同,因此使用∇
撤消D
不會發生合並沖突,並使您的文件看起來與C
發生后的文件完全相同.
還原系列中的所有提交會取消該系列中的所有更改。 git revert
命令足夠聰明,可以自己以相反的順序執行此操作,因此您只需列出所有要還原的提交。 例如,要同時恢復D
和E
,您可以運行:
git revert c00795f..HEAD # if that's the hash ID of commit `C`
或者:
git revert HEAD~2..HEAD
將退出E
和D
。 請注意,當我們像這樣使用雙點<hash1>..<hash2>
形式時,我們在我們關心的第一次提交之前將提交哈希 ID 作為hash
提供。 這與我們可以使用哈希 ID或HEAD
或ARE-1195
之類的名稱,甚至是HEAD~2
或ARE-1195~2
之類的后綴技巧(在 Git 需要哈希 ID 的地方)的想法一起是基本的-to-work-with-Git 知識,你應該隨身攜帶。 但是,如果您需要復習它,請參閱gitrevisions 文檔,該文檔值得多次閱讀,因為它包含了很多好東西。
OP:我嘗試尋找可能的解決方案,遇到了兩個術語, revert和reset ,但我對它們感到困惑。
下面是reset和revert之間區別的一般案例說明,以及何時使用什么。
簡而言之, reset用於將分支的尖端移動到預先退出的提交(通常回到時間,但並非總是如此)。 另一方面, revert創建了一個新的提交,該提交恢復了另一個提交所做的更改(本質上它是一個反向的cherry-pick )。
考慮上面的插圖,左側是一個常見的起始案例,右側是兩個選項並排。 在這種情況下,請注意master分支,因為它是使用兩個命令修改的內容。
資料來源:以上摘錄摘自這篇關於該主題的完整帖子: 如何撤消 Git 中的更改(重置 vs 恢復 vs 恢復)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.