Git Submodule 使用

himg

git 的 submodule 做爲一個獨立的 repo, 其擁有普通 repo 所有的功能, 咱們能夠徹底按照普通的 repo 管理命令來進入 submodule 中進行手動管理. 不過若是存在多個 submodule 位於同一 superproject 下時, 掌握一些 git submodule ... 命令就變得尤其重要了.git

本文列出了經常使用的一些 git submodule 管理命令, 並舉出實際應用中遇到的問題及解決方案.github

submodule 介紹

在 git 倉庫 superproject 的目錄中使用 git submodule add https://github/HanleyLee/C 便可將 https://github/HanleyLee/C 做爲一個 submodulesuperproject 依賴與管理.bash

submodule 被修改時咱們能夠在 superproject 中獲得通知:markdown

  • submodule 中添加了新文件:oop

    himg

  • 修改了 submodule 中的內容 (沒有 new commit):fetch

    himg

  • 在 submodule 中產生了 new commit:ui

    himg

submodule 特色

repo Test 做爲 submodulesuperproject 管理後:url

  • 咱們仍然能夠進入 repo Test 的目錄中對相關內容進行修改, 而後經過經常使用的 git 命令進行操做.
  • superproject 下能夠經過 git submodule *** 命令來管理其下的全部子倉庫, 使其與遠程庫保持同步或推送到遠程庫.

submodule 的版本如何被管理的 (大體思路)

添加 git submodule 的方法很簡單, 使用 git submodule add <repo url> 便可. 添加完以後, 在 superproject 的目錄下會產生一個 .gitmodule 文件, 文件的結構以下:spa

[submodule "C"]
    path = C
    url = git@github.com:HanleyLee/C.git
[submodule "Cpp"]
    path = Cpp
    url = git@github.com:HanleyLee/Cpp.git
複製代碼

能夠看到, .gitmodule 文件中標記了每個 submodulepathurl.指針

而後咱們進入 ./C:

$ cd ./C
$ ls -l
$ ls -lhia

    35107110 drwxr-xr-x   9 hanley  staff   288B May  7 21:15 .
    35107043 drwxr-xr-x  14 hanley  staff   448B May  7 20:45 ..
    35107127 -rw-r--r--   1 hanley  staff    26B May  7 19:59 .git

$ cat .git

    gitdir: ../.git/modules/C
複製代碼

咱們發現 ./C/.git 居然是一個文件 (常規 git 目錄中的 .git 是文件夾), 而後其內容指向了另外一個文件夾 (相似於指針), 咱們再去到那個文件夾:

$ cd ../git/modules/C
$ ls -lhia

    35107128 drwxr-xr-x  16 hanley  staff   512B May  7 21:17 .
    35107126 drwxr-xr-x   9 hanley  staff   288B May  7 19:59 ..
    35112722 -rw-r--r--   1 hanley  staff    17B May  7 21:17 COMMIT_EDITMSG
    35109064 -rw-r--r--   1 hanley  staff     0B May  7 21:44 FETCH_HEAD
    35109063 -rw-r--r--   1 hanley  staff    16B May  7 21:44 FETCH_LOG
    35112529 -rw-r--r--   1 hanley  staff    21B May  7 20:25 HEAD
    35116056 -rw-r--r--   1 hanley  staff    41B May  7 21:15 ORIG_HEAD
    35114647 -rw-r--r--   1 hanley  staff   319B May  7 20:47 config
    35107131 -rw-r--r--   1 hanley  staff    73B May  7 19:59 description
    35107132 drwxr-xr-x  15 hanley  staff   480B May  7 19:59 hooks
    35116224 -rw-r--r--   1 hanley  staff   1.7K May  7 21:17 index
    35107129 drwxr-xr-x   3 hanley  staff    96B May  7 19:59 info
    35107178 drwxr-xr-x   4 hanley  staff   128B May  7 19:59 logs
    35107159 drwxr-xr-x   9 hanley  staff   288B May  7 21:40 objects
    35107174 -rw-r--r--   1 hanley  staff   112B May  7 19:59 packed-refs
    35107146 drwxr-xr-x   5 hanley  staff   160B May  7 19:59 refs
複製代碼

咱們發現這個文件夾纔是 submodule 的真實 .git 文件夾, 咱們對於 submodule 的所作的 commit 信息也都保存在這裏.

submodule 經常使用命令

  • git submodule: 顯示全部 submodule, 等同於git submodule status

  • 添加 submodule 到現有項目

    1. Run git submodule add -b <branch> --name <name> <repo-url> <local dir>
    2. Commit both files on the superproject
  • 從當前項目移除 submodule

    1. git submodule deinit -f <submodule_path>
    2. rm -rf .git/modules/<submodule_path>
    3. git rm -f <submodule_path>
  • 複製含 submodule 項目到本地

    1. Clone the superproject as usual
    2. Run git submodule init to init the submodules
    3. Run git submodule update to have the submodules on a detached HEAD

    或者直接執行 git clone --recurse-submodules <repo-url>

  • git submodule init: 將本項目所依賴的 submodule 進行初始化

  • git submodule update: 將本項目所依賴的 submodule 更新到本地最新版本

  • git submodule update --init: 前面兩個命令的合併

  • git submodule update --init --recursive: 前面三個命令的合集, --recursive 是爲了保證即便 submodule 又嵌套了 sub-submodule, 也能夠被執行到. 這個命令比較全面, 會常用

  • git submodule update: 更新 submodule 爲 superproject 本次 commit 所記錄的版本 (本地版本爲舊版本的話那麼就與舊版本保持同步!)

  • git submodule update --remote: 更新 submodule 爲遠程項目的最新版本 (更爲經常使用!)

  • git submodule update --remote <submodule-name>: 更新指定的 submodule 爲遠程的最新版本

  • git push --recurse-submodules=

    • check: 檢查 submodule 是否有提交未推送, 若是有, 則使本次提交失敗
    • on-demand: 先推送 submodule 的更新, 而後推送主項目的更新(若是 submodule 推送失敗, 那麼推送任務直接終止)
    • while: 全部的 submodule 會被依次推送到遠端, 可是 superproject 將不會被推送
    • no: 與 while 相反, 只推送 superproject, 不推送其餘 submodule
  • git pull --recurse-submodules: 拉取全部子倉庫 (fetch) 並 merge 到所跟蹤的分支上

  • git diff --submodule: 查看 submodule 全部改變

  • git submodule foreach '<arbitrary-command-to-run>': 對全部 submodule 執行命令, 很是有用, 如 git submodule foreach 'git checkout main'

爲何 superprojectgit pull 以後 submodule 沒有切到最新節點?

默認狀況下, git pull 命令會遞歸地抓取子模塊的更改 (fetch), 然而, 它不會將 submodule merge 到所跟蹤的分支上. 所以咱們還須要執行 git submodule update.

若是咱們想一句話解決, 那麼可使用 git pull --recurse-submodule, 這個能夠在拉取完 submodule 後再將其 merge 到所跟蹤的分支上.

若是咱們想讓 Git 老是以 --recurse-submodules 拉取, 能夠將配置選項 submodule.recurse 設置爲 true. 具體命令爲 git config --global submodule.recurse true. 此選項會讓 Git 爲全部支持 --recurse-submodules 的命令使用該選項 (除 clone 之外).

git pull --recurse-submodule 會讓咱們的pull命令遞歸地用於全部 submodule, 若是你的 submodule 數量過多的話可能會等較長時間. 這時可使用 git pull && git submodule update --init --recursive 一句話命令來只拉取更新了的 submodule 並更新到最新 commit, 使用 git config --global alias.sdiff '!'"git diff && git submodule foreach 'git diff'" 爲此命令設置 alias

submodule 沒有提交commit的狀況下推送 superprojectcommit

若是咱們在主項目中提交併推送但並不推送子模塊上的改動, 其餘嘗試檢出咱們修改的人會遇到麻煩, 由於他們沒法獲得依賴的子模塊改動. 那些改動只存在於咱們本地的拷貝中.

爲了確保這不會發生, 咱們可讓 Git 在推送到主項目前檢查全部 submodule 是否已推送. git push 命令接受能夠設置爲 checkon-demand--recurse-submodules 參數. 若是任何提交的 submodule 改動沒有推送那麼 check 選項會直接使 push 操做失敗.(此外還有 demand, while, no 選項, 參考前節命令列表進行理解)

爲了之後方便, 咱們能夠設置默認檢查 git config --global push.recurseSubmodules check

爲何每次 update 後 submodule 的 HEAD 狀態變爲了 detached?

不少人用了 git submodule 後, 都發現每次 update 以後, submodule 中的 HEAD 都是 detached 狀態的, 即便本次 git checkout master 後, 下次更新仍然恢復原樣, 難道就沒有辦法使其固定在某個 branch 上嗎? 通過研究, 參考 stackoverflow 的答案, 我發現是能夠解決的.

問題的關鍵在於 .gitmodule 的配置:

[submodule "C"]
    path = C
    url = git@github.com:HanleyLee/C.git
    update = rebase
[submodule "Cpp"]
    path = Cpp
    url = git@github.com:HanleyLee/Cpp.git
    update = rebase
複製代碼

咱們須要添加 update = rebase 這行, 根據 git official 的說明

checkout
    the commit recorded in the superproject will be checked out in the submodule on a detached HEAD.

    If --force is specified, the submodule will be checked out (using git checkout --force), even if the commit specified in the index of the containing repository already matches the commit checked out in the submodule.

rebase
    the current branch of the submodule will be rebased onto the commit recorded in the superproject.

merge
    the commit recorded in the superproject will be merged into the current branch in the submodule.
複製代碼

submodule 的 update 有多種選擇, 默認狀況下是 checkout, 其會根據 superproject 所記錄的 submodulecommit 進行 checkout. 相似於 git checkout 4eda5fgrd, 這必然致使 submoduleHEAD 處於 detached 狀態. 解決辦法就是使用 rebase(merge 也能夠), 這樣當咱們對 submodule 設置了一個初始的 branch 後, 其之後都只會在這個 branch 上對遠程的最新 commit 進行 rebase, 不會致使 detached 狀態的產生.

submodule 的目錄爲 ./C/ 爲例. 具體的解決步驟以下:

$ cd C/
$ git checkout main
$ cd ..
$ git config -f .gitmodules submodule.C.update rebase
複製代碼

此時, 之後再使用 git submodule update 就不會有 detached 狀態的產生了

另外, 在 .gitmodule 文件中也能夠指定 branch, 這裏指定的 branch 表示跟蹤的遠程倉庫的分支, 若是不指定, 則默認爲跟蹤遠程的 HEAD 所指向的 branch

參考

相關文章
相關標籤/搜索