[英]Fixing history on GIT repository
我的GIT存儲庫中存在以下情況。 在做出改變之前,有人忘記了拉大師,然后他的當地大師承諾。 在那之后,由於某種原因,他將原點/主人合並到他的本地主人,然后推了它。 結果是原點/主人有點“切換位置”與他當地的主人。 我有意義嗎? 這是一個例子:
在推前
x----x-----x----x----x----x----x-----x----x (MASTER)
推后
---------------------------------------------x---x (MASTER)
| |
x----x-----x----x----x----x----x-----x----x-------
這有點搞亂了存儲庫,因為現在所有的歷史似乎都在分支上。
在那之后,有一些新的提交被推送到新的主人,然后由於一個現在不重要的原因,我們決定我們不想要那些,所以我們設法放棄了我們不想要的提交,並且同時將de MASTER恢復到原來的位置。 像這樣:
之前
---------------------------------------------x---x---x---x---x (MASTER)
| |
x----x-----x----x----x----x----x-----x----x-------
后
(2)
---------------------------------------------x---x---x---x---x--
| | |
x----x-----x----x----x----x----x-----x----x-----x----------------x (MASTER)
(1) (3)
正如你所看到的那樣,現在那個忘記了tu pull的人所提交的提交已被合並到最初的主人那里。 這是通過以下方式實現的:
git checkout <HASH OF COMMIT MARKED AS (1) >
git checkout -b refactor_master
git merge --no-ff <HASH OF COMMIT MARKED AS (2) >
git push origin refactor_master
git merge --strategy=ours mastergit checkout master
git merge refactor_master
git push origin master
這有效地使那些承諾所包含的變化從主人那里消失了,並且使主人變成了過去的樣子。 但是,我現在有一個不應該存在的“分支”。 實際上,標記為(3)的最后一次提交不會進行任何更改。 它只會“切換”主人。 有沒有辦法讓這些提交消失?
這是有道理的:他所做的是違反“主線發展是第一父母”的規則。
請注意,git本身沒有任何內容可以強制執行此規則。 這是不可能的,原因很簡單:誰定義哪條線是“主線”? 這個問題唯一可能的答案是“你”,其中“你”的意思是“運行git來操縱提交圖的人”。 所以這不是一個真正的git規則,它是一個“使用git”規則的人。
每當你運行git merge
(或者在這種情況下“he”運行它),你選擇當前的分支作為開發的主線,以及你正在合並的任何合並的替代線。 因此,如果你這樣做:
$ git checkout master
$ make-some-change; git add ...; git commit -m message
$ git fetch origin # and let's assume this brings in a new commit
$ git merge origin/master
你告訴git將你的主人作為主線,並將上游變化合並為分支線。
請注意,最后兩個命令 - git fetch
后跟git merge
- 是git pull
默認執行的操作。 反過來,這意味着“主線是第一父母”經常被違反,除非你非常嚴格/小心,否則不能依賴它。
有沒有辦法讓那些[merge]提交消失?
是的,但只能寫一個新的提交行(“重寫歷史”)。
讓我把你的最終圖表(不用擔心你是如何到達那里)並對繪圖進行一些小的改動以獲得更緊湊的表示:
------------------------A---M1--B--C--D
/ / \
o--o--o--o--o--o--o--o--o---x-------------M2 <-- master
此時提交B
到D
“在錯誤的行上”,因為合並提交M2
的第一個父項是x
,其第二個父項是D
同時,提交A
是M1
的第一個父級, x
是M1
的第二個父級。
如果你真的非常關心第一父規則,你可以使用提交x
來提交一個新的提交行:
------------------------A---M1--B--C--D
/ / \
o--o--o--o--o--o--o--o--o---x-------------M2 <-- master
\
A'--B'--C'--D' <-- new-master
這里A'
第一個也是唯一一個父是commit x
,當事情第一次“出錯”時,這就是master
的提示。 B'
的第一個也是唯一的父母是A',依此類推。
如果你有這個圖表,你從白板上刪除提交A
到M2
並讓master
點提交D'
,你將擁有:
o--o--o--o--o--o--o--o--o---x
\
A'--B'--C'--D' <-- master
現在你可以“理順”從x
到A'
的鏈接,它看起來像一個很好的線性歷史。
這是一個棘手的部分:這只是你想要的圖形 。 對於圖中的每個提交,git都會保留一個樹 :當你git checkout
提交時,要放在工作目錄中的一組文件。 你想為每個樹提交A'
通過D'
可能不完全一樣的原始樹木A
通過D
。
可以肯定的是, B'
, C'
和D'
所需的樹將分別與B
, C
和D
相同。 但是,您想要用於新提交A'
的樹可能是當前在合並M1
下的樹。 這可能與提交A
下的那個相同,但可能不是。 這實際上取決於A
與M1
A
比較。
在沒有大量手工工作的情況下,有許多相對棘手的方法來構建新提交,但它們很難在文本中描述。 此外,這種“歷史重寫” - 當你強行讓舊的master
標簽指向new-master
的提交D'
時發生的那一部分 - 給你所有的開發人員帶來了痛苦,他們正在做出以M2
作為他們的承諾父提交。 他們必須將這些提交復制到新提交的新D'
作為他們的父母。
這取決於你是否值得,這取決於你和他們。
git分支只是一個指向單個提交的標簽。 提交不知道當前指向哪個分支; 它也不知道以前哪些分支指向它的歷史。 因此,唯一真正重要的事情(並且改變是非常重要的)是提交歷史本身。
清除它的最簡單方法可能如下:找到您認為代表代碼庫的合理狀態的最新提交,並運行以下命令(假設該提交的哈希值為123abc
):
git checkout -B master 123abc
git push -f origin master
這將使master
在本地(在運行這些命令的任何人的機器上)和服務器上指向123abc
。 當其他開發人員運行git fetch
,其origin/master
會移動到123abc
,他們可以檢查出來,並把他們自己的master
與有git checkout -B master origin/master
(我不能完全肯定的語法此但是,命令,我手邊沒有git存儲庫。)
警告:除非你有一個分支指向比123abc
更新的提交,否則這些提交似乎會消失。 如果你想稍后查看它們的內容以便清理它並重新提交它,你應該首先為這些提交創建分支,例如git branch tempbranch 567def
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.