在 Unity 使用 Git Submodule/Subtree 的困境及應對 – trouble when using Submodule or Subtree in Unity

同為 Unity 專案之間,一定有許多方便且時常重複使用的程式碼,是加快開發的重要工具。如果能夠同時在所有專案都有一份這樣的程式碼,又有手段可以同時對每一分程式碼進行更新或修改就再好不過了,於是我們通常會想到利用 Submodule / Subtree 來管理專案。

不過因為 Unity 專案的特性,事情可能沒有那麼簡單容易。

在使用 Git Submodule / Subtree 來管理你的 Unity 專案之前,你應該先釐清 Submodule / Subtree 的特性其實跟 Unity 專案結構是有些衝突的。

subproject.jpg

在 Unity 使用 Git Submodule / Subtree 的困境

上面的圖簡單呈現當我們使用 Submodule / Subtree 來建構子專案 (Subproject) 時會呈現的檔案結構。其中 Project A、Project B、Subproject 都是一個完整的 Git 專案,所以也有著各自的 .gitignore 檔。

我想大部分的人都理解 meta 檔在 Unity 專案中扮演著重要的腳色 (不清楚地可以看:適用於 Unity 的 git ignore 設定 – gitignore setting for unity),要保持專案的完整,許多 meta 檔案勢必要列入版控的追蹤範圍的。

不過 Unity 產生 meta 檔的位置是直接與關聯檔案位在同一處,也就是說,與子專案關聯的 meta 檔案會生成於子資料夾之中 (圖片的紅色問號處),可是那個位置的檔案不歸主專案 (Project A、Project B) 管理呀!

將這些 meta 檔理交由主專案的 git 來追蹤,而子專案的 git 則作為橫跨所有專案的純腳本之版控,不與 meta 檔扯上關係是我想達到的目標。歸屬於主專案的 GUID 檔案卻散佈在子專案的資料夾之中,這樣的專案結構與 Git Submodule / Subtree 在子資料夾中建立獨立環境的設計是相違背的。

(關於 Git Submodule / Subtree 的詳細原理會另外撰文)

Submodule / Subtree 的選擇及應對困境的手段

確實,如果你很清楚自己在做些甚麼,你可以決定針對性放棄某些 meta 檔案的版控,也會不會專案的完整性造成重大影響。又或者在盡量不去變動 meta 檔的前提下,交由共用的子專案版控這些 meta 檔。

如果你採用 Submodule 建立子專案結構,因為 Submodule 會將子專案的內外完全隔離成兩個 git 專案,所以子資料夾中的 meta 檔絕對無法在主專案中追蹤 (tracking)。如果你確定你不需要這些 meta 檔案的版本控制,那使用 Submodule 並不會有任何問題,通常沒有資源檔,也沒有用於掛載的 component 腳本在子專案其中便可。

反之,如果你要利用接下來我所描述的手段來版控子專案中的 meta 檔,你必須採用 Subtree 來建立子專案結構才行。Subtree 對於子專案的處理方式類似於副本的存在,可以說只有主專案是真正運作中的 git 專案,所以是可以對子專案的資料夾進行強制干涉的。

如果你有將 Submodule 結構替換成 Subtree 結構的需求,可以看看文末參考連結,接下來文章會直接描述我目前所使用的工作流程 (git-workflow)。

Unity Git Subtree Workflow

文章接下來的內容,可能會需要對 Git subtree 有基本了解後會比較容易理解。

事前準備

因為子專案中的檔案不需要追蹤 meta 檔,所以子專案的 gitignore 應該為:

*.meta

而主專案必須要追蹤重要的 meta 檔來保持完整,所以 gitignore 必須有:

!*.*.meta

建立 Subtree

在一開始添加 Subtree 至專案中時,必須用比較繞圈的手段,在本地端另外建立一個 branch 來作為 push/pull 的中繼點,進行額外的處理動作。

# 在本地端建立一個子專案的 branch
git remote add  <remote_name> <subproject_repo_url>
git fetch <remote_name>
git checkout -b <local_branch> <remote_name>/<remote_branch_name>
# 移動到主專案中,以本地端的 branch 建立 subtree
git checkout master
git read-tree --prefix=<subdirectory_name>/ –u <local_branch>
# ***注意 prefix 後面的斜線***

強制追蹤子資料夾中的 meta 檔

因為 subtree 沒有隔離子專案,所以配合 git ls-files 與 git add -f 是可以強制追蹤位於子專案空間中的檔案。這邊 git ls-files 使用 -o 參數過濾出未被追蹤的檔案,使用 –exclude-from=.gitignore 來採用主專案的忽略設定而非標準模式,來找出子專案中需要被追蹤的檔案;最後利用 xargs 將檔案清單投放至 git add -f 執行強制追蹤。

# 使用主專案的 gitignore 對所有檔案進行過濾,強制追蹤應該被主專案追蹤的檔案
git checkout master
git ls-files -o --exclude-from=.gitignore | xargs git add -f

更新子專案 (將遠端的子專案更新合併至主專案中)

這邊沒有特別的處理,只有單純將子專案的 branch 進行 merge。

git checkout <local_branch>
git pull
git checkout master
git merge --squash -s subtree --no-commit <local_branch>

上傳子專案 (將主專案中對子專案進行的變動上傳至遠端)

首先從主專案 (master) 中將檔案變動拉到 中,再利用 git ls-files 的 -c 參數及 -i –exclude-standard 找出應該被子專案忽略,卻被主專案追蹤的檔案,執行 git rm –cached 的指令。

# 首先從主專案(master)中將檔案變動拉到 <local_branch> 中
git checkout <local_branch>
git merge --squash -s subtree --no-commit master
# 取消對於子專案應該忽略的檔案的追蹤
git ls-files -c -i --exclude-standard | xargs git rm --cached
# 留下 commit 後上傳至遠端
git commit -m <message>
git push

其中的重點步驟,可以視需要選擇只移除追蹤,或者連檔案都移除

git ls-files -c -i --exclude-standard | xargs git rm -f

後話

老實說,我不覺得這是一個方便的 workflow,只是在網路上搜索許久後,對於 Unity 專案切分的討論少之又少,Unity 的專案結構也屬特例,並沒有發現與之對應的 Git 操作手段。

所以這份 workflow 是我自行發展的結果,目的是將 submodule/subtree 不追蹤的 meta 檔案交由主專案版控,不排除之後會發現更好的做法來解決相同的問題。

另外補充,這份 workflow 並不是在 Unity 使用 submodule/subtree 則必備的。一切端看你的專案切分及規畫中,希望如何去管理 meta 檔,並理解如何避免專案有資訊遺失的情況。

參考

廣告

對「在 Unity 使用 Git Submodule/Subtree 的困境及應對 – trouble when using Submodule or Subtree in Unity」的想法

  1. 我們主專案上傳至 Engine resource (code, textures, and so on…) repository,
    其 *.meta 直接上傳到該 repository,
    其他子專案則直接使用 Submodule 來引用該 Engine repository,
    在 Unity 中使用該些 Engine resource 沒有什麼大問題。

    如果遇到 GUID conflict,
    我們會強迫子專案的資源修改其 GUID,
    而不修改 Engine 資源的 GUID,
    使用至今,還沒有遇過大問題過。

    按讚數

    • 確實很大部分的情況下,並不需要使用這套繁瑣的工作流程,也不會產生問題。

      這篇文章中的工作流程,目的在於將 submodule/subtree 底下的 meta 檔案之版控交給產品專案本身。

      設想的情況是 submodule/subtree 不打算或無法追蹤這些 meta 檔,可能是為了維持腳本庫的簡潔,又或者是來自外部的plugin等因素。

      我重新修改了一些描述及補充,希望不會讓讀者誤會以為這份 workflow 是必要的,這只是因應特定需求所設計出來的 workflow。

      按讚數

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s