Git 子模塊

在一個項目中使用另一個項目,兩個項目分別有各自的倉庫以及各自的維護周期

添加外部項目為子模塊

git submodule add 倉庫地址

文檔參考

.6 Git 工具 - 子模塊
子模塊

經(jīng)常有這樣的事情寞蚌,當(dāng)你在一個項目上工作時站削,你需要在其中使用另外一個項目瞬矩。也許它是一個第三方開發(fā)的庫或者是你獨立開發(fā)和并在多個父項目中使用的姑廉。這個場景下一個常見的問題產(chǎn)生了:你想將兩個項目單獨處理但是又需要在其中一個中使用另外一個饭望。

這里有一個例子塑娇。假設(shè)你在開發(fā)一個網(wǎng)站澈侠,為之創(chuàng)建Atom源。你不想編寫一個自己的Atom生成代碼埋酬,而是決定使用一個庫哨啃。你可能不得不像CPAN install或者Ruby gem一樣包含來自共享庫的代碼,或者將代碼拷貝到你的項目樹中写妥。如果采用包含庫的辦法拳球,那么不管用什么辦法都很難去定制這個庫,部署它就更加困難了珍特,因為你必須確保每個客戶都擁有那個庫祝峻。把代碼包含到你自己的項目中帶來的問題是,當(dāng)上游被修改時,任何你進行的定制化的修改都很難歸并莱找。

Git 通過子模塊處理這個問題酬姆。子模塊允許你將一個 Git 倉庫當(dāng)作另外一個Git倉庫的子目錄。這允許你克隆另外一個倉庫到你的項目中并且保持你的提交相對獨立奥溺。

子模塊初步

假設(shè)你想把 Rack 庫(一個 Ruby 的 web 服務(wù)器網(wǎng)關(guān)接口)加入到你的項目中辞色,可能既要保持你自己的變更,又要延續(xù)上游的變更浮定。首先你要把外部的倉庫克隆到你的子目錄中相满。你通過git submodule add將外部項目加為子模塊:

$ git submodule add git://github.com/chneukirchen/rack.git rack
Initialized empty Git repository in /opt/subtest/rack/.git/
remote: Counting objects: 3181, done.
remote: Compressing objects: 100% (1534/1534), done.
remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done.
Resolving deltas: 100% (1951/1951), done.

現(xiàn)在你就在項目里的rack子目錄下有了一個 Rack 項目。你可以進入那個子目錄桦卒,進行變更立美,加入你自己的遠程可寫倉庫來推送你的變更,從原始倉庫拉取和歸并等等闸盔。如果你在加入子模塊后立刻運行git status悯辙,你會看到下面兩項:

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

首先你注意到有一個.gitmodules文件琳省。這是一個配置文件迎吵,保存了項目 URL 和你拉取到的本地子目錄

$ cat .gitmodules
[submodule "rack"]
      path = rack
      url = git://github.com/chneukirchen/rack.git

如果你有多個子模塊,這個文件里會有多個條目针贬。很重要的一點是這個文件跟其他文件一樣也是處于版本控制之下的击费,就像你的.gitignore文件一樣。它跟項目里的其他文件一樣可以被推送和拉取桦他。這是其他克隆此項目的人獲知子模塊項目來源的途徑蔫巩。

git status的輸出里所列的另一項目是 rack 。如果你運行在那上面運行git diff快压,會發(fā)現(xiàn)一些有趣的東西:

$ git diff --cached rack
diff --git a/rack b/rack
new file mode 160000
index 0000000..08d709f
--- /dev/null
+++ b/rack
@@ -0,0 +1 @@
+Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433

盡管rack是你工作目錄里的子目錄圆仔,但 Git 把它視作一個子模塊,當(dāng)你不在那個目錄里時并不記錄它的內(nèi)容蔫劣。取而代之的是坪郭,Git 將它記錄成來自那個倉庫的一個特殊的提交。當(dāng)你在那個子目錄里修改并提交時脉幢,子項目會通知那里的 HEAD 已經(jīng)發(fā)生變更并記錄你當(dāng)前正在工作的那個提交歪沃;通過那樣的方法,當(dāng)其他人克隆此項目嫌松,他們可以重新創(chuàng)建一致的環(huán)境沪曙。

這是關(guān)于子模塊的重要一點:你記錄他們當(dāng)前確切所處的提交。你不能記錄一個子模塊的master或者其他的符號引用萎羔。

當(dāng)你提交時液走,會看到類似下面的:

$ git commit -m 'first commit with submodule rack'
[master 0550271] first commit with submodule rack
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 rack

注意 rack 條目的 160000 模式。這在Git中是一個特殊模式,基本意思是你將一個提交記錄為一個目錄項而不是子目錄或者文件缘眶。

你可以將rack目錄當(dāng)作一個獨立的項目腻窒,保持一個指向子目錄的最新提交的指針然后反復(fù)地更新上層項目。所有的Git命令都在兩個子目錄里獨立工作:

$ git log -1
commit 0550271328a0038865aad6331e620cd7238601bb
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Apr 9 09:03:56 2009 -0700

    first commit with submodule rack
$ cd rack/
$ git log -1
commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
Author: Christian Neukirchen <chneukirchen@gmail.com>
Date:   Wed Mar 25 14:49:04 2009 +0100

    Document version change

[克隆一個帶子模塊的項目]

這里你將克隆一個帶子模塊的項目磅崭。當(dāng)你接收到這樣一個項目儿子,你將得到了包含子項目的目錄,但里面沒有文件:

$ git clone git://github.com/schacon/myproject.git
Initialized empty Git repository in /opt/myproject/.git/
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (6/6), done.
$ cd myproject
$ ls -l
total 8
-rw-r--r--  1 schacon  admin   3 Apr  9 09:11 README
drwxr-xr-x  2 schacon  admin  68 Apr  9 09:11 rack
$ ls rack/
$

rack目錄存在了砸喻,但是是空的柔逼。你必須運行兩個命令:git submodule init來初始化你的本地配置文件,git submodule update來從那個項目拉取所有數(shù)據(jù)并檢出你上層項目里所列的合適的提交:

$ git submodule init
Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack'
$ git submodule update
Initialized empty Git repository in /opt/myproject/rack/.git/
remote: Counting objects: 3181, done.
remote: Compressing objects: 100% (1534/1534), done.
remote: Total 3181 (delta 1951), reused 2623 (delta 1603)
Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done.
Resolving deltas: 100% (1951/1951), done.
Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433'

現(xiàn)在你的rack子目錄就處于你先前提交的確切狀態(tài)了割岛。如果另外一個開發(fā)者變更了 rack 的代碼并提交愉适,你拉取那個引用然后歸并之,將得到稍有點怪異的東西:

$ git merge origin/master
Updating 0550271..85a3eee
Fast forward
 rack |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
[master*]$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#      modified:   rack
#

你歸并來的僅僅上是一個指向你的子模塊的指針癣漆;但是它并不更新你子模塊目錄里的代碼维咸,所以看起來你的工作目錄處于一個臨時狀態(tài):

$ git diff
diff --git a/rack b/rack
index 6c5e70b..08d709f 160000
--- a/rack
+++ b/rack
@@ -1 +1 @@
-Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
+Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433

事情就是這樣,因為你所擁有的指向子模塊的指針和子模塊目錄的真實狀態(tài)并不匹配惠爽。為了修復(fù)這一點癌蓖,你必須再次運行git submodule update

$ git submodule update
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 2 (delta 0)
Unpacking objects: 100% (3/3), done.
From git@github.com:schacon/rack
   08d709f..6c5e70b  master     -> origin/master
Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0'

每次你從主項目中拉取一個子模塊的變更都必須這樣做』樗粒看起來很怪但是管用租副。

一個常見問題是當(dāng)開發(fā)者對子模塊做了一個本地的變更但是并沒有推送到公共服務(wù)器。然后他們提交了一個指向那個非公開狀態(tài)的指針然后推送上層項目较性。當(dāng)其他開發(fā)者試圖運行git submodule update用僧,那個子模塊系統(tǒng)會找不到所引用的提交,因為它只存在于第一個開發(fā)者的系統(tǒng)中赞咙。如果發(fā)生那種情況责循,你會看到類似這樣的錯誤:

$ git submodule update
fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack'

你不得不去查看誰最后變更了子模塊

$ git log -1 rack
commit 85a3eee996800fcfa91e2119372dd4172bf76678
Author: Scott Chacon <schacon@gmail.com>
Date:   Thu Apr 9 09:19:14 2009 -0700

    added a submodule reference I will never make public. hahahahaha!

然后,你給那個家伙發(fā)電子郵件說他一通攀操。

[上層項目]

有時候院仿,開發(fā)者想按照他們的分組獲取一個大項目的子目錄的子集。如果你是從 CVS 或者 Subversion 遷移過來的話這個很常見崔赌,在那些系統(tǒng)中你已經(jīng)定義了一個模塊或者子目錄的集合意蛀,而你想延續(xù)這種類型的工作流程。

在 Git 中實現(xiàn)這個的一個好辦法是你將每一個子目錄都做成獨立的 Git 倉庫健芭,然后創(chuàng)建一個上層項目的 Git 倉庫包含多個子模塊县钥。這個辦法的一個優(yōu)勢是你可以在上層項目中通過標(biāo)簽和分支更為明確地定義項目之間的關(guān)系。

[子模塊的問題]

使用子模塊并非沒有任何缺點慈迈。首先若贮,你在子模塊目錄中工作時必須相對小心省有。當(dāng)你運行git submodule update,它會檢出項目的指定版本谴麦,但是不在分支內(nèi)蠢沿。這叫做獲得一個分離的頭——這意味著 HEAD 文件直接指向一次提交,而不是一個符號引用匾效。問題在于你通常并不想在一個分離的頭的環(huán)境下工作舷蟀,因為太容易丟失變更了。如果你先執(zhí)行了一次submodule update面哼,然后在那個子模塊目錄里不創(chuàng)建分支就進行提交野宜,然后再次從上層項目里運行git submodule update同時不進行提交,Git會毫無提示地覆蓋你的變更魔策。技術(shù)上講你不會丟失工作匈子,但是你將失去指向它的分支,因此會很難取到闯袒。

為了避免這個問題虎敦,當(dāng)你在子模塊目錄里工作時應(yīng)使用git checkout -b work創(chuàng)建一個分支。當(dāng)你再次在子模塊里更新的時候政敢,它仍然會覆蓋你的工作其徙,但是至少你擁有一個可以回溯的指針。

切換帶有子模塊的分支同樣也很有技巧堕仔。如果你創(chuàng)建一個新的分支擂橘,增加了一個子模塊,然后切換回不帶該子模塊的分支摩骨,你仍然會擁有一個未被追蹤的子模塊的目錄

$ git checkout -b rack
Switched to a new branch "rack"
$ git submodule add git@github.com:schacon/rack.git rack
Initialized empty Git repository in /opt/myproj/rack/.git/
...
Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
$ git commit -am 'added rack submodule'
[rack cc49a69] added rack submodule
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 rack
$ git checkout master
Switched to branch "master"
$ git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#      rack/

你將不得不將它移走或者刪除,這樣的話當(dāng)你切換回去的時候必須重新克隆它——你可能會丟失你未推送的本地的變更或分支朗若。

最后一個需要引起注意的是關(guān)于從子目錄切換到子模塊的恼五。如果你已經(jīng)跟蹤了你項目中的一些文件但是想把它們移到子模塊去,你必須非常小心哭懈,否則Git會生你的氣灾馒。假設(shè)你的項目中有一個子目錄里放了 rack 的文件,然后你想將它轉(zhuǎn)換為子模塊遣总。如果你刪除子目錄然后運行submodule add睬罗,Git會向你大吼:

$ rm -Rf rack/
$ git submodule add git@github.com:schacon/rack.git rack
'rack' already exists in the index

你必須先將rack目錄撤回。然后你才能加入子模塊:

$ git rm -r rack
$ git submodule add git@github.com:schacon/rack.git rack
Initialized empty Git repository in /opt/testsub/rack/.git/
remote: Counting objects: 3184, done.
remote: Compressing objects: 100% (1465/1465), done.
remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.

現(xiàn)在假設(shè)你在一個分支里那樣做了旭斥。如果你嘗試切換回一個仍然在目錄里保留那些文件而不是子模塊的分支時——你會得到下面的錯誤:

$ git checkout master
error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge.

你必須先移除rack子模塊的目錄才能切換到不包含它的分支:

$ mv rack /tmp/
$ git checkout master
Switched to branch "master"
$ ls
README  rack

然后容达,當(dāng)你切換回來,你會得到一個空的rack目錄垂券。你可以運行git submodule update重新克隆花盐,也可以將/tmp/rack目錄重新移回空目錄。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市算芯,隨后出現(xiàn)的幾起案子柒昏,更是在濱河造成了極大的恐慌,老刑警劉巖熙揍,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件职祷,死亡現(xiàn)場離奇詭異,居然都是意外死亡届囚,警方通過查閱死者的電腦和手機堪旧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奖亚,“玉大人淳梦,你說我怎么就攤上這事∥糇郑” “怎么了爆袍?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長作郭。 經(jīng)常有香客問我陨囊,道長,這世上最難降的妖魔是什么夹攒? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任蜘醋,我火速辦了婚禮,結(jié)果婚禮上咏尝,老公的妹妹穿的比我還像新娘压语。我一直安慰自己,他們只是感情好编检,可當(dāng)我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布胎食。 她就那樣靜靜地躺著,像睡著了一般允懂。 火紅的嫁衣襯著肌膚如雪厕怜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天蕾总,我揣著相機與錄音粥航,去河邊找鬼。 笑死生百,一個胖子當(dāng)著我的面吹牛递雀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播置侍,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼映之,長吁一口氣:“原來是場噩夢啊……” “哼拦焚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起杠输,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤赎败,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蠢甲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體僵刮,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年鹦牛,在試婚紗的時候發(fā)現(xiàn)自己被綠了搞糕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡曼追,死狀恐怖窍仰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情礼殊,我是刑警寧澤驹吮,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站晶伦,受9級特大地震影響碟狞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜婚陪,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一族沃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泌参,春花似錦脆淹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锯玛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間兼蜈,已是汗流浹背攘残。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留为狸,地道東北人歼郭。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像辐棒,于是被迫代替她去往敵國和親病曾。 傳聞我的和親對象是個殘疾皇子牍蜂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 做為一名程序員,大家對 ** Git ** 肯定都不陌生泰涂,最近項目中用到了 Git 子模塊鲫竞,今天,給大家講一下 *...
    rangel閱讀 2,017評論 5 6
  • git子模塊的使用 git子模塊可以用于項目包含另一個項目的情況逼蒙,也許是第三方庫或被多個項目引用的基礎(chǔ)框架从绘。此次學(xué)...
    看似平淡閱讀 3,028評論 0 0
  • Git子模塊(submodule)簡介 經(jīng)常有這樣的事情,當(dāng)你在一個項目上工作時是牢,你需要在其中使用另外一個項目僵井。也...
    孤逐王閱讀 9,277評論 0 2
  • 目前公司搭建了一個公共組件庫,想用于多個系統(tǒng)驳棱,就想到了使用 git 中的子模塊 submodule 在介紹子模塊前...
    皇冠黎閱讀 1,016評論 0 2
  • 其實今年的目標(biāo)是學(xué)做包包批什,就是看到一種包包,心里就非常明白這包是怎么做的這種程度社搅。 我首先訂了個目標(biāo)驻债,每個月學(xué)會兩...
    孤芳自賞手作閱讀 1,100評論 0 3