何時須要 Subtree ?
一、當多個項目共用同一坨代碼,而這坨代碼跟着項目在快速更新的時候
二、把一部分代碼遷移出去獨立爲一個新的 git 倉庫,但又但願可以保留這部分代碼的歷史提交記錄。php
有贊微商城曾經是一個很大的先後端代碼都包含在裏面的 Git 項目,爲了方便管理咱們把先後端代碼分離成2個 Git 倉庫,進而再做分項目拆分紅多個Git 倉庫。node
因而,就須要有好的方式同步各個項目共用的Css庫、JS庫、PHP庫(他們都是以獨立的 Git 倉庫的形式存在)。並且因爲開發節奏極快,咱們須要這些庫是能夠在不一樣項目間雙向同步的而不是單向同步。並且,最好能作到被遷移的這部分代碼在新的git倉庫裏保留原有的歷史提交記錄。git
舉個栗子:A項目須要在給某個子項目W裏添加一個文件,最方便的方式天然是直接在A項目裏改W子項目對應的目錄裏的代碼,而後測試經過後,把這個更改提交到W子項目的 Git倉庫裏。若是這時候還要先單獨更新W子項目的代碼而後提交到 Git 服務器,再在A項目裏把W子項目的代碼更新過來,顯然是很麻煩的,更麻煩的是若是發現代碼有bug,還得再走一遍這個流程。github
Git Submodule:這是Git官方之前的推薦方案shell
Git Subtree:從 Git 1.5.2 開始,Git 新增並推薦使用這個功能來管理子項目npm
npm:node package manager,實際上不只僅是 node 的包管理工具segmentfault
composer:暫且認爲他是php版npm、php版Maven吧後端
雖然 npm、composer、maven 等更側重於包的依賴管理,以上幾個方案都是可以作到在不一樣項目中同步同一塊代碼的,但無法雙向同步,更適用於子項目代碼比較穩定的情形。服務器
Git Submodule 和 Git Subtree 都是官方支持的功能,不具備依賴管理的功能,但能知足咱們的要求。Git Subtree相對來講會更好一些 。composer
用一句話來描述 Git Subtree 的優點就是:
經由 Git Subtree 來維護的子項目代碼,對於父項目來講是透明的,全部的開發人員看到的就是一個普通的目錄,原來怎麼作如今依舊那麼作,只須要維護這個 Subtree 的人在合適的時候去作同步代碼的操做。
它是怎麼作到的呢?簡單說下原理
首先,你有兩個偉大的項目——咱們叫他P1項目、P2項目,還有一個牛逼的要被多個項目共用的項目——咱們叫他S項目。咱們經過簡要講解使用Subtree來同步代碼的過程來解釋Subtree的原理
經過
cd P1項目的路徑 git subtree add --prefix=用來放S項目的相對路徑 S項目git地址 xxx分支
這樣的命令,把S項目(咱們姑且叫他S項目)的代碼下載到--prefix所指定的目錄——咱們姑且叫他S目錄把,並在P1項目裏自動產生一個commit(就是把S目錄的內容提交到P1項目裏)。
對於P2項目也作一樣的操做
你們在P1項目裏各類提交commit,其中有些commit會涉及到S目錄的更改,正如前面提到的,這是沒任何關係的,你們也不會感覺到有任何不同。
關鍵的地方來了:
當維護這個S項目 Subtree 的人但願把最近這段時間對S目錄的更改提交到S項目的 Git 服務器上時,他執行一段相似於這樣的命令:
cd P1項目的路徑 git subtree push --prefix=S項目的路徑 S項目git地址 xxx分支
Git 會遍歷全部的commit,從中找出針對S目錄的更改,而後把這些更改記錄提交到S項目的Git服務器上
OK,如今S項目有大量的新代碼了,P2項目也想使用這些新代碼,維護P2這個Subtree的人只要執行:
git subtree pull --prefix=S項目的路徑 S項目git地址 xxx分支
這樣就能夠將P2項目裏S項目目錄裏的內容更新爲S項目xxx分支的最新代碼了。
假設,你要在各個項目裏的components/zenjs這個目錄對 http://github.com/youzan/zenjs.git 這個項目作Subtree
1.首先必須確保各個項目已經添加zenjs 這個 remote(關於remote是什麼能夠看這裏):
git remote add zenjs http://github.com/youzan/zenjs.git
2.將zenjs添加到各個項目裏
git subtree add --prefix=components/zenjs zenjs master
3.各項目更新zenjs代碼的方法:
git subtree pull --prefix=components/zenjs zenjs master
4.各項目提交zenjs代碼的方法:
git subtree push --prefix=components/zenjs zenjs hotfix/zenjs_xxxx
這會在遠程的zenjs的倉庫裏生成一個叫 hotfix/zenjs_xxxx 的的分支,包含了你過去對components/zenjs 全部的更改記錄
5.把hotfix/zenjs_xxx分支更新併合併到master並提交
這樣其餘工程就能夠更新到你提交的代碼了。
有人可能會問,只用master分支,無論版本,太有風險了。
對的,正如咱們前面說到的那樣,subtree的方案適用的場景是:各個項目共用一個庫,而這個庫正在快速迭代更新的過程當中。若是追求穩定,只須要給庫拉出一個如v0.1.0這樣的版本號命名的穩定分支,subtree只用這個分支便可。
咱們如今使用的方式就是:A項目常常會對zenjs作更新,因此A項目用subtree來雙向同步;B項目只是使用,因此用bower用來按版原本更新代碼。
從新split出一個新起點(這樣,每次提交subtree的時候就不會從頭遍歷一遍了)
git subtree split --rejoin --prefix=components/zenjs --branch new_zenjs git push zenjs new_zenjs:master
本文首發於個人
我的技術博客:http://delai.me/code/git-subtree/
SegmentFault專欄:http://segmentfault.com/a/1190000003969060
轉載請註明出處
2016年3月3日,update,補充 subtree 的使用場景