伸縮自如的 git flow

前言

今天將分享一個具有以下特性的 git flow:

  1. 在開發過程中,可以隨意 commit

  2. 測試完成後,即可將相對較友善於開發的 commit 轉換成正式上線標準的 commit

  3. 兩個分支檔案內容完全相同,但卻擁有完全不相干的 commit 歷史

以 Ray 來說,我在開發時,習慣畫流程圖,並且將我腦中覺得可行的邏輯一條條寫下來,然後實踐。 通常一個大功能可能會由好幾個邏輯組成,而這個 git flow 讓我可以在一個 feature 分支上,將我的每一個小邏輯都記錄下來,分成一個個 commit 。 最後確定沒有問題了,在轉換成正式上線時需要的大功能 commit 。
在實踐這個 git flow 的過程中,我對 git rebase 的熟悉與日俱增,並且訓練自己以更嚴謹的方式來做每一個 commit。
以下 Ray 個人覺得這個 git flow 可以帶來的好處:

  1. 因為 commit 很小,邏輯單一,不管是在實驗或是除錯方面,都有不可言諭的便利性。
  2. 利用小 commit 的方式將邏輯都記錄下來,一方面讓自己的思緒清晰,一方面讓每一個功能的邏輯清清楚楚。
  3. 看似複雜的流程,但其實熟悉之後,分支的整理只是一瞬間,可以在開發過程中更加的熟悉 Git 的進階操作。
  4. 因為 commit 很小,可以訓練較嚴謹的 commit 風格。

理想的 commit

今天提出的 git flow 只是一個舉例,不一定適用於每一個人,但重點是在實現這個 git flow 所需要的概念!

當我們在本地 commit 時,我們傾向這個 commit 可以越小越好,因為越小越純粹功能越單一的 commit ,不管是在 Debug ,或是邏輯實驗與印證都有不可言傳,只可意會的妙用!

例如呢?

讓我們具體一點!

當今天你在 Debug ,發現印出來的東西不如你的預期。你嘗試不同的方式來測試,直到印出的數據是你想要的!

如果你的 commit 有符合上面的原則,在這個時候呢,在每一次失敗的嘗試之後,你不需要繁複的修修改改。你只需要簡單的

git reset --hard

就算你一個 commit 邏輯整個錯了,如果你的 commit 功能夠單一,要拿掉這個 commit ,你只需要

git reset @^ --hard

我們想要的是什麼?

然而,正式上線的 git flow 不容許我們這樣做。正式上線的 git flow 通常會要求推上去的每一個 commit 都要’指定的功能正常’。意思可以理解說,如果要將 commit 最小化,那很有可能我們會需要多個最小化的 commit 來組成一個正式上線公司所需要的大功能。

需求整理

  • 開發時: 我們需要隨時可以 commit 的靈活度,功能越單一越好
  • 正式上線後: 我們需要每一個 commit 都符合公司指定的要求,功能正常

該選哪一種?

人家說,魚與熊掌,不可兼得,真的是這樣嗎?
可以兩種都要嗎?

歸納具體需求

那先來歸納一下,我們具體上,需要的東西

  1. 我們需要一支本地開發的 develop 分支,在這分支上,只要你爽,你想怎麼 commit 就怎麼 commit
  2. 我們需要一支正式要推上線的 master 分支,在這分支上,每個 commit 都代表著公司指定的功能
  3. 上面兩個分支,被 commit 的檔案內容是完全一樣
  4. (選擇性)以上兩個分支我都要保留其各自獨特的歷史

具體看起來是?

如下圖可以看到,左邊是檔案內容,右邊是 commit ,大家可以看到,在 develop branch 上,每個 small function 都對應到相應的檔案,這只是範例,表達我們所要的最小 commit 的概念。

master 分支:

接下來,如下圖我們來看看實際上線時, commit 應該是什麼樣子。我們可以看到,在 master branch 上,我們只有4個 commit ,而每個 commit 都包含了4個檔案(除了 .gitignore 的 commit 之外)。這只是範例,表達正式上線時所需求的 commit 標準往往比我們理想的最小 commit 還要大。

範例連結

實作

一切的起點

通常 .gitignore 會是一切的起點!

  • 使用 vim 建立 .gitignore 檔案,然後輸入想要 ignore 的檔案,再按 :wq 離開。
    vim .gitignore
  • 完成第一個 commit
    git add .ignore; git commit -m 'Added .gitignore';

develop 分支

接下來,我們將以 master branch 為基礎,建立 develop branch ,如同前面敘述的, develop branch 上面的 commit 會是最細最小的,而 master branch 上的 commit 會是符合正式上線標準的。

  • 首先,從已經設定好 .gitignore 的 master 分支為基礎,建立 develop 分支
git checkout -b develop

feature 分支

然後呢,我們要開始開發了!

  • 建立一個 feature 分支
  • 這個 feature 分支代表當前正在開發的功能
  • 這邊所說的功能,是依照正式上線的標準
  • 所以,一個功能等於一個 feature 分支
    git checkout -b feature;

自由的 commit

  • 在 feature branch 上,我們可以隨便 commit ,可以完全依照我們本身的 commit 習慣,把 commit 做到最細小,以享受在開發以及測試過程中帶來的方便
  • 建立檔案1~4,然後每個檔案分別代表一個最細小的 commit
    touch {1..4};
    git add 1;
    git commit 1 -m 'small function 1';
    git add 2;
    git commit 2 -m 'small function 2';
    git add 3;
    git commit 3 -m 'small function 3';
    git add 4;
    git commit 4 -m 'small function 4';

功能完成了

  • 在測試完成之後,確定沒有問題了!現在我們要把我們 commit 的標準轉換成正式上線的標準

  • 如同前面提過的, feature branch 的開發範圍,是以正式上線的一個功能為單位

  • 所以呢?我們要將一整個 feature branch ,濃縮成一個 commit

  • 因為我們要保有兩個分支,所以我們不可以直接在 feature branch 幹這件事,因為 develop branch 會需要它!

  • 這邊特別說明一下,其實以這個 git flow 來說,開發過程中的 develop 分支不見得需要保留,但本篇範例會以如果你需要保留的狀況下來實作。如果開發用的 develop 不須保留的話,程序上會簡化很多。

用來 merged 的分支

  • 為正式上線的 master branch 來建一個一次性的 toBeMerged branch

    git checkout -b toBeMerged
  • 接下來,要把 toBeMerged branch 變成符合被 master branch merge 的狀態

    git rebase -i master

    壓縮、重新命名 commit

  • 然後,我們剛剛有說過,我們要將多個開發流程中的最小 commit 壓縮成一個正式上線的 commit ,對吧?所以我們要將所有的 commit 往前壓縮,可以利用 fixup 這個選項,再來,正式上線的 commit 名稱肯定會跟我們開發時的 commit 名稱不同,所以這邊我們要重新命名這個壓縮後的 commit ,如下:

    reword 96c6c18 small function 1
    fixup 1dd84d2 small function 2
    fixup 1a71401 small function 3
    fixup f9c90c6 small function 4
  • 接下來,我們將這個壓縮過後的 commit 命名為

    big function 1
  • 然後:wq存檔離開

  • 此時使用git log來看看,看起來會像是下圖那樣

    git log

merge

  • 讓我們回到 master 來把它 merge 掉吧!

    git checkout master;
    git merge toBeMerged;
  • 接下來,切到 develop ,並且 merge feature branch

    git checkout develop;
    git merge feature;
  • 最後,把已經被 merge 完成的 feature branch 以及 toBeMerge branch 刪掉吧!
git branch -D feature toBeMerged
  • 複習一下,還記得我們要的是什麼嗎?

    • 在 develop 分支上,是最小的 commit
    • 在 master 分支上,是正式上線標準的 commit
    • 兩個分支的檔案內容必須要一模一樣
    • (選擇性的)同時保有兩個分支
  • 看看我們的現在的 master 分支是否跟我們要的一樣?

git checkout master;
git log

  • 看看檔案內容
    ls

  • 看看 develop 分支的 commit
git checkout develop;
git tag 'bigFunction1'
git log

  • 看看檔案內容
ls

第二階段

  • 一樣的流程,建立 feature branch ,然後是 toBeMerge branch
git checkout develop;
git checkout -b feature;
touch {5..8};
git add 5;
git commit -m 'small function 5';
git add 6;
git commit -m 'small function 6';
git add 7;
git commit -m 'small function 7';
git add 8;
git commit -m 'small function 8';
git checkout -b toBeMerged;
  • 重點來了,這時我們不能使用傳統的 rebase 來改造 toBeMerged branch ,因為 toBeMerged branch 的歷史不曾存在於 master branch 。

  • 讓我們想想,toBeMerged branch 應該要是什麼樣子才符合我們要的,適合被 master branch merge ?

    • 兩者要有共同的歷史(同sha1值)
    • 先前已經被 merge 過的,未壓縮過的 commit 不可以重複出現。簡單來說, samll function 14 早就被壓縮成 big function 1 了,所以 small funciton 14 不可以再出現
    • master branch 上面沒有的內容,要壓縮成一個 commit
    • 完成以上條件之後, toBeMerged branch 就可以被 master merge 了

第二次 rebase 與壓縮

  • 所以具體該怎麼操作?

  • 我們將會使用到 git rebase 的進階應用 git rebase --onto 。我們 rebase toBeMerged branch 到 master branch 上,然後只要是 develop branch 上已經存在的 commit ,我們都不要,最後,將被 rebase 的 branch 叫做 toBeMerged,照這個順序往下排列,就成了以下的指令。 git rebase --onto 的用法,可以參考官方的文件

    git rebase -i --onto master develop toBeMerged
  • 然後同上,將最小的 commit 壓縮成一個正式上線的 commit

    reword 3668e72 small function 5
    fixup fd05fa1 small function 6
    fixup 3a87c08 small function 7
    fixup c38957e small function 8
  • 壓縮後的 commit ,名稱為big function 2

  • 重複之前步驟,該 merge 的 merge ,該刪的刪

  • 來看看當前 develop 分支上的 commit 狀態,以及檔案內容

git checkout develop;
git tag bigFunction2;
git log --oneline
  • develop 分支上的 commit

ls
  • develop 分支上的檔案內容

  • 來看看當前 master 分支上的 commit 狀態,以及檔案內容
git checkout master;
git log
  • develop 分支上的 commit

ls
  • develop 分支上的檔案內容

總結

此範例中的git flow,可以讓我們在開發過程中以最小的 commit 來進行,甚至是以任何我們喜歡的方式來進行,而不需因為正式上線的 commit 標準有所犧牲。
過程看似複雜,看就 Ray 的使用經驗,如果相關原理都已經非常熟悉,其實指令輸入很快就完成了。
範例中的 develop 分支不一定需要保留,因為日後若正式上線的 commit 有錯誤的話還是得從正式上線的 commit 來做修正,當然如果保留的話,應該還可以衍生一些其他的變化以及應用,這些就留待各位自己去發掘啦!
若大家有任何想法歡迎在下面留言,我相信意見想法的交流是進步的捷徑。

在 MacOS 及 AWS 上部署 supervisor PayPal REST API 串接金流好簡單

留言

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×