基於 Git Subtree 的多分支專案管理與自動化整合

這篇筆記說明如何用 Git Subtree 設計一個在 GitHub 上「可重複練習、集中管理」的多分支專案架構,並透過 Makefile 實現自動化整合。

核心目標是:

  • template 分支快速建立開發練習分支,讓每次開發練習都從一致的起點開始
  • 每個開發分支獨立開發、保有完整 commit 紀錄
  • 最終成果自動整合到 master 分支的子資料夾,方便集中展示與維運管理

這樣的流程設計有以下優點:

  • 快速複製練習環境,便於反覆練習與教學
  • 每次練習皆為獨立專案,保留完整開發歷程
  • 成果集中到 master,只需瀏覽一個分支即可看到所有進度
  • 可搭配 GitHub Actions 等工具,自動化整合與部署流程

🧱 建立 template 分支

template 分支是所有練習的起始樣板,可以依照以下兩種方式建立:

  1. 建立初始開發環境:準備好基礎的資料夾架構、依賴設定檔(例如 package.jsonrequirements.txt)或共用的 utility function。
  2. 給予練習規格:可以只放入說明文件、範例輸入/輸出或 TODO 任務,讓想練習的人依據這份規格自行開發。

建立指令如下:

# 從 master 建立 template 分支
git checkout -b template

# 推送到遠端
git push origin template

⚙️ 更改預設分支(GitHub 操作)

  1. 前往 GitHub Repo 頁面
  2. 點擊「⚙ Settings」
  3. 左側選單中選擇「Branches」
  4. 將 Default branch 改為 template

這個設定是為了能夠先刪除 master 分支並重建,讓 master 存放練習後的成果


🗑️ 刪除 master 並重建

如果你想讓 master 分支只存放整合後的成果,而非開發內容,可以重新建立一個乾淨的 master 分支:

# 刪除遠端 master 分支
git push origin --delete master

# 本地切換並建立新的空 master 分支
git checkout --orphan master

# 移除所有檔案(保留工作區)
git rm -rf .

# 建立 .gitignore 和 Makefile
touch .gitignore Makefile

# 提交並推送新的空白 master 分支
git add .gitignore Makefile
git commit -m "Initialize new master branch"
git push -u origin master

⚠️ 請記得將預設分支從 template 改回 master,以便未來集中展示成果與管理。


用 git subtree 將多分支專案整合

git subtree 是 Git 提供的工具,允許你將另一個分支的內容匯入到當前分支的某個子資料夾,並保留其完整的 commit 歷史。

git subtree add --prefix=<資料夾路徑> <遠端名稱>/<分支名稱>

這條指令的作用是將指定的遠端分支內容匯入到當前分支的某個子資料夾中,並保留該分支的完整 commit 歷史

  • --prefix=<資料夾路徑>

    • 指定將分支內容匯入到當前分支的哪個子資料夾。
    • 如果該資料夾不存在,Git 會自動建立。
  • <遠端名稱>/<分支名稱>

    • 指定要匯入的遠端分支。
    • 例如,origin/feature-1 表示從遠端 originfeature-1 分支匯入。

在本專案中,我們使用 git subtree 的目的包括:

  • 將每個練習分支(如 feature_1)的專案內容,整合匯入到 master 分支的對應子資料夾(如 feature_1/
  • 避免用 mergecherry-pick 導致 commit 歷史混亂
  • 可重複執行,不會影響其他資料夾內容

建立 Makefile 自動遷移腳本

以下是 master 分支根目錄的 Makefile 實作:

.PHONY: archive archive-all

# 將指定分支內容以 git subtree 匯入對應資料夾,保留完整 commit 紀錄
archive:
ifndef name
	$(error Usage: make archive name=feature-<X>)
endif
	@echo "🌲 Archiving $(name) into master:$(name)/ using git subtree..."

	# 確保在 master 分支
	@git checkout master

	# 檢查有無未提交的變更
	@if [ -n "$$\(git status --porcelain)" ]; then \
		echo "❌ master branch has uncommitted changes. Please commit or stash first."; \
		exit 1; \
	fi

	# 匯入分支內容到對應資料夾(不使用 --squash,可保留所有 commit)
	@git subtree add --prefix=$(name) origin/$(name)

	@echo "✅ Done archiving $(name) into master:$(name)/"

# 批次對所有符合 feature-* 的分支執行 subtree 匯入
archive-all:
	@git fetch origin
	@git branch -r | grep 'origin/feature_' | sed 's|origin/||' | while read branch; do \
		$(MAKE) archive name=$$branch; \
	done

📦 歸檔練習分支內容

每完成一個 feature_<n> 分支的專案後,執行:

make archive name=feature_<n>

系統會自動將該分支所有檔案以 git subtree 匯入到 masterfeature_<n>/ 資料夾中,保留完整 commit 歷史

若要一鍵歸檔所有 feature_<n> 分支,可執行:

make archive-all

🚀 後續可整合 GitHub Actions 自動化流程

未來可以使用 GitHub Actions 自動觸發 make archive name=feature_<n> 指令,將 PR 合併後的分支內容自動歸檔到 master 的對應子資料夾。建議流程如下:

  1. 當 PR 合併至 feature_<n> 分支時觸發 workflow
  2. 使用 actions/checkout 切換到 master 分支
  3. 使用 git fetch 確保能抓到對應的分支內容
  4. 執行 make archive name=feature_<n> 自動歸檔
  5. 將更新過的 master 分支 push 回遠端

這樣可以避免手動切換與操作,讓每個練習成果在合併 PR 後自動整理到展示區。你也可以加入條件判斷,只對分支名稱符合 feature_* 格式的情況執行此流程。


✅ 結果展示

master
├── .gitignore
├── Makefile
├── feature_1/
│   ├── manage.py
│   ├── app/
│   └── ...
├── feature_2/
│   └── ...
└── feature_3/
    └── ...

這樣的架構讓你可以:

  • 保有一份可重複使用的範本分支(template)
  • 在多個練習分支開發而不互相干擾
  • 將成果統一集中到 master 展示,方便他人瀏覽
  • 將 CI/CD、部屬、技術分享等操作聚焦於 master 管理

非常適合用於建立個人練習倉庫、技術文章範例、或團隊協作練習環境。