![](/img/trans.png)
[英]Push a branch of a git repo to a new remote (github), hiding its history
[英]Git: Fix repo with a branch and its remote on different branches
我不知道這是怎么發生的,但是我有一個處於以下狀態的 git repo:
A--B--C--D master
\
E--F origin/master
提交E
和F
在這一點上已經過時了; 他們對B
所做的更改沒有相關性,如有必要,可以將其刪除。
我只保留了origin
遠程作為我的代碼進行備份,而不是別人已經克隆它,這樣我就可以使用任何蠻力我所需要的。
清除此問題的最干凈方法是什么?
我只保留遠程源作為我代碼的備份,沒有其他人克隆它,所以我可以使用我需要的任何蠻力。
在這種情況下, git push --force origin master
就足夠了。 非常確定您對存儲庫中的origin/master
問題,並且在(單獨的)Git 存儲庫中的master
超過origin
,不再能夠找到提交F
。
(您自己的存儲庫會在一段時間內記住E
和F
提交,在reflog 名稱origin/master@{ number }
,因此只要您保持自己的存儲庫完整無缺,如果結果證明沒有,您仍然可以取回它們畢竟已經過時了。然而,在某些時候它們真的會被刪除。服務器可能根本沒有 reflogs,它的提交E
和F
副本可能會相對較快地消失——在幾秒鍾、幾小時或幾天內,例如。)
每個 Git 存儲庫都有自己的分支名稱,這些名稱對該特定 Git 存儲庫來說是半私有的。 在您調用origin
的機器上有一個存儲庫,它有一個分支名稱master
。
您的遠程跟蹤名稱,1如origin/master
,是什么記住一些其他Git有它的分支名稱的Git的方法。 在這種情況下,您的origin/master
是您的 Git 對origin
的master
的記憶。
當你讓你的 Git 通過git fetch
或git push
調用他們的 Git 時,兩個 Git 有一個啟動對話。 fetch 和 push 略有不同,但您可以使用git ls-remote
觀察git fetch
本身將觀察到什么。 只需運行git ls-remote origin
(或保留origin
作為默認值通常是origin
反正):您的 Git 將溢出它列出的另一個Git 的分支、標簽和其他名稱。 這是git fetch
的第一部分:您的 Git 打電話給他們的 Git 並獲取此列表。
當您使用git fetch
,您的 Git 使用此列表向他們的 Git 詢問他們擁有的、您沒有的、您的 Git 希望擁有的任何提交和其他內部 Git 對象。 例如,如果你還沒有提交F
,而你的origin/master
記住了提交B
或E
,並且他們已經提交F
作為他們的master
,你的 Git 會說:我想要提交F
,他們將其添加到要發送的提交列表中。 然后他們會提供提交E
一個 Git必須提供每個提交的父母——你的 Git 要么說不,我已經有了,要么請(在這種情況下,他們會提供B
, E
的父母,等等在)。
然后,他們的 fetch 過程將獲得這些提交所需的任何內容捆綁到您的 Git 中——這是您將看到counting objects
和compressing objects
——並將其發送過去。 您的 Git 擴展了這些,以便您擁有一組正確的提交及其數據,然后根據存儲在其master
的哈希 ID 創建或更新您的origin/master
master
:現在您肯定有提交F
,您的 Git 可以創建您的origin/master
指向它。
因此,如果您運行:
git fetch origin
在任何時候——這是命令的“讓我得到一切”形式——你的 Git 將調用他們的 Git 並獲得初步的“一切”列表。 這兩個 Git 會從那里找出您的 Git 需要獲得什么,並將其提供給您的 Git。 然后,您的 Git 將根據所有分支名稱創建或更新所有遠程跟蹤名稱。
當您使用git push
,這與 Git 與fetch
的對立面非常接近——由於歷史錯誤2,這對是push
和fetch
而不是push
和pull
—— 會發生類似的過程,但有兩個關鍵區別:
你的 Git 發送,而不是接收。 (您的 Git 仍然選擇要發送的內容,並且“如果發送提交則必須提供父項”仍然有效,依此類推,但使用git fetch
,他們的 Git 已發送,而您的 Git 已收到。)
最后,不是某些 Git 更新某種“遠程跟蹤名稱”,而是您的 Git 要求(禮貌地)或命令(強制地)他們的 Git 來設置它的一些分支名稱。 3
如果您要求或命令他們設置他們的master
,並且他們服從,您的 Git 現在知道他們的master
設置為您提供的哈希 ID,因此您的 Git 現在更新您的origin/master
遠程跟蹤名稱。
當您使用溫和的、詢問類型的git push origin master
,您的 Git 會發送您擁有但他們沒有的提交——例如C
和D
然后禮貌地詢問他們:請,如果可以,請將您的master
設置為指向提交D
? 他們會說不! 如果我這樣做,我會忘記提交F
! 這個錯誤隨着 Git 行話的拒絕和non-fast-forward再次出現,但這只是意味着他們說不,因為這會丟失提交F
,因此也會丟失E
但這正是您希望他們做的。 所以你只需要給他們一個強有力的命令:設置你的master
! 他們仍然可以拒絕——例如,如果你沒有許可——但是你當然有許可,所以在這種情況下他們會服從,並失去提交F
。
git push
有兩種強制模式: git push --force
是古老的版本, git push --force-with-lease
是更現代的(自 Git 1.9 左右以來)變體。 它們之間的區別在於--force
只是說set ,但--force-with-lease
說:我認為你的master
是 _____。 如果是這樣,請將其設置為 _____ 並讓我知道您這樣做了; 如果沒有,告訴我我錯了,畢竟不要設置你的master
。 第一個空白填充了您的 Git 的origin/master
值:您的 Git 對其 Git 的master
的記憶。 因此,這使您能夠確保您讓他們扔掉的正是您認為他們會扔掉的東西。
如果您是另一個 Git 的唯一用戶,則無需關心--force-with-lease
添加的內容:您認為正確的,根據定義就是正確的。 但是如果您擔心自己可能會健忘(例如,如果您使用兩台不同的筆記本電腦並且不記得哪台是最新的),您可以使用更高級的 check-first-then-force --force-with-lease
。 使用--force-with-lease
永遠不會錯,除了舊的--force
是歷史默認值這一事實之外, --force-with-lease
確實應該是默認值。
1 Git 稱這些遠程跟蹤分支名稱,但幾年前我決定這里的分支這個詞是無用的混亂,會造成一些額外的混亂,所以我現在只稱它們為遠程跟蹤名稱。
2通常,在獲取提交后,您希望集成這些提交。 這就是git pull
所做的:它運行git fetch
來獲取提交,然后運行第二個 Git 命令來集成這些提交。 這在早期似乎是正確的自然粒度,因此git pull
是面向用戶的命令,與git push
相反。
然而,事實證明,將提取操作與集成操作分開通常很重要,例如,在兩者之間插入某種檢查操作,以便選擇正確的集成形式。 Mercurial 做對了,而 Git 做錯了:在 Mercurial 中, hg pull
表示 Git 對git fetch
含義,您必須向hg pull
添加一個單獨的操作或一個額外的標志,以使其也執行集成步驟。
3你可以選擇你要求他們設置的名字,這樣你就可以設置你的 Git 詢問或命令標簽名稱,或者其他一些名稱,但一般來說,這里的通常情況是“分支名稱”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.