在 hexo 中使用 git submodules 管理主題

昊羲發表於2019-01-03

hexo 中有著豐富的主題可以選擇, 大部分的使用示例或者教程都是將主題 clone 到 theme 資料夾中來使用. 但這樣來使用主題會存在如下的問題:

  • theme 中的主題屬於一個獨立的 Git 專案, 有自己的 .git 專案資料夾, 提交 hexo 專案時預設不會提交 theme 的 .git 資料夾, 在其他電腦上 clone 後會失去 theme 原本的版本控制功能.
  • 使用自己修改的主題時, 每次更改完主題, 需要在主題資料夾中提交一次, 然後再在 hexo 專案根資料夾中提交一次, 會產生兩次修改內容一樣的提交, 不夠優雅.

還好萬能的 Git 針對這種問題已經有了成熟的解決方案, 通過自帶的 Git submodules 功能即可優雅的避免以上的問題.

Git submodules 簡介

Git submodules 稱之為 Git 子模組. 子模組允許你將一個 Git 倉庫作為另一個 Git 倉庫的子目錄. 它可以讓你將另一個倉庫克隆到自己的專案中, 同時還保持提交的獨立. 它非常適合我們程式設計師在專案管理時遇到的一種情況: 某個工作中的專案需要包含並使用另一個專案. 這些包含的專案也許是第三方庫, 或者你獨立開發的, 用於多個父專案的庫. 你想要把它們當做兩個獨立的專案, 同時又想在一個專案中使用另一個.

在 hexo 中使用豐富的第三方主題的情況正非常符合這種情景.

Git submodules 使用

瞭解了 Git submodles 的使用場景後, 這麼強力的工具如何在 hexo 中來使用呢. 在這裡演示下我的用法.

首先初始化一個用來演示的 hexo 專案:

npm install hexo-cli -g
hexo init blog
cd blog
npm install
hexo server
複製程式碼

執行以上操作後, 開啟瀏覽器進入 http://localhost:4000/ 便可以預覽到初始化好的 hexo 頁面. 在專案的 theme 資料夾中可以看到使用了預設的 landscape 主題. 在這個 hexo 專案中建立起 Git 版本管理.

git init
git commit -m 'initial project'
複製程式碼

現在一個 hexo 本地倉庫已經建立好了並將初始化的檔案提交了進去.

基礎用法

現在我們使用 Git submodules 的方式來選擇一個第三方主題來替換原本的 landscape 主題. 這裡選擇我比較喜歡的的 pure 主題.

git submodule add https://github.com/cofess/hexo-theme-pure themes/pure
複製程式碼

git 便會將 hexo-theme-pure 主題作為一個專案子模組 clone 到 themes/pure 中. 同時 hexo 專案中會自動生成一個 .gitmodules 檔案, 這個配置檔案中儲存了專案 URL 與已經拉取的本地目錄之間的對映.

.gitmodules 檔案內容

$ cat .gitmodules
[submodule "themes/pure"]
	path = themes/pure
	url = https://github.com/cofess/hexo-theme-pure
複製程式碼

git 目前狀態

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   .gitmodules
        new file:   themes/pure
複製程式碼

可以看到, 雖然 themes/pure 是工作目錄中的一個子目錄, 但 Git 還是會將它視作一個子模組. 當不在那個目錄中時, Git 並不會跟蹤它的內容, 而是將它看作該倉庫中的一個特殊提交.

主題 clone 好後, 按照主題的說明安裝好需要的外掛模組, 再執行 hexo s, 重新開啟 http://localhost:4000/ 便可以看到使用新主題的部落格頁面了.

提交新的 git 記錄

$ rm -rf themes/landscape
$ git commit -am 'added pure themes'
create mode 100644 .gitmodules
create mode 160000 themes/pure
複製程式碼

刪除舊的 landscape 主題, 使用 git commit -am 重新提交新增了 pure 主題的 hexo 專案, 可以看到 git 使用 160000 模式建立 themes/pure 記錄. 這是 git 中的一種特殊模式, 它本質上意味著將一次提交記作一專案錄記錄, 而非將它記錄成一個子目錄或者一個檔案.

拉取含子模組的修改

主題作為子模組新增到專案中後, 若主題作者有更新, 便可通過兩種方法來拉取主題的更新內容.

  1. 進入themes 下主題目錄, 執行 git fetchgit merge origin/master 來 merge 上游分支的修改
  2. 直接執行 git submodule update --remote, Git 將會自動進入子模組然後抓取並更新

更新後重新提交一遍, 子模組新的跟蹤資訊便也會記錄到倉庫中.

拉取含子模組的專案

使用 git clone 命令預設不會拉取專案中的子模組, 在 clone 後的專案中可以通過執行兩個命令:

  1. git submodule init 初始化本地配置檔案
  2. git submodule update 從該專案中抓取所有資料並檢出父專案中列出的合適的提交

也可在 clone 使用 git clone --recursive 命令, git 就會自動初始化並更新倉庫中的每一個子模組.

高階使用

通過子模組基礎用法, 可以直接方便的跟蹤管理一些的簡單的主題. 但很多主題都存在一些自己的配置專案, 需要我們根據自己的需要來進行設定, 或者我們想要在主題的基礎上自定義修改自己喜歡的主題, 這個時候就需要對主題進行修改並提交倉庫以便在各處使用.

不過對於主題倉庫我們一般沒有提交的許可權, 不能提交到主題源倉庫中. 此時可以通過 fork 功能, 在源主題上 fork 出自己專案, 從而在自己倉庫中進行提交來跟蹤修改.

這裡我在 pure 主題上 fork 出了一個自己的主題倉庫 my-hexo-theme-pure, 使用這個倉庫按照之前的步驟替換原本的主題來作為子模組.

修改子模組

當執行 git submodule update 從子模組倉庫中抓取修改時, Git 將會獲得這些改動並更新子目錄中的檔案, 但是會將子倉庫留在一個稱作 遊離的 HEAD 的狀態. 這意味著沒有本地工作分支(例如 "master")跟蹤改動, 此時做的任何改動都不會被跟蹤. 因此, 我們首先需要進入子模組目錄然後檢出一個分支.

$ git checkout stable
Switched to branch 'stable'
複製程式碼

若子分支倉庫中有未同步的更新, 可通過 git submodule update --remote --rebase 來同步最新的內容. 之後便可以開啟編輯器在子模組上工作修改程式碼了.

同步源主題的修改

主題作者釋出了新的主題功能或者修復了Bug, 我們想同步到自己的自定義主題當中. 因為我們的自定義主題是從原主題中 fork 出來的, 可以通過 git remote add source https://github.com/cofess/hexo-theme-pure 命令將源主題倉庫新增為子模組的 一個新的 source 倉庫. 然後執行 git fetch 拉取修改後, 便可以通過 git merge origin/master 來同步源主題的更新了.

釋出子模組的修改

子模組修改完成後, 我們便可以釋出到倉庫中, 以便在其他地方重新 clone 時可以使用最新的主題檔案. 為了防止我們遺忘子模組的提交, 可以在 push 時通過 git push --recurse-submodules=check 命令, 如果任何提交的子模組改動沒有推送那麼 check 選項會直接使 push 操作失敗.

另外也可以使用 git push --recurse-submodules=on-demand git 會自動嘗試推送變更的子專案.

擴充用法

以上介紹了使用子模組來管理 themes 的方法, 實際上在 hexo 中還可以使用 子模組來管理 hexo 的靜態部署檔案. 對於使用 github 託管靜態頁面等部署方式的使用者而言, 通過 hexo-deployer-git 外掛可以方便的自動化部署靜態頁面.

hexo-deployer-git 工作流程

hexo-deployer-git 部署的方式是在 hexo 專案根目錄下建立了一個 .deploy_git 資料夾, 並在其中建立了一個獨立的 git 分支, 將生成靜態檔案移入這個資料夾中並推送到指定的地址. 但 .deploy_git 資料夾預設也被寫入 .gitignore 檔案中, hexo 專案 git 庫不會記錄這個資料夾, 同時 hexo-deployer-git 在每次部署時也不會自動同步伺服器上的提交歷史, 而是強制覆蓋舊的提交. 在新的電腦或路徑上重新 clone 後也會出現舊的靜態檔案記錄丟失的情況, 重新 deploy 後伺服器上舊的部署歷史也會丟失.

如果想要儲存每次的部署記錄, 那麼就可以將 .deploy_git 中的檔案也看做一個子專案, 以子專案的形式提交到 hexo 主專案中儲存, 就可以保持部署記錄不丟失, 並且在任何地方重新 clone 時都可以恢復最新的記錄.

新增 .deploy_git 中分支為子專案

.deploy_git 中是由部署外掛在 hexo 專案上建立的一個獨立分支, 只需通過傳遞 -b 選項將 hexo 專案的這個分支作為主專案的依賴即可, 例如部署在 coding 時, 使用的 coding-pages 分支:

git submodule add -b coding-pages <site>
複製程式碼

參考資料

Pro Git book - 子模組 git-scm.com/book/zh/v2/…

相關文章