前幾天看到龍哥的 YouTube 影片:【GIT 小教室】"~" 跟 "^" 有什麼不同?,赫然發現之前沒真的搞懂 HEAD1 HEAD2 的意義(其實「為自己學 Git」電子書裡有講,不知是沒翻到還是沒讀進腦袋裡,總之我就廢 Orz),難怪有時執行結果跟我想的不一樣。而另一個額外收獲,影片提到 Parent 給了我動機做完實驗,對 Commit 的 Parent 有更清楚的概念,之前約略知道,但被同事臨時抽問還是熊熊答不出來。既然沒有過人天分(像是森川葵),有些東西你就是得動手做過才會刻骨銘心,真正轉成屬於自己的知識,沒法偷懶,至少我的學習經驗是如此。

Parent 關係決成了 Git 線圖的線如何連接,除了作為起點的 Commit 之外,每個 Commit 都有 Parent,有時還會有兩個以上的 Parent (如下圖):

Parent 的概念很簡單,假設分支目前在 CommitA,我們增加或修改某些檔案提交 CommitB,CommitB 的 Parent 就是 CommitA。CommitB 再改一些東西後提交存成 CommitC,CommitC 的 Parent 便會是 CommitB。至於兩個 Parent 怎麼來?合併兩個分支時,兩個分支的最新 Commit 就是合併後 Commit 的兩個 Parent。至於有沒有可能出現三個 Parent?有! Git 可以一次合併兩個以上的分支(稱為 Octopus Merge 章魚式合併),合併幾條分支就會有幾個 Parent。

為加深印象,我設計了以下實驗:

rem 清空上次 Git 測試結果
rmdir .git /s /q
del . /q
rem 建立 Git Repository
git init
rem 建立 master.txt 並 Commit
touch master.txt
git add . && git commit -m "初始化"
rem 建立 dev Branch
git branch dev
rem 在 master 修改 master.txt 並 Commit
echo "ABCD" >> master.txt
git add . && git commit -m "master 異動"
rem 切到 dev 加入 new-feature.txt 並 Commit
git checkout dev
echo "New Feature" > new-feature.txt
git add . && git commit -m "加入 feature 修改"
rem 切回 master 把 dev 合併進來
git checkout master
git merge dev
rem 建立 alpha Branch
git branch alpha
rem 在 alpha Commit 一些內容
git checkout alpha
touch alpha.txt
git add . && git commit -m "加入 alpha 修改"
rem 切回 master 再建一個 beta Branch
git checkout master
git branch beta
rem 在 beta Commit 一些內容
git checkout beta
touch beta.txt
git add . && git commit -m "加入 beta 修改"
rem 切回 master,進行一些修改並 Commit 
git checkout master 
echo "123" >> master.txt
git add . && git commit -m "master 異動"
rem 一次合併 alpha & beta 
git merge alpha beta
rem 檢視分支樹
git log --graph --pretty="(%p) %h %s"

在電腦開個 gitmerge 之類的資料夾,開 Cmder 或 DOS 視窗切到該目錄執行以上指令,它會模擬三方合併的情境。最後我用 git log --graph --pretty="(%p) %h %s" 顯示 Commit 路徑,使用 pretty 指定顯示格式,括號內的代碼指向該 Commit 的 Parent。

Git 用 ASCII 文字畫出來的線圖很酷的,Commit 顯示順序由新到舊,時間愈晚的在愈上面,最底下的【初始化】b3413a5 是開天闢地第一個 Commit,故沒有 Parent。

加入【初始化】後,從 master 另開 dev 分支(此時 dev 分支會指向【初始化】),接著在 master 加入【master 異動】Commit 672dfb7,其 Parent 指向【初始化】b34135a5。之後切到 dev 分支新增【加入 feature 修改】Commit 1726b19,其 Parent 也是指向【初始化】b34135a5。

再來切回 master,下指令將 dev 合併進 master,此一合併 Commit 便有兩個 Parent,分別指向【master 異動】672dfb7 及【加入 feature 修改】1726b19 兩個 Commit,由於是 dev 併進 master,故第一個是【master 異動】672dfb7 在前,第二個才是【加入 feature 修改】1726b19,而此一順序即對映到 HEAD^1、^2 中 1 或 2。

合併後我再加開 alpha、beta 分支並提交 Commit,接著切回 master 用 git merge alpha beta 一次合併三個分支,最後做出有三個 Parent 的 Commit。

實驗觀察完畢。

Using experiment to explain the parent relationship between Git commits.


Comments

Be the first to post a comment

Post a comment