代碼管理之git submodule 使用小結

前言

工作中碰到這樣一個場景,一個項目裏面的代碼分爲基礎代碼定製化代碼,定製化代碼是針對不同客戶的,基礎代碼需要和定製化代碼分開管理,部署的時候是作爲一個項目一起跑的。

我們在這裏使用git submodule功能來嘗試解決代碼的管理問題。

功能介紹

submodule 目前對 git 倉庫拆分的已有實現之一。
它允許將一個Git倉庫作爲另一個Git倉庫的子目錄。能夠將另一個倉庫克隆到自己的項目中,同時還保持獨立的提交。

功能聽上去非常的牛逼,那我們趕緊來試用一下吧!

使用

準備工作

我們將在Github上進行相關功能的操作,並在操作過程中時刻觀察倉庫狀態的變化,加深理解。

首先確保自己有GitHub的賬號哈~

在github新建兩個倉庫

很簡單,像這樣
新建倉庫
我這裏直接使用github新建了一個倉庫作爲主倉庫。
依葫蘆畫瓢直接在新建兩個子倉庫,分別起名child1_repochild2_repo(名字隨便起,自己認得就行)

我們把主倉庫(以下稱parent)clone下來,隨便提交點什麼,像這樣
在這裏插入圖片描述
同樣對子倉庫child1_repo(以下簡稱child1)和child2_repo(以下簡稱child2)進行一樣的操作。

將子倉庫添加至父倉庫

來到parent目錄下,執行

# []中爲子倉庫的git url
git submodule add [child1 url]
git submodule add [child2 url]

會出現如下提示:
示例我們來看看這條命令做了哪些事情:

  1. 首先,child1child2被克隆到了parent目錄下
  2. 使用git status命令查看以下文件狀態
    查看狀態·
    可以看到,除了兩個子倉庫外,還多了一個叫.gitmodules的文件,這是一份子模塊與路徑的映射關係圖,git 根據這份文件去識別 submodule。現在查看一下文件內容:
[submodule "child1_repo"]
	path = child1_repo
	url = [email protected]:xxx/child1_repo.git
[submodule "child2_repo"]
	path = child2_repo
	url = [email protected]:xxx/child2_repo.git

一個子倉庫對應一個git url,清晰明瞭。

提交

我們將parent下的改動進行提交,並push到遠程倉庫上
提交
我們注意到,在commit了改動之後,除了常規的100644之外(100代表regular file,644代表文件權限),還出現了160000

160000 代表 Git 中的一種特殊模式,它本質上意味着你是將一次提交記作一項目錄記錄
的,而非將它記錄成一個子目錄或者一個文件。
上面這句話是什意思呢,我們通過git diff 查看剛纔提交的信息

# child1_repo
Commit	8e182c74182adde49f2b6d192f2a85c50d87f538
Commit Message	add child1
# child2_repo
Commit	64a14d2c3f22aee584071b93c2bda4ef5243e4a2
Commit Message	add child2

可以看到,這兩個子倉庫在parent下commit的就是一次提交的信息,而git就把它們當作是一個目錄進行記錄的。

我們查看一下遠端倉庫的情況:
遠端倉庫
可以看到,兩個子倉庫均已經push到了parent下,另外在child1child2後面還跟着一串code,這是子倉庫的commitId的後綴,表示該子倉庫簽出時的版本(這個在下面解釋),這個code是不會顯示在克隆到本地的倉庫中的。

進入child1_repo @ 8e182c7觀察一下倉庫的狀態
child1
如圖所示,此時child1處於一種遊離的狀態,在git頁面無無法新建和編輯文件
點擊編輯會提示:

you must be on a branch to make or propose changes to this file

我們點擊上圖畫圈的部分,將其切換到master分支後,我們就可以執行正常的git操作。
(注意:此時我們已經是在child1_repo下進行操作的)

我們查看一下child1的commit記錄
commit記錄
可以看到,child1提交的commitId與parent中的child1_repo @ 8e182c7的後綴是一致的,因爲本地parent子模塊在push時簽出的版本正是8e182c7

總結一下,對於child1child2來說,只有它們的遠程 URL 會被記錄在父倉庫中,以及它們在主項目中的本地路徑簽出的版本

模擬多人協作

我們在git 頁面嘗試進入child1,其下有一個文件,我們嘗試編輯這個文件,並將修改的內容提交,像這樣
new
再看看child1的commit記錄,此時git head的指針已經指向了新的commitId:
b99d
這是一個標準的git操作流程。

我們返回到parent中查看一下child1的狀態,並沒有變化,記錄依舊保持在第一次簽出時的版本。

拉取遠程子模塊的代碼到本地

子倉庫在遠程更改了,那麼我們本地如何進行同步呢(如果只要修改主倉庫的代碼,正常的git操作就可以了

這裏以child1爲例,進入parent,執行

 git submodule update --remote child1_repo

這條命令將會拉取child1_repo中最新的提交,結果如下:
在這裏插入圖片描述
這裏,我們如果單純的執行git submodule update,我們拉取的將是遠程parent下最後一次簽出的子倉庫的版本。

另外一種更新子倉庫child1_repo的方法就是直接進入child1_repo目錄下,像任何普通的Git那樣進行操作即可。

本地子模塊修改提交到遠程

我們在本地的子模塊中進行了一些修改,需要進行提交,如何操作呢?

這裏依舊以child1_repo爲例進行闡述,在上述拉取child1最新的一次提交之後,我們本地相對於遠端的parent已經有了改動,我們現在在本地再次進行一些修改,並進行提交。
在這裏插入圖片描述
我們在本地cd到child1_repo目錄中瞅瞅~
在這裏插入圖片描述
在這裏插入圖片描述
首先,我們的修改是被捕捉到了,這說明我們是可以進行正常的git操作的。
觀察一下此時child1_repo的狀態,它沒有指向任何一個分支,而是停留在一個稱作“遊離的 HEAD”的狀態,這意味着沒有本地工作分支(例如 “master” )跟蹤改動,你也就沒辦法提交代碼。

解決方案很簡單,使用git checkout branch命令切換到某個分支就可以了
我們把之前修改commit一下,然後切換到master分支上。
在這裏插入圖片描述
在這裏插入圖片描述
我們嘗試將本地子倉庫的修改推送到遠程(上述操作存在一個問題,導致我本地的修改丟失了,記得先pull,我這裏重新修改提交了)

我們切回到parent,使用 add commit push 三連將代碼提交到遠端(記得先pull),完事我們查看下遠端的倉庫
在這裏插入圖片描述
看來已經正確的提交了:)

可能會出現的幺蛾子

Git對於子模塊的管理相對來說比較複雜。當然出現異常的情況也是不可避免的,我們來看看在使用git submodule的過程中可能會出哪些問題。

主模塊提交併推送了改動,而子模塊並沒有推送

如果我們在主倉庫中提交併推送但並不推送子模塊上的改動,其他人嘗試更新子模塊的人會遇到麻煩,因爲他們無法得到依賴的子模塊改動。那些改動只存在於我們本地的拷貝中。

我們嘗試一下上述過程,對child1_repo做一些修改並提交,但不推送。
在這裏插入圖片描述
這裏我又添加了一句話,並提交了。我們回到parent瞅瞅
在這裏插入圖片描述
在這裏插入圖片描述
好,parent追蹤到了submodule的改動,現在我們把它提交併推送到遠程
在這裏插入圖片描述
成功了…看來git並不會主動幫你檢測子模塊的改動是否推送。

我們把本地的倉庫刪除,重新克隆一份下來。我們觀察一下目錄,所有子倉庫只有一個空的文件夾,這裏需要我們去git submodule init初始化本地配置文件以及 git submodule update 拉取代碼。
在這裏插入圖片描述
直接報錯,無法更新了。

爲了確保這不會發生,你可以讓 Git 在推送到主項目前檢查所有子模塊是否已推送。
使用如下命令

git push --recurse-submodules=check  # 如果子模塊沒有提交,會直接報錯
# or
git push --recurse-submodules=on-demand  # 如果子模塊沒有提交,會嘗試提交,提交不成功同時會阻止主倉庫的推送

其他

其實問題還不少,暫時不一一復現了,主要出現的問題參考了這篇博客
另外還有別的可能出現的問題(e.g.將子目錄轉換成子模塊、git submodule update failed等),可以參考文章結尾給出的文檔

在有子模塊的項目中切換分支可能會造成麻煩

如果你創建一個新分支,在其中添加一個子模塊,之後切換到沒有該子模塊的分支上時,你仍然會有一個還未跟蹤的子模塊目錄,這時候如果不小心提交了這個子模塊(git commit -am “message”),就會有問題了。

提交和獲取的問題

對子模塊做了修改,需要先推送子模塊再主模塊,同時拉取的時候也需要先主模塊,再子模塊。

記得先切換分支

對子模塊做本地修改需要先檢出分支,否則有可能在 “遊離的 HEAD” 上做修改。

記得pull完了還得update一下

如果你的同事更新了 submodule,然後更新了父項目中依賴的版本號。你需要在 git pull 之後,調用 git submodule update 來更新 submodule 信息。這兒的坑在於,如果你 git pull 之後,忘記了調用 git submodule update,那麼你極有可能再次把舊的submodule 依賴信息提交上去(使用 git submit -am "message" 或者 git add .提交的人會遇到這種事)。

參考

git子模塊
git submodule(csdn)
子模塊 - Git Tower
git submodule updated failed

配圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章