工作中會遇到這樣的問題,當你在一個項目上時,你需要在其中使用另外一個項目,這個項目也許是一個第三方開發的庫或者是你獨立開發 合並在多個項目中使用的。這樣就會產生一個問題:你想將兩個項目單獨處理但是又需要其中一個項目使用另一個。而Git通過子模塊處理這個問題,子模塊允許你將一個Git倉庫當做另一個Git倉庫的子目錄。允許你克隆另外一個倉庫到你的項目中並且保持你的提交相對對立。
開始使用子模塊
首先我們將一個已經存在的Git倉庫添加為正在工作的的倉庫的子模塊。運行git submodule add <url> 命令添加子模塊。如果你想將它放在其他地方,可以通過在命令結尾添加路徑來實現。
模塊添加成功了,接下來我們看一下工作區的變化。運行 git status 命令
我們會發現多兩個新的文件,首先是.gitmodules文件。這個文件是一個配置文件,保存了項目URL和已經拉取的本地目錄之間的映射,查看一下里面的內容
可以看到文件里包含了上述的兩條內容,要重點注意的是,這個文件跟其他文件一樣是處在版本控制之下的,它會和該項目的其他文件一樣被拉取推送,這也是為什么克隆該項目的人知道去哪里獲得子模塊的原因。
這里要注意一點,因為.gitmodules文件中的URL是人們第一次克隆項目時的地址,因此我們需要盡可能的確保這個URL是可以被其他人訪問的。
接下來我們看看另一個新的文件,運行 git diff 命令,
我們可以看到,雖然childModule1是工作目錄中的子目錄,但是Git會將它視作一個子模塊。當你不在那個目錄中時,Git並不會跟蹤他的內容,而是把它看做該倉庫的一個特殊提交。
當你將更改提交到版本庫時,我們會看到以下信息
注意childModule1記錄的160000模式。這是Git中的一種特殊模式,表示將一次提交記作一項目錄記錄的,而非將它記錄成一個子目錄或者一個文件。
除了自己添加子模塊,我們還可以克隆帶子模塊的目錄
克隆一個帶子模塊的項目
我們可以通過git clone <url> 來克隆一個帶子模塊的項目,在克隆這個項目時,默認會包含該子模塊的目錄,但是目錄里面沒有任何文件。克隆成功之后我們查看一下這個文件
childModule1文件目錄存在了,但是目錄是空的。這時我們需要運行兩個命令:第一個 git submodule init 用來初始化本地配置文件
第二個命令 git submodule update 用來拉取childModule1目錄的所有數據並檢出你上層項目里所列的合適的提交
到這里我們第一個克隆 包含子模塊項目的方法就到這了。既然說了有第一個,當然就有第二個了,第二個方法更簡單一點,直接一步到位,就是給 git clone 命令加一個--recursive 參數,他就會自動初始化並更新倉庫的每一個子模塊
子模塊克隆好之后(不管是用那種方法克隆)我們看一下它的狀態,
可以看到的是非常醒目的一串紅色的字母,那這一串字母代表獲得一個游離的HEAD,這意味着HEAD指向的只是一次提交。那會這樣的原因是因為我們克隆項目的時候,Git將會獲得這些改動並更新子目錄的文件,但是會將子模塊留在一個叫做“游離的HEAD”的狀態,這也意味着子模塊沒有本地工作分支跟蹤改動。所以我們的做的任何改動都不會被跟蹤。
現在子模塊克隆好了,子目錄也處於我們先前提交的狀態了。
如果另外的一個開發者更改了子目錄的代碼,並作了提交,然后我們拉取那次引用合並,然后查看一下主目錄的狀態。
git merge origin/master 命令的作用是將上游分支合並到本地
因為我們合並的僅僅只是只想子模塊的指針,但是它並不更新子模塊的代碼,所以主目錄的工作狀態處於一個臨時的狀態
然后我們再查看一下工作區和版本庫的區別
會發現,我們所擁有的指向子模塊的指針和子模塊目錄的真實狀態並不匹配。所以為了修復這一點我們要再次執行 git submodule update 命令
執行之后再次查看工作區和版本庫的區別我們會發現沒有區別了。
有一個很不方便的地方就是我們每次從主項目中拉去子模塊的變更都要這樣做。