vocus logo

方格子 vocus

專案中的專案:git submodule 的協作魔法與陷阱

更新 發佈閱讀 13 分鐘

學校快開學了,孩子們的暑假已進入尾聲,工作這邊則是越來越上手,也越來越忙碌。

這週因為工作上需要處理一個較為特殊的專案結構,接觸到了 git submodule,可能有很多人都聽過,但實際應用在專案中的機會可能不多。

在 iOS 開發的生態系中,我們早已習慣使用 CocoaPods 或 Swift Package Manager (SPM) 來管理第三方套件。它們就像是 App 開發的得力助手,幫我們處理好 Dependency 的下載、編譯與整合。那為什麼我們還需要 git submodule呢?它和這些套件管理工具有什麼不同?又適合用在什麼樣的情境?

今天,就讓我們一起來揭開 git submodule 的神秘面紗。

什麼是 git submodule

簡單來說,git submodule 允許你在一個 Git repository(我們稱之為主專案 superproject)中,嵌入另一個 Git repository(稱之為子模組 submodule)。

這就像是在你的專案資料夾中,放入一個指向另一個專案的「書籤」。主專案本身不包含子模組的實際程式碼,它只記錄了兩件事:子模組的 repository URL,以及要使用的確切版本。

這意味著,主專案最終儲存的是一個特定的 commit hash。當然也可以設定讓子模組追蹤(track)某個特定的 branch或 tag。當你更新子模組時,Git 會自動抓取該分支或標籤的最新 commit,並將其 hash 記錄到主專案中。這讓版本控制變得既精確又靈活。

.gitmodules 檔案結構

當你新增 submodule 時,Git 會自動在專案根目錄建立一個 .gitmodules 檔案,記錄所有子模組的資訊:

[submodule "SharedLibrary"]
path = SharedLibrary
url = https://github.com/some/shared-library.git
branch = main

這個檔案包含三個關鍵資訊:

  • path:子模組在主專案中的相對路徑
  • url:子模組的遠端 repository URL
  • branch:要追蹤的分支(選用,預設為預設分支)

這個檔案會被納入版本控制,確保團隊成員都能取得相同的子模組設定。

submodule vs. CocoaPods / SPM

既然有了方便的 SPM 和 CocoaPods,為什麼還需要 submodule?它們的核心差異在於「目的」與「管理方式」。

raw-image

簡單來說,SPM 和 CocoaPods 更像是「消費者」模式,我們引入別人打包好的套件來使用。而 git submodule 則更像是「協作者」模式,我們將另一個我們也需要維護的專案,直接納入目前的開發流程中。

多人協作的魔法與陷阱

git submodule 在多人協作時,既是魔法,也充滿陷阱。

魔法之處

當你的團隊有多個專案需要共享一個私有的核心模組時,submodule 就非常方便。開發者可以在開發主專案的同時,直接修改 submodule 的程式碼,並將主專案與 submodule 的變更分別提交。這對於需要頻繁、同步修改主從專案的情境非常有用。

陷阱之處

然而,它的複雜性也帶來了許多協作上的挑戰:

  1. 忘記初始化:新成員 git clone 主專案後,submodule 的目錄會是空的。他們必須記得執行 git submodule update --init --recursive,否則專案會因為缺少檔案而編譯失敗。這是最常見的新手錯誤。
  2. 游離的 HEAD (Detached HEAD):當你進入 submodule 目錄時,Git 會自動切換到主專案所記錄的那個特定 commit,這會使你處於 detached HEAD 狀態。如果你在這個狀態下做了修改並 commit,這個 commit 不屬於任何分支,很容易在下次更新時遺失!正確的做法是先 cd 進入 submodulegit checkout 到一個分支,再進行修改。
  3. 更新流程繁瑣:要更新 submodule 到最新版本,你需要:
    • cd 進入 submodule 目錄,git pull 拉取最新程式碼。
    • 回到主專案目錄,git add 子模組的路徑,將新的 commit hash 記錄下來。
    • git commit 提交這次的更新。
      這個多步驟的流程很容易出錯或遺漏。
  4. 合併衝突:當不同分支都更新了 submodule 到不同的 commit 時,合併分支就會產生衝突,而解決這種衝突比解決一般程式碼衝突要複雜一些。
  5. 遠端 URL 過期 (Outdated Remote URL):如果子模組的 repository URL 變更了(例如,專案遷移到新的 Git 伺服器),單純執行 git submodule update 是不夠的。主專案的 .gitmodules 檔案雖然更新了,但每個協作者的本地 .git/config 仍然記錄著舊的 URL。這時就需要 git submodule sync 來同步設定。

基本操作

雖然有些複雜,但掌握以下幾個核心指令,就能應付大部分場景。

# 1. 新增一個 submodule
git submodule add https://github.com/some/shared-library.git SharedLibrary

# 2. Clone 一個包含 submodule 的專案
# 方法一:Clone 後再初始化
git clone https://github.com/my/project.git
cd project
git submodule update --init --recursive

# 方法二:Clone 時一併處理
git clone --recurse-submodules https://github.com/my/project.git

# 3. 更新 submodule 到遠端的最新版本
# 方法一:自動更新所有 submodule 到其追蹤分支的最新 commit
git submodule update --remote --merge

# 方法二:手動進入 submodule 更新
cd SharedLibrary
git pull origin main
cd ..
# 將 submodule 的變更加入主專案的 stage
git add SharedLibrary
git commit -m "Update SharedLibrary to latest"

# 4. 移除一個 submodule
# 步驟較為複雜,需要清理多個地方

# 4a..gitmodules 移除設定並 stage 變更
git submodule deinit -f SharedLibrary

# 4b. 從 Git 索引移除子模組目錄
git rm -rf SharedLibrary

# 4c. 提交變更
git commit -m "Remove SharedLibrary submodule"

# 4d. 清理 .git 目錄中的殘餘檔案(可選,但建議執行)
rm -rf .git/modules/SharedLibrary

進階操作:讓協作更順暢的技巧 

為了讓團隊合作更順利,特別是為了照顧對指令不熟悉的成員,可以採用以下技巧來簡化流程:

使用 Git GUI 工具 

如果你不習慣使用 CLI,許多 Git GUI 工具(如 Sourcetree、Fork、Tower 等)都內建了對 submodule 的支援。你可以透過圖形化介面來新增、更新或同步子模組,這可以大幅降低操作的複雜度,並減少人為失誤。

設定全域 Git Config 

為了避免每次 pull 主專案後都要手動更新 submodule,你可以設定一個全域的 Git 設定:

git config --global submodule.recurse true

設定之後,每當你執行 git pull 時,Git 會自動幫你更新 submodule 到主專案所記錄的 commit,省去了一個步驟。

建立輔助 Scripts 

對於團隊協作,建立一個簡單的 shell script 來標準化更新流程是個好方法。一個可靠的腳本應該包含 sync 和 update兩個步驟,確保 URL 和 commit 都保持最新狀態。例如,你可以建立一個 update_project.sh 腳本:

#!/bin/sh
# update_project.sh

# 1. 拉取主專案的最新變更
echo "Pulling latest changes for the main project..."
git pull

# 2. 同步 submodule 的 URL 設定,確保本地指向正確的遠端倉庫
echo "Syncing submodule URLs..."
git submodule sync --recursive

# 3. 初始化並更新 submodule 到主專案指定的 commit
echo "Updating and initializing submodules..."
git submodule update --init --recursive

# 4. 更新所有 submodule 到各自追蹤分支的最新版本(可選)
echo "Updating all submodules to latest commits..."
git submodule foreach 'git fetch origin && git pull origin main'

# 5. 將 submodule 的變更記錄到主專案
echo "Staging submodule updates..."
git add .
git status

echo "Project and submodules are up to date!"
echo "Don't forget to commit the submodule updates if needed."

團隊成員只需要執行這個腳本,就能確保專案和所有子模組都保持在正確的狀態,避免了因操作不一致導致的問題。

結論:我該用它嗎? 

那麼,回到最初的問題:我們應該在 iOS 專案中使用 git submodule 嗎?

我的建議是:除非你有非常明確的理由,否則優先選擇 SPM

SPM 作為 Apple 官方的套件管理工具,已經非常成熟,與 Xcode 深度整合,能滿足絕大多數 Swift/Objective-C 的依賴管理需求,無論是公開還是私有套件。它的學習曲線更平緩,協作流程也更簡單直觀。

git submodule 則是一個更底層、更強大的工具,它不受語言或生態系的限制,適用於一些特殊場景:

  • 跨平台或跨語言專案:當你需要管理的模組包含非 Swift/Objective-C 的程式碼(例如 C++, Python, 或網頁前端資源),submodule 是唯一可行的原生 Git 方案。
  • 緊密的同步開發:你需要對模組進行非常頻繁的修改,並且希望將其與主專案的開發流程緊密綁定。
  • 歷史專案結構:公司的專案結構有歷史因素,已經在使用 submodule

如果你決定使用 git submodule,請務必確保團隊中的每個人都理解其工作流程與潛在的陷阱,並建立清晰的協作規範。否則,它帶來的麻煩可能會遠大於便利。

希望這篇文章能幫助你更了解 git submodule,有任何疑問或想法也歡迎留言討論。

See ya!

留言
avatar-img
Rice把拔的生活與開發筆記
0會員
26內容數
這裡是我的開發與生活筆記。 分享iOS開發經驗、教學技巧,也記錄生活中的點滴與觀察。 偶爾來點評論,輕鬆聊聊技術與日常。
2025/08/22
新工作的頭兩週,我遭遇了8分鐘 build time 的巨型Legacy Objective-C專案。這個燙手山芋最後的結局會是....?
2025/08/22
新工作的頭兩週,我遭遇了8分鐘 build time 的巨型Legacy Objective-C專案。這個燙手山芋最後的結局會是....?
2025/07/31
SF Symbols 的出現,為我們提供了一套共享的「視覺語言」。降低了對設計方面的溝通成本,並提高了設計的一致性。
Thumbnail
2025/07/31
SF Symbols 的出現,為我們提供了一套共享的「視覺語言」。降低了對設計方面的溝通成本,並提高了設計的一致性。
Thumbnail
2025/07/16
Swift Forums 宣布 Android Workgroup 的計畫後,對 Mobile App Development 生態造成了什麼樣的變化?
Thumbnail
2025/07/16
Swift Forums 宣布 Android Workgroup 的計畫後,對 Mobile App Development 生態造成了什麼樣的變化?
Thumbnail
看更多
你可能也想看
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
這篇文章將介紹工程師使用版控和git的相關知識和技能,包括版本控制的意義和git的基本指令,以及開發流程和webhook的概念。
Thumbnail
這篇文章將介紹工程師使用版控和git的相關知識和技能,包括版本控制的意義和git的基本指令,以及開發流程和webhook的概念。
Thumbnail
建立自己的Module有哪些好處?
Thumbnail
建立自己的Module有哪些好處?
Thumbnail
這篇文章描述了作者從兼職開發轉為全職開發的過程,並分享了從混進學界指日可待的積極態度。作者也提及自己在專案製作與個人生活上的矛盾與感想,最後分享了專案管理和敏捷開發相關的文章與影片。
Thumbnail
這篇文章描述了作者從兼職開發轉為全職開發的過程,並分享了從混進學界指日可待的積極態度。作者也提及自己在專案製作與個人生活上的矛盾與感想,最後分享了專案管理和敏捷開發相關的文章與影片。
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。
Thumbnail
本篇文章介紹如何使用Git Bash進行版本控制操作,包括創建repository、查看狀態、歷程以及加入暫存和提交暫存等操作。透過基本的Git指令,您可以更深入地瞭解Git工具的使用方法。
Thumbnail
本篇文章介紹如何使用Git Bash進行版本控制操作,包括創建repository、查看狀態、歷程以及加入暫存和提交暫存等操作。透過基本的Git指令,您可以更深入地瞭解Git工具的使用方法。
Thumbnail
這系列是我在 2023 六角學院 Vue作品實戰班的筆記,筆記以本人理解的方式記錄。此篇主題為 Slot Props 進階應用 ,其中包含單筆資料、多筆資料。
Thumbnail
這系列是我在 2023 六角學院 Vue作品實戰班的筆記,筆記以本人理解的方式記錄。此篇主題為 Slot Props 進階應用 ,其中包含單筆資料、多筆資料。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News