模擬情境
公司有 Sam 和 Jack 兩位開發者,用公司內部 Git 版控儲存庫進行開發。
簡述版控流程,開發者先在自己的分支開發,再將結果合併回 Develop
分支驗測功能,驗證無誤再透過 PR 程序合併至 master
分支上線。
公司內部版控儲存庫不提供任何管道給外部網路連線使用,但有開放 Webex 可遠端連線進去控制公司內部的電腦。
如下圖,假設公司在 commit e3251c8
時開始實施 Work From Home,從這裡開始就無法再連線至公司內的遠端儲存庫。
實施 Work From Home 後,Sam 開始修改本地 Develop_Sam
分支的 README.md
檔案內容,並且合併回本地的 Develop
分支。
同時,Jack 也在修改本地 Develop_Jack
分支的 README.md
檔案內容,也合併回本地的 Develop
分支。
前兩張圖可看到 Sam 與 Jack 在本地 Develop
分支的版控記錄 (Commit ID) 已經不一致了,雙方修改過的 README.md
檔案內容如下,之後若要合併肯定會發生衝突。
接下來 Sam 和 Jack 要同步彼此的開發進度,但是連不到公司的版控儲存庫了,此時有以下幾種選擇。
可用的方法
使用外部私有 Git 遠端儲存庫同步彼此的版控記錄 (公司沒有禁止的情況適用)
此做法無法解決上版至公司內部儲存庫的問題,只能解決開發者間的版控同步問題 (但光是解決這個問題,就可省下大半的時間了)
公司不一定允許 Source Code 放在外部的私有儲存庫
將 Source Code (包含
.git 目錄
) 通通打包傳給其他開發者或公司 (你瘋了嗎? 😆)- 每次交付的檔案都很大包,就算只改一個字,要上版都要整包傳輸,我實在想不到這個做法的優點…
用
git bundle
產生版控記錄檔 (★ 推薦)會產生一個容量很小的 binary 檔案利於傳輸,檔案大小取決於要包含的版控記錄多寡,裡面會有修改的檔案內容,以及 commit 版控記錄
本地端可透過
git pull
將記錄檔的版控內容 pull 合併至本地分支,並且保持開發的版控記錄,也可以送入公司同步版控記錄可把
git bundle
當成原本git push
的替代操作,只是變成離線版
用
git format-patch
產生內容修補檔 (只適合交付修補程式用,不適合開發過程同步版控用)開發者可套用修補檔以更新彼此修改過的檔案內容,也可送入公司上版,但是在開發過程中,每個人的版控記錄會不一致
修補檔記錄的是
git diff
的差異內容,檔案也不大,但不會保留版控記錄,套用修補檔時,背後是透過git commit
在處理,會產生新的版控記錄 (commit hash)版控記錄脫鉤,會造成查找問題或溝通上的困難,將來連回公司內部的版控儲存庫時,有可能要再面臨大量合併衝突的問題
git format-patch
只適合交付修補程式用,不適合開發過程同步版控用,若不打算同步版控記錄就沒差了
為了節省大家寶貴的時間,先說結論
如果公司禁止使用私有儲存庫
開發過程的版控同步
每位開發者的修改告一段落,就用git bundle
產生Develop
分支的版控記錄檔,交付給其他開發者同步Develop
分支的版控記錄功能開發完成,要回傳公司上版
用git bundle
產生Develop
分支的版控記錄檔,只是這次要傳進公司,遠端遙控公司內部電腦操作git pull
將版控記錄檔合併到Develop
分支,驗測無誤後再 PR 合併至master
分支
如果公司同意使用私有儲存庫 (★ 個人認為這是最佳狀況)
- 開發過程的版控同步
使用私有 Git 儲存庫進行開發,只有在這種非常時期才深刻體會到可以git push
是幸福的 😆
(安全性要記得做好,否則 Source Code 外流就……
會變成 Open Source😆 )- 功能開發完成,要回傳公司上版
用git bundle
產生Develop
分支的版控記錄檔,只是這次要傳進公司,遠端遙控公司內部電腦操作git pull
將版控記錄檔合併到Develop
分支,驗測無誤後再 PR 合併至master
分支
- 開發過程的版控同步
接下來說實作,有興趣的人請繼續看下去
實作方式
私有 Git 遠端儲存庫
這個做法就不說明了,有免費的、付費的、私有自架的…路很多條,任君挑選。
接下來,為了節省大家寶貴的時間,先說個人推薦的 git bundle
實作方式
用 git bundle 產生版控記錄檔 (★ 推薦)
先把時光回朔到公司開始實施 Work From Home,並且 Sam 和 Jack 都在自己的本地分支修改過檔案的狀態
先從 Sam 的角度開始,假設 Sam 已經完成自己負責的功能開發,準備要將完成的結果 Develop
分支 push 給 Jack,但現在沒有遠端儲存庫可以 push,所以改用 git bundle
來實現離線同步。
從 Sam 的本地版控記錄可看到 Jack 在 Develop
分支有 commit e3251c8
,這是雙方 Develop
分支都有的 commit 可以作為合併的基準點。
Sam 透過 git bundle
產生從 e3251c8
到本地分支 Develop
範圍的版控記錄檔,檔案命名為 Develop_FromSam
,檔案會產生在工作目錄內,將這個檔案交給 Jack。
1 | git bundle create Develop_FromSam e3251c8..Develop |
Jack 收到檔案 Develop_FromSam
先用 git bundle verify
檢查檔案內容
1 | git bundle verify Develop_FromSam |
圖中可看到,此檔案內含 Develop
分支的版控記錄,並且只要本地端有 commit e3251c8
就可以進行 git pull
合併。
把 Develop_FromSam
檔案想像是一個遠端儲存庫,裡面有一個 Develop
遠端分支。
Jack 現在想把遠端 Develop
分支 pull 合併到本地的 Develop
分支。
和平常 pull 流程一樣,先 checkout 到本地 Develop 分支,再 git pull
遠端儲存庫的 Develop
分支。
如果 checkout 錯分支,這個 git pull
會合併到錯的分支上,這和平常 pull 行為是一樣的,只是現在遠端儲存庫換成版控記錄檔。
1 | git checkout Develop |
因為 Sam 在遠端 Develop
分支和 Jack 在本地都修改到同一個 README.md
檔案,此時合併發生衝突進入 MERGING 狀態。
透過 git status
或 git status -s
確認發生衝突的是 README.md
檔案,下圖 Untracked files Develop_FromSam
檔案可以無視,這個檔案在 git pull
合併完成後就可以刪掉了。
打開 README.md
看到 Git 版控提示的衝突內容
修正衝突並存檔
將修正的 README.md
檔案加入 git 索引,再透過 git commit
提交,合併完成了
1 | git add README.md |
Jack 的合併已經完成,因為有修正衝突,所以合併會產生新的 commit,將合併後的本地 Develop
分支也透過 git bundle
輸出版控記錄檔 Develop_FromJack
給 Sam 離線同步用。
上面這一步可以想成是原本在做 git push
的動作,只是目前沒有 Git 遠端儲存庫可以 git push
,所以這類動作現在都改用 git bundle
代替
從 Jack 的本地版控記錄可看到 Sam 在 Develop
分支有 commit e3251c8
,這是雙方 Develop
分支都有的 commit 可以作為合併的基準點。
Jack 透過 git bundle
產生從 e3251c8
到本地分支 Develop
範圍的版控記錄檔,檔案命名為 Develop_FromJack
,檔案會產生在工作目錄內,將這個檔案交給 Sam。
1 | git bundle create Develop_FromJack e3251c8..Develop |
Sam 收到檔案後先確認版控記錄檔內是否有 Develop
遠端分支和合併時必要的 commit e3251c8
因為 Jack 已經解決合併衝突,所以 Sam 的本地 Develop
分支直接 git pull
就完成合併了,此時 Sam 與 Jack 的 Develop
分支記錄都是一致的。
小提醒:git pull
前要記得切對分支,此例 Sam 要 checkout Develop
完成後即可將 Develop_FromJack
檔案刪除。
下圖可見,現在 Sam 和 Jack 的版控記錄是一致的,如果要送進公司上版,也可以直接把 Develop_FromJack
檔案傳進公司,再遠端遙控公司內部電腦透過 git pull
把 Develop
合併進去上版。
因為大家的版控記錄都是維持同步的狀態,將來 Work From Home 結束,回公司連上內部儲存庫,也是無縫銜接。
接下來說明如何實作 git format-patch
,但這個做法我不推薦,不想看的人可以直接跳過。
用 git format-patch 產生內容修補檔 (不建議開發用)
先把時光回朔到公司開始實施 Work From Home,並且 Sam 和 Jack 都在自己的本地分支修改過檔案的狀態
Sam 使用 git format-patch
產生 e3251c8
到 4df1ae8
之間的內容修補檔,並指定檔案輸出的目錄路徑 D:/Temp/GitTest/Develop_PatchFromSam/
1 | git format-patch e3251c8..4df1ae8 -o /d/Temp/GitTest/Develop_PatchFromSam/ |
目錄 D:/Temp/GitTest/Develop_PatchFromSam/
內產生了兩個修補檔,傳送給 Jack
Jack 收到 Sam 給的內容修補檔,使用 git am
將修補內容 commit 至本地的 Develop
分支
1 | git am [修補檔路徑] |
因為雙方 Develop
分支的 README.md
檔案都有修改,發生衝突了
聰明的 Sam 早早寫完先交付就沒事了,寫得慢的 Jack 就要解衝突,陷入被越拖越慢的惡性循環 😆
見下圖右上方,目前狀態進入 Develop|AM 處理模式了,倒楣的 Jack 先看看是什麼造成衝突
1 | git am --show-current-patch=diff |
把造成衝突,缺少的內容補進 README.md
,並存檔
把修改的結果加入 git 索引
1 | git add . |
問題排除,繼續執行 AM
1 | git am --continue |
第二個修補檔也發生衝突了,Jack 在心裡咒罵 Sam
再看看這次的衝突原因
再次把造成衝突,缺少的內容補進 README.md
並存檔
把修改的結果加入 git 索引
1 | git add . |
問題排除,繼續執行 AM
1 | git am --continue |
見圖右方 Develop|AM 狀態結束,總算合併完成了
現在 Jack 本地 Develop
分支多了兩個 commit 將 Sam 修改的內容併進來了
等等,做到這邊,有人注意到 Sam 還沒拿到 Jack 合併後的結果嗎?
所以 Jack 要再 git format-patch
產生 Develop
分支的修補檔回去給 Sam
而 Sam 將 Jack 給的修補檔再透過 git am
合併到自己本地 Develop
分支,又再產生新的 commit,大家的版控記錄越來越不一致…
在可以連到遠端儲存庫同步之前,大家的 Commit ID 都對不起來,有時會造成溝通或追查問題的困難,即使連到遠端儲存庫了,也免不了要再解決合併衝突
由此看來 git format-patch
並不適合開發過程的版控交換用途,他的重點是檔案的修補,做為交付修補程式的一種方式,而非版控記錄
以上是小弟個人的理解和實務操作經驗分享,如果有錯誤的地方還請不吝指點,謝謝 😊