我有像你一樣的問題:作爲子模塊的大型通用實用程序庫,以及依賴它的許多項目。我不想爲實用程序庫的每個實例都創建一個單獨的結帳。
jthill提出的解決方案工作正常,但它只解決了問題的前半部分,即如何保持git快樂。
缺少的是如何保持構建系統的快樂,期望實際的文件能夠使用,而不關心gitlink引用。
但是,如果你將他的想法和符號鏈接結合起來,你會得到你想要的!
爲了實現這一點,讓我們開始從你的榜樣
/home/projects/project1
/home/projects/project2
/home/projects/library_XYZ
項目假設兩個PROJECT1和項目2已經library_XYZ已經被添加作爲一個子模塊,而目前所有三個項目包含library_XYZ的完整結賬。
爲了通過一個共享的符號鏈接庫的結賬更換庫子模塊的全部檢出,這樣做:
sharedproject="/home/projects/library_XYZ"
superproject="/home/projects/project1"
submodule="library_XYZ"
cd "$superproject"
(cd -- "$submodule" && git status) # Verify that no uncommited changes exist!
(cd -- "$submodule" && git push -- "$sharedproject") # Save any local-only commits
git submodule deinit -- "$submodule" # Get rid of submodule's check-out
rm -rf .git/modules/"$submodule" # as well as of its local repository
mkdir -p .submods
git mv -- "$submodule" .submods/
echo "gitdir: $sharedproject.git" > ".submods/$submodule/.git"
ln -s -- "$sharedproject" "$submodule"
echo "/$submodule" >> .gitignore
,然後重複用於/ home /項目/項目2爲$上層項目相同的步驟。
這裏是做了什麼解釋:
第一子模塊結賬與「混帳子模塊DEINIT」去掉,留下library_XYZ身後,一個空目錄。請務必在執行此操作之前進行任何更改,因爲它會刪除結帳!
接下來,我們使用「git push」將任何尚未推送到共享項目的本地簽出提交保存到/ home/projects/library_XYZ。
如果因爲你沒有安裝遠程或的Refspec對於這不起作用,你可以這樣做:
(saved_from=$(basename -- "$superproject"); \
cd -- "$submodule" \
&& git push -- "$sharedproject" \
"refs/heads/*:refs/remotes/$saved_from/*")
這將節省子模塊的本地倉庫作爲遠程分支機構的所有分支機構的備份/家用/項目/ library_XYZ。 $ superproject目錄的基名稱將用作遠程的名稱,即i。即在我們的例子中是project1或project2。
當然,在/ home/projects/library_XYZ中並不存在該名稱的真正遠程名稱,但保存的分支將顯示爲當在此處執行「git branch -r」時所做的操作。
作爲一種安全措施,上述命令中的refspec並不以「+」開頭,所以「git push」不會意外覆蓋已經存在於/ home/projects/library_XYZ中的任何分支。
接下來,.git/modules/library_XYZ將被刪除以節省空間。我們可以這樣做,因爲我們不再需要使用「git submodule init」或「git submodule update」。這是因爲我們將共享/ home/projects/library_XYZ的檢出和.git目錄與子模塊,避免兩者的本地副本。
然後,我們讓git將空子模塊目錄重命名爲「.submods/library_XYZ」,這是項目中的文件永遠不會直接使用的(隱藏)目錄。
接下來我們將jthill的部分解決方案應用到問題中,並在.submods/library_XYZ中創建gitlink文件,這使得git將/ home/projects/library_XYZ視爲子模塊的工作樹和git repo。
現在出現了新的東西:我們創建一個名爲「library_XYZ」的符號鏈接,它指向/ home/projects/library_XYZ。這個符號鏈接將而不是置於版本控制之下,所以我們將它添加到.gitignore文件中。
在project1和project2中的所有構建文件將使用library_XYZ符號鏈接,就好像它是一個普通的子目錄,但實際上從/ home/projects/library_XYZ中的工作樹中找到文件。
除git之外沒有人實際使用.submods/library_XYZ!
但是,由於符號鏈接./library_XYZ沒有版本化,所以在簽出project1或project2時不會創建它。因此,我們需要注意,它會在失蹤時自動創建。
這應該PROJECT1 /項目2的構建基礎設施來實現與命令等同於下面的shell命令的:
$ test ! -e library_XYZ && ln -s .submods/library_XYZ
例如,如果PROJECT1使用一個Makefile建成幷包含以下目標規則更新子項目
library_XYZ/libsharedutils.a:
cd library_XYZ && $(MAKE) libsharedutils.a
然後我們插入線從上面的規則的動作的第一行:
library_XYZ/libsharedutils.a:
test ! -e library_XYZ && ln -s .submods/library_XYZ
cd library_XYZ && $(MAKE) libsharedutils.a
如果您的項目使用其他構建系統,則通常可以通過創建用於創建library_XYZ子目錄的自定義規則來執行相同的操作。
如果您的項目只包含腳本或文檔,根本不使用任何種類的構建系統,您可以添加一個腳本,用戶可以運行該腳本來創建「缺少的目錄」(實際上是:符號鏈接),如下所示:
(n=create_missing_dirs.sh && cat > "$n" << 'EOF' && chmod +x -- "$n")
#! /bin/sh
for dir in .submods/*
do
sym=${dir#*/}
if test -d "$dir" && test ! -e "$sym"
then
echo "Creating $sym"
ln -snf -- "$dir" "$sym"
fi
done
EOF
這將爲.submods中的所有子模塊簽出創建符號鏈接,但前提是它們尚不存在或者它們已損壞。
到目前爲止,從傳統的子模塊佈局轉換到允許共享的新佈局。
一旦您已經有佈局承諾,地方檢查出上層項目,去它的頂級目錄,然後執行以下操作以啓用共享:
sharedproject="/home/projects/library_XYZ"
submodule="library_XYZ"
ln -sn -- "$sharedproject" "$submodule"
echo "gitdir: $sharedproject.git" > ".submods/$submodule/.git"
我希望你明白了吧: project1和project2使用的library_XYZ子目錄是未版本化的符號鏈接,而不是與「.gitmodules」中定義的子模塊路徑相對應的子目錄。
符號鏈接將由構建基礎結構本身自動創建,然後將指向.submods/library_XYZ,但這僅僅是,如果符號鏈接尚不存在,這一點很重要。
這允許手動創建符號鏈接,而不是讓構建系統創建它,因此也可以指向單個共享簽出而不是指向.submods/library_XYZ。
這樣你就可以在你的機器上使用共享簽出。
但是,如果另一個人沒有什麼特別的,只是檢出project1並執行正常的「git子模塊更新 - 初始library_XYZ」,事情將工作相同,沒有共享簽出。
在這兩種情況下都不需要更改檢出的構建文件!
換句話說,project1和project2的簽出將照常開箱即用,沒有其他人使用您的repo需要遵循特殊說明。
但是通過在構建系統有機會創建符號鏈接之前手動創建gitlink文件和library_XYZ符號鏈接,您可以在本地「覆蓋」符號鏈接並強制共享庫的結帳。
還有一個好處:事實證明,如果您使用上述解決方案,則不需要混淆「git submodule init」或「git submodule update」:它只是在沒有!
這是因爲「git submodule init」只是作爲「git子模塊更新」的準備工作而需要的。但是你不需要後者,因爲該庫已經在其他地方簽出了,並且在那裏已經有了自己的.git目錄。所以「git子模塊更新」沒有任何關係,我們也不需要它。
作爲不再使用「git子模塊更新」的副作用,也不需要.git/module子目錄。也不需要爲子模塊設置替代( - 參考選項)。
此外,您不需要在/ home/projects/project1和/ home/projects/project2中推/拉/ home/projects/library_XYZ的任何遠程控制。因此,您可以從project1和project2中刪除遠程用於訪問library_XYZ。
雙贏!
該解決方案唯一明顯的缺點是它需要符號鏈接才能工作。
這意味着,不可能在VFAT文件系統上籤出project1。
但是,那麼,誰呢?
即使這樣做,像http://sourceforge.net/projects/posixovl這樣的項目仍然可以解決文件系統的任何符號鏈接限制。
最後,一些建議Windows用戶在這裏:
符號鏈接可用,因爲通過mklink命令VISTA,但它需要特殊的權限。
但是,當從sysinternals使用「連接」命令時,即使在Windows XP時代,已經可以創建到目錄的符號鏈接。
此外,您可以選擇使用CygWin,即使沒有操作系統的支持,它也可以(AFAIK)模擬符號鏈接。
你真的不想這樣做。您可能在將來的某個時候出現一種情況,即當前版本的'project1'依賴於來自'library_XYZ'的不同提交,而不是當前版本的'project2'。如果他們共享,事情會變得有點困惑。最好只保留兩個獨立的子模塊實例並學習相關的工作流程... – twalberg 2014-12-09 19:11:49
@twalberg與此案例相關的正確工作流程是什麼? – theV0ID 2014-12-09 19:23:10
無論您在問題中提到了什麼 - 使用中央存儲庫(兩個子模塊存儲庫同步)或設置兩個子模塊直接在它們之間傳播更改。沒有「一個正確的方法」,但有很多方法是不正確的,甚至是徹頭徹尾的危險...... – twalberg 2014-12-09 19:25:35