簡體   English   中英

恢復到 Gitlab 上的特定提交

[英]Revert to a specific commit on Gitlab

我對git很陌生。
Gitlab上進行項目時,我在development分支(綠色分支)中進行了一些更改,而不是從development中簽出新分支,在新分支中進行更改,然后將其合並回development
同時,其他開發人員之一從開發分支中提取並開始工作( ARE-1195 )。 現在,我知道當開發人員嘗試將他的分支合並到development分支時,這會產生很多合並沖突。 我怎樣才能避免這種情況? 我嘗試尋找可能的解決方案並遇到了兩個術語, revertreset ,但我對它們感到困惑。
我想將我的存儲庫恢復到其他開發人員創建ARE-1195的提交,但仍然保留我之后所做的更改。
發布最新的 git graph 片段以供參考。

在此處輸入圖像描述

我嘗試尋找可能的解決方案並遇到了兩個術語, revertreset ,但我對它們感到困惑。

你的困惑是恰當的。 不幸的是,Git 的作者 (Linus Torvalds) 為這兩個動作中的至少一個選擇了錯誤的動詞:調用revert的動作可能應該被稱為backout (就像在 Mercurial 中一樣)。

但是,為了理解這兩者,我們應該從提交是什么以及為您做什么開始。 您包含的圖像(我將在此處將其轉換為新的不同圖像)顯示了其中的一些:

       B--C--D--E   <-- ARE-1195
      /
...--A-----F----G   <-- development-ui-...

這里的每個大寫字母AG都代表一個提交,就像原始圖像中的每個彩色點都代表一個提交一樣。 在我的繪圖中,較新的提交朝向右側,而在原始圖像中,較新的提交朝向頂部 所以圖紙是不同的,但它們顯示的是相同的東西:

  • 最近最新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 ,我們可以向后工作到歷史的開始,但我們不能前進BF ,因為提交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 就好像提交DE剛剛停止存在。 他們沒有——如果你記住了他們的原始哈希 ID,或者把它們寫在紙上,或者其他什么東西,你仍然可以找到這兩個提交(實際上你只需要保存E的哈希 ID,因為E讓你找到了D )——但它們似乎已經消失了。

現在, git reset ,當在它移動分支名稱的模式下使用時,還會做兩件事——或者更確切地說,可選地做兩件事:

  1. 首先, git reset移動分支名稱。
  2. 然后,除非您在第 1 步之后使用--soft使其停止,否則git reset會重置 Git 的index
  3. 然后,如果你使用了--hard ——但如果你使用--soft--mixed ,它們分別在步驟 1 或 2 處停止——它會重置你的工作樹

我們沒有在這里解釋 Git 的索引和你的工作樹(並且不會因為空間原因)但是當你刪除這樣的提交時,你通常希望git reset --hard :你希望提交DE完全消失永遠,或者至少看起來是這樣,所以你想擺脫它們對 Git 的索引和你的工作樹的影響,這意味着使用git reset --hard

這就是git reset --hard將為您做的:讓那些提交看起來像消失了 不過這里有一個很大的缺陷。 如果您之前讓您的 Git 連接到其他 Git 軟件,並且讓您的 Git提交DE發送到其他 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的某些內容是壞的,但是提交DE已經逃脫並且現在不在其他 Git 存儲庫中。 您可以使用git reset完全丟棄DE ,但它們會回來並重新感染您的存儲庫,就像某種討厭的病毒一樣。 此外,您喜歡提交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命令足夠聰明,可以自己以相反的順序執行此操作,因此您只需列出所有要還原的提交。 例如,要同時恢復DE ,您可以運行:

git revert c00795f..HEAD    # if that's the hash ID of commit `C`

或者:

git revert HEAD~2..HEAD

將退出ED 請注意,當我們像這樣使用雙點<hash1>..<hash2>形式時,我們在我們關心的第一次提交之前將提交哈希 ID 作為hash提供。 這與我們可以使用哈希 IDHEADARE-1195之類的名稱,甚至是HEAD~2ARE-1195~2之類的后綴技巧(在 Git 需要哈希 ID 的地方)的想法一起是基本的-to-work-with-Git 知識,你應該隨身攜帶。 但是,如果您需要復習它,請參閱gitrevisions 文檔,該文檔值得多次閱讀,因為它包含了很多好東西。

OP:我嘗試尋找可能的解決方案,遇到了兩個術語, revertreset ,但我對它們感到困惑。

重置還原的區別

下面是resetrevert之間區別的一般案例說明,以及何時使用什么。

簡而言之, reset用於將分支的尖端移動到預先退出的提交(通常回到時間,但並非總是如此)。 另一方面, revert創建了一個新的提交,該提交恢復了另一個提交所做的更改(本質上它是一個反向的cherry-pick )。

重置和還原的區別

考慮上面的插圖,左側是一個常見的起始案例,右側是兩個選項並排。 在這種情況下,請注意master分支,因為它是使用兩個命令修改的內容。

資料來源:以上摘錄摘自這篇關於該主題的完整帖子: 如何撤消 Git 中的更改(重置 vs 恢復 vs 恢復)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM