Git Submodule 的常見錯誤

此篇文章最近更新時間為2011-03-01 08:08:21 目前共有5篇留言

關於作者 - JosephJ

任職於 Faria。喜好戶外運動、2008 年 5 月完成「跑步環島」。對於新技術跟程式碼有著強烈的偏執狂。

最近常發現改好的 Bug、過了沒多久又壞掉了。首先當然是去檢查程式碼,發現:「咦,怎麼又改回舊的?莫非我沒 Commit??」,再去用 git log -5 去看此檔案最近五筆 Commit 才發現是同事幹的好事!原來我們同事感情如此的不好?非也非也,其實是我們大量使用 Git Submodule 來分工,另外加上大家 Commit 習慣的不良所導致。

Git Submodule 讓我們分工容易

簡單列一下我們的 Submodule 目錄結構:

miiicasa.git
|-- apps.git
|     |-- modules/allpolarize.git
|     |-- modules/email.git
|     |-- modules/facebook.git
|     |-- modules/photofun.git
|     `-- modules/twitter.git
`-- static/
      `-- platform.git

miiicasa.git 是我們所有內部團隊改程式的地方。掛在下面的第一個 Submodule、apps.git 是我們切出去給外包團隊專用的,他們也依照不同的應用再在 apps.git 下掛了許多的 Submodules。另外掛在 miiicasa.git 的還有 static/platform.git,這是我放在 github 上的 JavaScript Module Platform

所以誰可以改那一個 Submodule 是很清楚的一件事,理論上不會也不應該被其他人修改:

  • miiicasa.git:內部團隊的 8 個 RD。
  • apps.git 及以下的 5 個 Submodules:外包團隊的 6 個 RD。
  • static/platform.git :我。

Submodule 的原理

當 Parent Git 掛上一個 Submodule,Parent Git 不負責記錄 Submodule 所有的檔案變動,僅僅記錄一行的 Commit ID,如下:

[~/miiicasa] $ git submodule
 e34f1acb03d8cd98cd5f0f9a5e6275ea8aa14b91 apps (remotes/origin/HEAD)
 317fb010fb27222609403fa9c55c8f34bdf5d864 static/platform (remotes/origin/HEAD)

一但 Parent Git 記錄的 Commit ID 與 Child Git 的 Commit ID 不同時(另外還有修改或新增但未 Commit 時)就會出現一個 + 號在前面,通常發生在每次的 git pull。

[~/miiicasa] $ git submodule
+e34f1acb03d8cd98cd5f0f9a5e6275ea8aa14b91 apps (remotes/origin/HEAD)
+317fb010fb27222609403fa9c55c8f34bdf5d864 static/platform (remotes/origin/HEAD)

目前 + 是 Parent Git 所拿到的最新 Commit ID,此時最好是搭配 git status 來看一下情況:

[~/miiicasa] $ git status
# Changed but not updated:
#    (use "git add ..." to update what will be committed)
#    (use "git checkout -- ..." to discard changes in working directory)
#
#          modified:    apps (new commits)
#          modified:    static/platform (new commits)
#
no changes to added to commit (use "git add" and/or "git commit -a")

這樣理論上代表了我有更新 static/platform.git外包團隊有更新 apps.git,你只要輸入 git submodule update 就可以將 Submodule 的檔案更新到 Parent Git 所記錄的 Commit ID

開發人員必須永遠心存懷疑!

在下 git submodule update 前請先三思:「真的有這麼巧、在一次的 push/pull 間就有兩個 Submodule 被我跟外包團隊更新了嗎?。」建議此時用 git log -5 的指令來看一下外包團隊所負責 apps.git 最近 5 筆的變動:

[~/miiicasa] $ git log -5 apps
commit 49e629af891f4255c184aeb3ecee1a1255fff21e
Author: miiiCasa RD 1 
Date:   Tue Mar 1 11:33:15 2011 +0800

    change theme key

commit 1470f6b1829542be800f695f01f10b918a6fa58f
Author: Outsource RD 1 
Date:   Wed Feb 23 18:44:54 2011 +0800

    sync submodule to latest(1.0.1) again, with fixing Puzzle for selecting image from space

commit 8aaac9595f239fc66d3272ea2326dbf79b8683f7
Author: miiiCasa RD 2 
Date:   Tue Feb 22 17:02:15 2011 +0800

    commit

commit f223e5614381883e560321d644abb96bc5baffb4
Author: miiiCasa RD 2 
Date:   Wed Feb 16 16:59:37 2011 +0800

    Re-generated lang files.

commit cfbdfc743a0968b807b34b367d4297004281e9f1
Author: Outsource RD 1 
Date:   Wed Feb 16 13:58:47 2011 +0800

    sync submodule for appupdate-1.0.1 3

發現很有趣的事實:「5 個 Commit 居然只有 2 個是外包團隊所下的,其他 3個都是我們自己的 RD 下的」,原來大家這麼熱心幫外包團隊解決問題?但事實上是第 1, 3, 4 次的 Commit 都被還原成之前的版本了 :p 此時有修好的 Bug 被 Reopen 也不意外。如果你在不了解的情況下做了 git submodule update 更新回舊的檔案,那麼你也算是幫凶啊 XDD

禍害無窮的快速指令

還是那個「」字所導致。如果工程師的習慣不好,常見的像是:改了一大堆不相關的東西才做一次 Commit、或者是省略了一些重要步驟,直接下 git add . 或 git commit -am "註解",就 push 出去了。

拿上面的問題來說,其實是我們的工程師是為了修一個佈景主題的 Cookie 問題,改動到的檔案較多,為了節省時間使用了 git commit -am "註解"。另外加上他在前一次的 git pull 後沒有用 git submodule update 將 Submodule Commit ID 不一致的問題解決掉。所以這一個 git commit -am 一執行時就會把錯誤的 Submodule Commit ID 給寫入 Repository、push 出去就會影響到所有人了... 當初解掉 Bug 的人一定會唉唉叫的 :p

建議的好習慣

必須承認我自己在一開始用版本控制系統時也很懶惰,能少打幾個指令就少打。但是用久了深深了解到註解不好好寫、Commit 不確實,都會對自己或其他團隊成員造成困擾。所以我現在每次 Commit 前都有以下的流程:

  1. git diff 看所有的變動,了解自己改了哪些東西、替自己做個 Code Review、有個印象應該跟那些檔案一起 Commit。
  2. git status 看異動的檔案列表,看看是不是有 Submodule 還沒被 update、看看是不是有檔案還沒納入。
  3. git add file1 file2 file3 ... fileN :避免用 git add . 或 git commit -am 偷吃步,列表都在剛剛的 git status 中,複製貼上很快的,也可以避免有不相關的修改在一個 Commit 中。
  4. git commit -m "註解":註解寫的越完善越好(像我這樣也是在偷吃步,之後連 -m 寫 Inline 的註解也應該要避免掉才是,進到 Editor 中用多行寫清楚才是王道啊。)如果跟 Ticket 有相關的,要在行首加個 [Bug 1232131] 標明!
  5. 若有修改 Submodule 的程式,一定將他單獨 commit。

另外每次在 git pull 後,都應該檢查有沒有 Submodule 的更新,若有則立刻下 git submodule update。

犯錯了如何補救

剛剛提到 apps.git 的例子,如果我們想要回復到上一個版本,可以先找到正確的 Commit ID,例如:

[~/miiicasa] $ git log -2 apps
commit 49e629af891f4255c184aeb3ecee1a1255fff21e
Author: miiiCasa RD 1 
Date:   Tue Mar 1 11:33:15 2011 +0800

    change theme key

commit 1470f6b1829542be800f695f01f10b918a6fa58f
Author: Outsource RD 1 
Date:   Wed Feb 23 18:44:54 2011 +0800

    sync submodule to latest(1.0.1) again, with fixing Puzzle for selecting image from space

其中 1470f6b1829542be800f695f01f10b918a6fa58f 這個 Commit ID 就是我們想回復的,你可以用下列指令達成:

[~/miiicasa] $ git checkout 1470f6b1829542be800f695f01f10b918a6fa58f apps

當然你還得再度 Commit 此更新、接著 Push 回 Central Repository、其他人才能一同更新。

另一個常見的問題:忘記更新 Submodule 的 origin。

常碰到的另外一個情況,我們常常會碰到 git submodule update 無法更新的情況。這樣的情況通常發生在:例如負責更新 static/platform.git(origin 在 github)的我,在 static/platform 修改了東西,且在 Child Git 與 Parent Git 都做了 git commit,也在 Parent Git 做了 git push 的動作,可是我唯獨在 Child Git 忘記了 git push 的動作,此時其他人拿到的 Submodule Commit ID 雖然是正確的,但是因為我的檔案並沒有更新到 github 去,別人在以新的 Commit ID 跟 github 做請求時,自然也會出現無法對應而 Fail 的情況啦 :p

版本控制也是良心事業

之前常會說寫 Code 是個良心事業(包含註解、考慮效能或優使性),但版本控制何嘗不是呢?有經驗的人一看就知道你的 Commit 習慣好不好,註解有沒有寫清楚,包含的檔案是否相關。寫得好無形之中對你的聲譽是一種累積、可以減少困擾。鼓勵大家盡量有耐心,採用正確的步驟、並將 Note 撰寫完善 :)



Comments

  1. Drake 2011-07-06 16:18:32
    submodule 實在是個很複雜的玩意兒吶~~感覺應該根據大家使用的行為,包幾個 command 出來用,簡化指令,減少出錯,就像 git-flow 那樣,我想這樣會親民多一些 :D
  2. sj 2011-03-07 08:57:18
    也有 git add -i 這個參數可以用選的唷
  3. Caesar 2011-03-02 02:20:38
    版本控制也是良心事業。
    commit 是寫給自己看,很多時候回顧之前的資料才會發現,每次多花一點時間,多留一點紀錄,對自己的幫助有多大。
    感謝,從當中體會不少。
  4. 虎哥 2011-03-02 01:07:16
    非常同意習慣是培養出來的,但是若能培養 "好" 習慣步步照著來,就不易出錯。
    在每次 commit 程序每一步都謹慎,確定自己改動的東西,也較不易出錯了。
    推!
  5. 馬小白 2011-03-01 23:59:48
    ﹝咦,怎麼又改回舊的?莫非我沒 Commit﹞一針見血!!因為我今天也跳之前挖的洞,留下來在想要怎麼補回來...之前因為時間不夠加上頁面少又是by module去寫case,要拆case出來測試就直接copy paste delete就搞定了!!加上少下一些tag,現在Page和CR越來越多,要拆case出來測試就是一整個殺時間的工作,而且今天拆出來的還只能用一次...明天正打算回頭全部一次把tag補完 QQ一勞永逸呀...T_T
暱稱: 必填。
Email: 非必填。若填寫為不公開欄位,僅供站長參考聯繫。
內容: 必填。限 255 個字元以內。
驗證碼:
送出

Facebook Comment