[英]Why can't I push from a shallow clone?
git clone --depth
命令選項說
--depth <depth>
Create a shallow clone with a history truncated to the specified number of revisions.
A shallow repository has a number of limitations
(you cannot clone or fetch from it, nor push from nor into it),
but is adequate if you are only interested in the recent history of a large project with a long history,
and would want to send in fixes as patches.
為什么淺克隆有這個限制? 為什么它只是補丁工作流程?
對於某些項目工作流,我只需要將來自單個分支的最新提交傳遞給編碼器,然后讓他們能夠push
他們的(快進)開發push
送到主服務器。 這部分是為了安全、IP 保護和 repo 大小,部分是為了減少大型 repo 會給天真的編碼員帶來的混亂。 是否有允許這樣做的 git 工作流程?
更新:根據 Karl Bielefeldt 的回答, git checkout --orphan
應該是正確的答案。 但是仍然需要將該分支單獨“克隆”給新用戶,並能夠有效地推送它。
手冊頁指出:
git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] --orphan
創建一個新的孤立分支,名為
<new_branch>
,從<start_point>
開始並切換到它。 在這個新分支上進行的第一次提交將沒有父級,它將成為與所有其他分支和提交完全斷開的新歷史的根。索引和工作樹的調整就像您之前運行過
git checkout <start_point>
。 這允許您通過輕松運行git commit -a
進行根提交來開始記錄一組類似於<start_point>
的路徑的新歷史記錄。當您想從提交中發布樹而不公開其完整歷史記錄時,這會很有用。 您可能希望這樣做以發布當前樹是“干凈”的項目的開源分支,但其完整歷史包含專有或其他阻礙的代碼位。
如果要開始記錄一組與
<start_point>
路徑完全不同的路徑的斷開連接歷史記錄,那么您應該在創建孤立分支后立即通過運行git rm -rf .
清除索引和工作樹git rm -rf .
從工作樹的頂層。 之后,您將准備好准備新文件、重新填充工作樹、從其他地方復制它們、提取 tarball 等。
VonC 指向 Junio 評論的鏈接很有趣。 我認為手冊應該在這種情況下提供指導,並允許正確的命令 [例如clone <branch> --options
] 僅提取 repo 的相關部分。 顯然,通過在歷史底部有一些鏈接提交和 SHA1 來鎖定 repo 匹配, push
成功的可能性會增加。
更新 Git 1.9.0:2014 年 2 月 14 日發行說明。
“過去禁止從淺克隆存儲庫中獲取,主要是因為所涉及的代碼路徑沒有經過仔細審查,我們也沒有費心支持這種用法。此版本試圖以更可控的方式允許對象從淺克隆存儲庫中傳輸出來(即接收器變成了一個歷史被截斷的淺層存儲庫)。”
這對淺層克隆人來說是個好消息。 下一步 - 可能是窄克隆。
正如 Junio C. Hamano(主要 Git 維護者) 所說:
規則是不是或多或少像:
如果您的淺層存儲庫的歷史擴展不夠長,並且另一個存儲庫在您截斷的歷史記錄之前分叉,則您無法計算共同祖先,也無法推出。
2014 年更新:請參閱“ git clone --depth 1(淺克隆)是否比它看起來更有用? ”:Git 1.9 將取消該限制!
2015 年更新:使用 Git 2.5+,您甚至可以獲取單個提交。 請參閱“從遠程 git 存儲庫中提取特定提交”
原始答案(2011 年 8 月):
其實仔細想想,比“不能算公”強多了。
歷史可能是這樣的:
R---R---R
/
--R---R---X---X---S---S---S
其中
S
是您在淺層存儲庫中的提交,R
是接收推送的存儲庫中存在的提交。
因為你的歷史很淺,所以兩個存儲庫都沒有“X
”,這是為了保持接收者存儲庫的歷史完整而需要存在的提交; 接受者一開始並不膚淺,我們不想讓它變得膚淺。如果您前段時間進行淺層克隆,在另一端進行時不與另一端通信而工作,並且如果另一端的進度包括歷史的倒帶和重建,您將看到類似的拓撲。
上圖中最左邊的 'S
' 可能是當你以深度 1 進行淺層克隆時分支的尖端,從那時起,遠端可能已經丟棄了最上面的三個提交並重建了它的歷史記錄,導致最右邊的 'R
' .
在這種情況下,推送到遙控器的HEAD
將失敗。
所以它在某些情況下可以工作,但不受支持:
如果我必須要說些什么...
我認為“不支持”是提供足夠好的信息的簡潔方式,但它只適用於聰明人。
不是每個人都聰明; 有些人自己嘗試一下,發現該操作似乎對他們有限的試驗次數有效,並得出結論,它在大多數情況下都有效。
他們祝賀自己說“大部分時間”而不是“總是”的智慧。
當他們看到它不起作用時,他們會感到不安,即使他們已經被警告過。
有關淺克隆更新過程的更多信息,請參閱“如何更新 git 淺克隆? ”。
是否有允許這樣做的 git 工作流程?
是的,它是將修復作為補丁發送。 git format-patch
專門設計用於啟用此功能。 它被稱為“看門人”工作流程,如果您想通過谷歌搜索更多詳細信息。 很難相信像您這樣關注“安全和 IP 保護”的組織還沒有使用類似的東西,其中一個人或一個小組負責在“不受信任”的更改進入實際構建之前對其進行審查。
根據您的評論,我現在對您的要求有了更好的了解。 我推薦的是創建一個孤立分支(請參閱git checkout --orphan ),無論您希望開發人員從哪個點開始。 僅將該分支克隆到這些開發人員可訪問的不同存儲庫,並讓他們從該存儲庫中正常克隆、推送和拉取。
然后,當您需要將他們的更改重新集成到官方受保護的存儲庫時,只需拉取他們的分支,使用git branch
復制它,這樣您就不會覆蓋原來的孤兒(以防您以后想重復該過程),然后變基復制到您的原始分支點,然后合並或正常進行。 歷史看起來像是直接從您的受保護存儲庫中工作的。
這比正常情況要復雜一些,但這是額外隔離所付出的代價。
我找到了一個使用 git bundles 的解決方法。
此解決方案將像“git push”一樣將完全相同的提交復制到其他存儲庫,並且不需要重新定位或導致更改提交 ID。
不幸的是,它需要對目標主機進行 shell 訪問(例如 ssh)。
我將通過示例展示解決方案。
首先,為了演示目的,我們需要獲得一個淺層克隆。
讓我們從https://github.com/skarnet/s6-rc獲取單個提交版本 v0.5.0.0 作為淺克隆到一個新的存儲庫。
我將在我的示例中使用 shell 變量,而不是直接在命令中包含示例設置,因為這將允許您在將變量設置為適用於您的情況的不同值后,將本次發布的指令直接復制/粘貼到您的 shell 中.
因此,請隨意使用不同的 URL 和版本來替換以下變量分配。
在我們的示例中,可以使用以下命令創建淺克隆:
$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ git init ${url##*/} && cd ${url##*/}
$ git pull --depth=1 "$url" $rel:master
這將創建一個包含新克隆的“s6-src”(當使用上述變量值時)子目錄。
現在我們的淺層克隆只包含一個提交,並且在本地存儲庫中丟失了它的所有父歷史記錄,我們將這個單一提交捆綁到一個 git bundle 文件中:
$ b=$rel.gbnd
$ git bundle create $b HEAD
這將使用之前設置的 shell 變量創建文件 v0.5.0.0.gbnd。
現在您必須將此文件復制到您通常希望推送到的目標機器上。 (只有 git push 拒絕從淺克隆推送,因此不會工作,至少不會使用舊的 git 版本。)
在目標主機上,執行以下操作以創建一個新的存儲庫作為子目錄,其中包含與之前捆綁的相同的提交:
$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ git init ${url##*/} && cd ${url##*/}
$ c=`git bundle unbundle $b | cut -d " " -f 1`; echo "$c"
$ git tag $rel $c # optional: create a tag for the imported commit.
$ git reset --hard $c
$ git fetch --depth=1 .
請注意,您應該將變量設置為與您在從中復制包的主機上所做的相同的值。
另請注意,如果存儲庫已存在,您可以省略“git init”。
就是這樣!
但是,后面的說明僅適用於常規結帳。
也許您想將淺克隆包導入到“裸”存儲庫中。
在這種情況下,請改為執行以下操作:
$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ cd ${url##*/}.git
$ c=`git bundle unbundle $b | cut -d " " -f 1`; echo "$c"
$ git tag $rel $c
$ git fetch --depth=1 . $rel
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.