201607211349Git multiple repositories management

一個專案往往會由許多 sub projects 組成才能運作,而這些 sub projects 可能被許多其他專案所共用而單獨形成一個 repository 由專人負責維護,這時候我們該怎麼管理這些 sub projects 呢?這裡列出三種管理方式,每種方式各有優缺點。

Git submodule

Git submodule 是三種方法裡面最早提出的作法,基本指令如下

初始化 submodule 專案

初始化 submodule 需要使用 submodule add 指令來告訴 main project 去建立一個 submodule

[autosun@:~/demo/Test-git]$ git submodule add https://github.com/autosun/autosun-toolset.git toolset

這時候我們打開產生的 .gitmodule 檔案就會看到 submodule 的資訊了。此外也可以看到 submodule 的 source code 被 sync 下來了。

[autosun@:~/demo/Test-git]$ cat .gitmodules
[submodule "toolset"]
    path = toolset
    url = https://github.com/autosun/autosun-toolset.git
[submodule "uva"]
    path = uva
    url = https://github.com/autosun/Uva-code.git

更新 submodule

當 submodule 有更新版的時候,我們需要到各個 submodule 目錄下 git pull 去拉最新的版本,或者使用 submodule foreach。

$ git submodule foreach --recursive git pull origin master

Clone 帶有 submodule 的 repository

當要去 Clone 一個帶有 submodule 的 repository 的時候,你會發現下了 git clone 之後雖然有 submodule 的目錄但是是空的,這時候還需要 submodule init 指令與 submodule update 指令去 sync。

[autosun@:~/demo]$ git clone git@github.com:autosun/Submodule-test.git
[autosun@:~/demo/Submodule-test]$ git submodule init
[autosun@:~/demo/Submodule-test]$ git submodule update --recursive

或是在 clone 的時候多帶 --recursive 參數才會一併 clone submodule。

[autosun@:~/demo]$ git clone --recursive git@github.com:autosun/Submodule-test.git

缺點

  1. Clone 時不方便
  2. 不便於切換 submodule 的分支

Git subtree

Subtree 是比較新的作法,有個文章寫的滿詳細的這邊借用了他的例子,你可以在這裡 Christophe Porteneuve's blog - Mastering GIt subtrees. 找到範例並下載

首先為了方便日後操作,我加入遠端 repository

[git-subtree/main]$ git remote add plugin ../remotes/plugin

初始化 subtree

初始化 subtree 跟 submodule 一樣簡單,透過 submodule add 指令即可

[git-subtree/main]$ git subtree add --prefix=vendor/plugins/demo plugin HEAD
git fetch plugin master
warning: no common commits
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 11 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (11/11), done.
From ../remotes/plugin
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> plugin/master
Added dir 'vendor/plugins/demo'

然後就可以從 git log 看到 sub project 的原始碼變成 main project 的一部分

[git-subtree/main]$ git log --oneline --graph --decorate
*   32e539d (HEAD, master) Add 'vendor/plugins/demo/' from…
|\
| * fe64799 (plugin/master) Fix repo name for main project…
| * 89d24ad Main files (incl. subdir) for plugin, to populate its…
| * cc88751 Initial commit
* b90985a (origin/master) Main files for the project, to populate…
* e052943 Initial import

如果你不想保留原有 sub project 的 commit 紀錄,可以在 add 的時候加上 squash 參數

[git-subtree/main]$ git subtree add --prefix=vendor/plugins/demo --squash plugin master
git fetch plugin master
From ../remotes/plugin
 * branch            master     -> FETCH_HEAD
Added dir 'vendor/plugins/demo'

就可以看到所有 commit 被融合成一份

[git-subtree/main]$ git log --oneline --graph --decorate
*   352af7a (HEAD, master) Merge commit '03e04026fdba2ff1200a226c3…
|\
| * 03e0402 Squashed 'vendor/plugins/demo/' content from commit…
* b90985a (origin/master) Main files for the project, to populate…
* e052943 Initial import

更新 subtree

當 remote subtree  repository 有更新的時候,可以使用 subtree pull 指令來更新,其中 --squash 是可選項目

[git-subtree/main]$ git subtree pull --prefix=vendor/plugins/demo --squash plugin master

你也可以貢獻 Subtree 的專案,透過 subtree push 指令就可以達成,不過在我的經驗裡,這麼做很容易造成 subtree 的 conflict,建議回到 subtree 原有的 repository 去修改提交。

[git-subtree/main]$ git subtree push -P vendor/plugins/demo plugin master

變更 subtree 的分支

變更 subtree 的分支也相當不方便,必須刪除現有的然後重新加入

$ git rm subtree_path
$ git commit
$ git subtree add --prefix=subtree_path repository_url branch_name

優點

  1. Clone 方便
  2. 不需要額外的 meta data (即 .gitmodule)
  3. Subtree 的 commit 紀錄可以直接看到

 

缺點

  1. Commit 紀錄不易閱讀
  2. 容易忘記進行 subtree pull
  3. subtree 切換分支不便利

 

Google repo

Google repo 是 Google 開發的 python script,Android 開放原始碼計畫(AOSP)也是使用它進行管理,開始之前必須先下載並安裝 repo 指令

$ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
$ chmod a+x ~/bin/repo

撰寫 repo script

Google repo 是依照 XML 檔的描述來拉取 sub project 的 repositories,範例描述如下,這裡描述了一個叫做 github 的 remote,並且使用了兩個 projects,一個是 autosun-toolset repository,拉取放到 tool 資料夾,另一個是 Uva-code repository 放到 uva 資料夾。寫好該 XML 檔需要將之放於某 repository 上,這裡我將放到 Test-git repository 裡。



    
    
    
    

初始化 project

之後我們找個地方,把所有需要的 repositoies 都 sync 下來,使用方法先下 repo init 進行初始化,要注意的是 Test-git repository 需要有前步驟寫好的 default.xml 檔

[autosun@:~/demo]$ repo init -u https://github.com/autosun/Test-git.git -m default.xml
warning: gpg (GnuPG) is not available.
warning: Installing it is strongly encouraged.
Get https://gerrit.googlesource.com/git-repo/clone.bundle
Get https://github.com/autosun/Test-git.git
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (22) The requested URL returned error: 404 Not Found
Server does not provide clone.bundle; ignoring.
remote: Counting objects: 22, done.       
remote: Compressing objects: 100% (3/3), done.       
remote: Total 22 (delta 0), reused 0 (delta 0), pack-reused 19       
From https://github.com/autosun/Test-git
 * [new branch]      master     -> origin/master
Your identity is: autosun
If you want to change this, please re-run 'repo init' with --config-name
repo has been initialized in /Users/autosun/demo

完成之後會看到目錄底下有個 .repo 目錄但是還沒有程式碼,需要下 repo sync 將程式碼通通拉下來。

[autosun@:~/demo]$ repo sync
Fetching project autosun/Uva-code.gitFetching project autosun/autosun-toolset.git
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0curl: (22) The requested URL returned error: 404 Not Found
Server does not provide clone.bundle; ignoring.
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0curl: (22) The requested URL returned error: 404 Not Found
Server does not provide clone.bundle; ignoring.
remote: Counting objects: 87, done.       
remote: Counting objects: 110, done.       
remote: Compressing objects: 100% (5/5), done.       
remote: Total 110 (delta 0), reused 0 (delta 0), pack-reused 105       
Receiving objects: 100% (110/110), 22.91 KiB | 0 bytes/s, done.
Resolving deltas: 100% (7/7), done.
From https://github.com/autosun/Uva-code
 * [new branch]      master     -> github/master
Fetching projects:  50% (1/2)  remote: Total 87 (delta 0), reused 0 (delta 0), pack-reused 87       
From https://github.com/autosun/autosun-toolset
 * [new branch]      master     -> github/master
Fetching projects: 100% (2/2), done.

更新 project

更新 project 也是直接使用 repo sync 指令即可

變更 sub project 分支

更改 XML 檔並且下 repo sync 即可


優點

  1. 易於更改分支
  2. 可搭配 gerrit 做 code review 系統

 

缺點

  1. 不能在 main project 看到 sub project 的 Commit 紀錄

 

Reference

Git 工具 - 子模組 (Submodules)
https://git-scm.com/book/zh-tw/v1/Git-工具-子模組-Submodules

Git Submodule 用法筆記
http://blog.chh.tw/posts/git-submodule/

The problem with Git submodules
http://ayende.com/blog/4746/the-problem-with-git-submodules

Git SubTree 共編 Library
http://yutin.logdown.com/posts/188306-git-subtree-total-addendum-library

Alternatives To Git Submodule: Git Subtree
http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/

Mastering Git subtrees
https://medium.com/@porteneuve/mastering-git-subtrees-943d29a798ec

Using Google's repo command in your own projects
http://www.instructables.com/id/Using-Googles-repo-command-in-your-own-projects/

Repo command reference
https://source.android.com/source/using-repo.html

by autosun

回應
    沒有新回應!
關鍵字
共用引入檔