Linux伺服器 Linux伺服器

資工所專業課程上課教材

資工所專業課程 > 課程內容 > 專題 - 使用 git 進行版本控制 - 分支與合併

專題 - 使用 git 進行版本控制 - 分支與合併

上次更新日期 2020/10/03

使用 git 一段時間後,你可能需要做某些測試,此時,你可能需要將你的專案做個分支來開發新的檔案或者是測試。 等到這個分支使用過一段時間,確定是可行的方案之後,就可以考慮將分支融合到原本的專案當中!完成這次的開發!

關於 git 檔案的追蹤與否: .gitignore 功能

  1. 關於 git 對於專案目錄底下的檔案分類:

    基本上, git 對於該專案目錄底下的檔案追蹤,主要分為三個群組,大概是:

    • 被追蹤:就是被 git add type1file 的檔案群,這些檔案在變更時, git 會提醒開發者,該檔案可能需要傳送紀錄才好。 這也是專案開發最重要的部份;
    • 被忽略:很多檔案可能是開發過程中會產生的目標檔案 (object file),這些檔案不需要被追蹤,此時,可以透過 .gitignore 的檔案, 紀錄可被忽略的檔案群,這樣,許多不應該被 git 管理的檔名,就可以略過而不被查詢了。
    • 不被追蹤:就是在專案目錄下,且不屬於其他兩群的檔案。基本上,這些檔案還是在『 git status 』時,會被 git 告知說, 是否需要被追蹤囉。
  2. 模擬網頁伺服器的環境中,如果有提供用戶上傳資料時,可能會有暫存檔,這些暫存檔假設放置 cache 這個目錄下, 因此,我們想要忽略 cache 這個目錄,那該如何處理呢?可以這樣做:
    # cd /var/www/html
    # git status
    On branch master
    nothing to commit, working tree clean
    
    # git ls-files
    index.html
    phpinfo.php
    server.php
    
    # mkdir cache
    # chown apache:apache cache
    # ll
    drwxr-xr-x. 2 apache apache   6  9月 24 04:55 cache
    -rw-r--r--. 1 root   root    89  9月  4 00:27 index.html
    -rw-r--r--. 1 root   root    21  9月  4 00:23 phpinfo.php
    -rw-r--r--. 1 root   root   126  9月 24 00:14 server.php
    
    # touch cache/upload_{a,b,c}_{1,2,3}.o
    # ll cache
    -rw-r--r--. 1 root root 0  9月 24 04:57 upload_a_1.o
    -rw-r--r--. 1 root root 0  9月 24 04:57 upload_a_2.o
    ....
    -rw-r--r--. 1 root root 0  9月 24 04:57 upload_c_2.o
    -rw-r--r--. 1 root root 0  9月 24 04:57 upload_c_3.o
    
    # git status
    On branch master
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
    
            cache/
    
    nothing added to commit but untracked files present (use "git add" to track)
    
    這個時候你就可以發現到 git 有注意到 cache/ 目錄的產生,而且裡面有好多檔案了。現在,如果我想要完整的忽略該目錄,基本上可以這樣做:
    # vim .gitignore
    cache/
    
    # git status
    On branch master
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
    
            .gitignore
    
    nothing added to commit but untracked files present (use "git add" to track)
    
    你可以輕鬆的發現到,只剩下 .gitignore 這個可被忽略的檔案生成而已,那個要被忽略的 cache/ 目錄,就整個被忽略了! 這樣就不用在乎該目錄囉!接下來,將 .gitignore 加入追蹤列表,並且上傳到資料庫即可。
    # git add .gitignore
    # git commit -m 'add ignore file .gitignore'
    [master 723ea7f] add ignore file .gitignore
     1 file changed, 1 insertion(+)
     create mode 100644 .gitignore
    
    # git status
    On branch master
    nothing to commit, working tree clean
    
    # git ls-files
    .gitignore
    index.html
    phpinfo.php
    server.php
    
    此時,你的專案目錄裡面依舊會存在 cache 目錄,但是沒有被追蹤了!而且,你的 git status 會回傳你 tree clean 的情境喔!真好!
  3. 忽略的追蹤檔 .gitignore 檔案格式:

    初次接觸 .gitignore 時,大部分的人都會直接將檔名寫入。如果是非特定檔名,或者是需要萬用字元的檔名 (wildcard) 時, 例如使用 * 來代表 0 到無窮多個任意字元時,有些規則還是得要稍微注意的好:

    萬用字元使用格式接受的檔名列表示範
    # 或空白行# example其實就是註解,會被略過
    /(斜線)/index.htmlindex.html
    index.htmlindex.html
    hw/index.html
    newclass/index.html
    cache/cache (通常代表目錄名稱,連同內部檔案喔!)
    *(星號)index.*index.html
    index.php
    index.txt
    *index.htmlindex.html
    hw/index.html
    newclass/index.html
    **(兩個星號)cache/**cache/ 底下的所有檔案,連同子目錄
    **/cachehw/cache
    newclass/cache
    cache
    ?index?.htmlindex1.html
    index2.html
    index??.htmlindex11.html
    indexbb.html
    [a-z] (集合)*.[ot]main.o
    check.t
    *.[0-9]main5.o
    check3.t
  4. 實做練習:
    1. 假定除了 cache 之外,你的網頁目錄當中的 tmp 也是個暫存目錄。請建立該目錄, 並在 tmp 底下隨意增加大約 3~5 個以上的檔案看看。
    2. 檢查看看你的 git 狀態,有沒有偵測到該子目錄呢?
    3. 在你的 .gitignore 裡面加入 tmp 的目錄忽略,然後再次的觀察一下狀態,有發現什麼現象?
    4. 若想要知道目前的目錄與之前的 git 狀態差別有多少,可以使用『 git diff 』檢查一下喔!就能知道哪個檔案修改了哪些東西。
    5. 請上傳最新的資料,同時加入 'add tmp/ ignore' 的訊息!

開始使用分支

基本上,除了多人共同開發的網路環境之外,在本機上面的 git 系統運作,大部分都是單人開發的環境。 那既然是單人開發的環境,幹麻需要分支?不都是我自己能夠處理的?那如此一來,透過 git 原本的版本控制不就好了? 搞個分支是要搞死自己?

不過,我們得要想到,有時候總是需要進行測試的吧?這個測試可能是會成功也可能是會失敗吧?如果成功的話, 就將程式碼留下來,這沒問題!那如果是失敗呢?怎麼去除?另外,在測試的途中,可能還會發現某些有趣的程式碼, 於是,你可能也會想要測試分支當中的分支。

此外的此外,有時候你只是想要解決一個問題,這個問題解決完畢之後,你可能就會將這個問題產生的過程資料全部銷毀! 但是最終解決問題的解答則要寫回主程式中,這可就得真的要使用分支了!即使是個人開發的環境, 你也是需要了解分支的應用,可以讓你管理你的程式碼,更加有效率!

  1. 分支的命名規則:
    1. 主線分支稱為 master:
      基本上,你的主程式碼也是一個分支,當然,目前我們也只有這個分支的意思。而目前這個分支是最重要的, 所以, git 預設稱呼目前這個主線分支為 master。
    2. 建立分支的主要指令大致如下:
      # git branch 新分支名稱
      # git show-branch
      
      branch 是分支的意思,透過上述的指令,就可以建立分支與觀察分支。那麼,分支的名稱是否該有規則? 是的,就跟變數設定很類似喔!分支的名稱不可以隨便亂取!
    3. 分支名稱的命名規則:
      • 分支名稱的開頭不可以是減號 (-)
      • 分支名稱可以包含斜線 (/) ,例如 bugs/mybug ,但是結尾不能是斜線 (容易被誤判為目錄)
      • 每個接在斜線後的第一個字元,不可以是小數點,例如 bugs/.mybug 就是錯誤的。
      • 不能使用連續的兩個小數點 (..)
      • 分支名稱內不可以用空白字元、不可以用 git 的特殊符號 ( ~, ^, :, ?, *, [ )、不可以用 ASCII 的控制字元。
    4. 基本上,保持良好的命名習慣,單純使用英文、數字與底線,搭配斜線分類,應該就能夠應付大部分的情境! 也不容易對 git 的系統運作產生問題!
  2. 開始建立分支與開始應用分支:
    1. 在目前的狀態下,建立名為 hw/unit2 的分支,並且觀察分支狀態:
      # git branch hw/unit2
      # git branch
        hw/unit2
      * master
      
      建立了兩個分支,名稱如上,而目前的系統環境,使用的是 master 分支,就是帶有星號 (*) 的那個分支系統。 那麼如果我想要知道每個分支從哪邊來的呢?
    2. 查看每個分支的狀態:
      # git show-branch
      ! [hw/unit2] add tmp/ ignore
       * [master] add tmp/ ignore
      --
      +* [hw/unit2] add tmp/ ignore
      12 3
      
      上面的結果可以透過兩個減號 (--) 分開來看,先看上半部的部份:
      • 上半部是目前所有分支的列表,基本上排序是由最新到最舊,每一個分支佔用一行。
      • 從上而下,每個分支的狀態都會佔用自己的一個位置,例如 [hw/unit2] 佔用第 1 個位置,[master] 佔用第 2 個位置。
      • 有星號的 (*) 那個,代表目前使用的分支。
      下半部的部份則是該分支在哪些分支裡面出現的意思,若有加號 (+) 星號 (*) 或減號 (-) 在該分支之前,都代表該分支在某分支之內。以 [hw/unit2] 為例:
      • 第一個位置上為 + 號,代表 [hw/unit2] 在 [hw/unit2] 分之內
      • 第二個位置上為 * 號,代表 [hw/unit2] 在 [master] 分支內,且目前正在應用中。
      至於減號 (-) 一般代表合併送交的意思。 至於分支後面的資料,就是該分支最新的送交點。我們上個小節的實做不是有做這個動作? 那就是該送交點囉!
    3. 變更分支的應用 - 最好需要保持在 clean 的狀態來切換較佳:
      # git status
      On branch master
      nothing to commit, working tree clean
      
      # git branch
        hw/unit2
      * master
      
      # git checkout hw/unit2
      Switched to branch 'hw/unit2'
      
      # git branch
      * hw/unit2
        master
      
      現在,開始來處理 hw/unit2 這個分支的許多動作!這個動作預計將進行:
      • 增加 unit2 目錄,並且在其中增加 index.html 的檔案,內容寫 class02 即可
      • 修改 index.html 檔案,增加一條到 unit2 的連結即可。
      # mkdir unit2
      # echo class02 > unit2/index.html
      # vim index.html
      <ul>
      	<li><a href='unit2'>第二章作業</a></li>
      </ul>
      
      # git status
      On branch hw/unit2
      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:   index.html
      
      Untracked files:
        (use "git add <file>..." to include in what will be committed)
      
              unit2/
      
      no changes added to commit (use "git add" and/or "git commit -a")  <==用這個處理看看
      
      # git commit -a -m 'add homework unit2'
      [hw/unit2 b6d2522] add homework unit2
       1 file changed, 3 insertions(+)
      
      # git status
      On branch hw/unit2
      Untracked files:
        (use "git add <file>..." to include in what will be committed)
      
              unit2/
      
      nothing added to commit but untracked files present (use "git add" to track)
      
      // 看起來,還是需要手動將目錄加入控制監視才行啊!
      
      # git add unit2/
      # git commit -a -m 'add homework unit2'
      [hw/unit2 1b03199] add homework unit2
       1 file changed, 1 insertion(+)
       create mode 100644 unit2/index.html
      
      # git status
      On branch hw/unit2
      nothing to commit, working tree clean
      
      # git ls-files
      .gitignore
      index.html
      phpinfo.php
      server.php
      unit2/index.html
      
      # ll
      drwxr-xr-x. 2 apache apache 186  9月 24 04:57 cache
      -rw-r--r--. 1 root   root   146  9月 28 00:20 index.html
      -rw-r--r--. 1 root   root    21  9月  4 00:23 phpinfo.php
      -rw-r--r--. 1 root   root   126  9月 24 00:14 server.php
      drwxr-xr-x. 2 root   root    51  9月 24 05:32 tmp
      drwxr-xr-x. 2 root   root    24  9月 28 00:20 unit2
      
    4. 切換回較早、較舊、與目前檔案狀態不同的分支,讓我們試圖回到 master 分支上面去看看:
      // 觀察現有分支,並且切換到 master 上
      # git branch
      * hw/unit2
        master
      
      # git checkout master
      Switched to branch 'master'
      
      # git branch
        hw/unit2
      * master
      
      # ll
      drwxr-xr-x. 2 apache apache 186  9月 24 04:57 cache
      -rw-r--r--. 1 root   root    89  9月 28 00:41 index.html
      -rw-r--r--. 1 root   root    21  9月  4 00:23 phpinfo.php
      -rw-r--r--. 1 root   root   126  9月 24 00:14 server.php
      drwxr-xr-x. 2 root   root    51  9月 24 05:32 tmp
      
      # cat index.html
      
      不知道你會不會嚇一跳,因為 index.html 裡面新增的部份不見了,而新增加的 unit2 目錄也不見了! 怎麼會這樣呢?這是因為目前你的環境變這樣了:
                     ------ hw/unit2 ---->  A
                    /
      ----- master ----->  B
      
      我們剛剛做的動作在 A 點上面,但是你切換回 B 點的環境了!因此,所有在 B 與 A 之間的動作, 會全部被拿掉,而僅帶入 B 點的資料而已!所以,當然 index.html 以及 unit2/ 目錄就會被歸零復原! 也就是說,當你使用了切換分支的行為,你當下的目錄結構就會進行:
      • 預計使用的分支沒有,目前分支有的資料,將會被暫時移除
      • 預計使用的分支有,目前分支沒有的資料,則會被加入到目前的目錄結果中。
      所以也無須害怕,如果你想要知道新的資料有什麼,就切換到該分支即可! git 會將這些資料好好的放置到 .git 底下儲存好的! 不要擔心!
  3. 簡單的分支合併行為:
    1. 假設我們對於 hw/unit2 的開發已經完成,這個分支確實是正確運作的結果,因此,我們想要將這個分支開發的結果回歸到主線 (master) 上面, 這個時候我們可以這樣做:
      // 先要確認目前是在 master 主線上,這樣才能夠合併其他分支喔!
      # git show-branch
      ! [hw/unit2] add homework unit2
       * [master] add tmp/ ignore            # 確實在主線上
      --
      +  [hw/unit2] add homework unit2       # 最新的送交是在 add homework unit2 這個點上面
      +  [hw/unit2^] add homework unit2
      +* [master] add tmp/ ignore
      
      // 開始進行合併的行為
      # git merge hw/unit2
      Updating a40d703..1b03199
      Fast-forward
       index.html       | 3 +++
       unit2/index.html | 1 +
       2 files changed, 4 insertions(+)
       create mode 100644 unit2/index.html
      
      # git show-branch
      ! [hw/unit2] add homework unit2
       * [master] add homework unit2
      --
      +* [hw/unit2] add homework unit2
      
      # git log
      // 就會看到將 hw/unit2 的送交 log 也通通加入回來了!
      
      這時你就會發現,主線的資料跟 hw/unit2 是一模一樣的了!相當方便進行開發與彙整喔!
  4. 實做練習:
    1. 建立名為 hw/unit5 的新的分支
    2. 切換到該新分支,並且依據剛剛的處理方案,建立名為 unit5 的目錄,建立名為 unit5/index.html 的檔案, 修改 index.html 的連結內容
    3. 確認 hw/unit5 分支的狀態是 clean 的!很重要喔!
    4. 確認一切無誤,將 hw/unit5 這個分支合併到 master 主線上面。

前一版次的分支應用

  1. 什麼情境產生了非新版資料的分支應用:
    1. 例如,目前是 2.0 版,但是有讀者說,他的 1.5 版次有繼續更新,所以你得要回去 1.5 版次的環境,然後根據讀者的說明, 重建與測試該環境的情境。
    2. 例如前面一小節的測試中,我們先寫了 unit2 與 unit5 這兩個作業,但是現在我們要回去 unit1 的作業環境, 以讓使用者比較有乾淨的測試環境來理解,而不是亂七八糟的存在。
  2. 從之前的舊版次增加分支的方式:
    1. 先找出尚未加入 hw/unit2 那個入口點,如下圖所示,找到 A 點的 log 旗標名稱:
                      ------ hw/unit2 ------>  B
                     /                    \
      ----- master -A----------------------C---> D
      
      可以這樣做:
      # git log
      commit 1b0319971db9a8c2327a8283faf61498151dc0d6 (HEAD -> master, hw/unit2)
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Mon Sep 28 00:18:09 2020 +0800
      
          add homework unit2
      ....
      commit a40d703f6c206f93ec409d93132a2ab4c1604c5f
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Thu Sep 24 05:35:25 2020 +0800
      
          add tmp/ ignore
      ....
      
      // 建立第一章 hw/unit1 的作業環境:
      # git branch hw/unit1 a40d7
      # git branch
        hw/unit1
        hw/unit2
      * master
      
      # git show-branch
      ! [hw/unit1] add tmp/ ignore
       ! [hw/unit2] add homework unit2
        * [master] add homework unit2
      ---
       +* [hw/unit2] add homework unit2
       +* [hw/unit2^] add homework unit2
      ++* [hw/unit1] add tmp/ ignore
      
      你會發現到,下半部的資料中,只有 hw/unit1 自己包含自己在內,其他兩個分支並沒有被包含在 hw/unit1 喔! 畢竟 hw/unit1 是回到之前的環境中,與新的 master, hw/unit2 比較,當然不可能包含呀!也就是, 舊的分支不太可能包含新的分支在內!
    2. 切換到舊的環境去。事實上,你目前所在處有點像下圖的 E 點:
                      ------ hw/unit2 ------>  B
                     /                    \
      ----- master -A----------------------C---> D
                    \
                     --- hw/unit1 ---> E
      
      目前這個點應該是與 hw/unit2 完全無關!現在,讓我們來回到過去吧!
      # git checkout hw/unit1
      Switched to branch 'hw/unit1'
      
      # ll
      drwxr-xr-x. 2 apache apache 186  9月 24 04:57 cache
      -rw-r--r--. 1 root   root    89  9月 28 01:35 index.html
      -rw-r--r--. 1 root   root    21  9月  4 00:23 phpinfo.php
      -rw-r--r--. 1 root   root   126  9月 24 00:14 server.php
      drwxr-xr-x. 2 root   root    51  9月 24 05:32 tmp
      
      你會發現到,確實喔! unit2 也不見了! index.html 也回到原始狀態!好,讓我們來處理 unit1 的作業吧!
    3. 在新的分支工作:
      # git branch
      * hw/unit1
        hw/unit2
        master
      
      # mkdir unit1
      # echo 'my class1 homework' > unit1/index.html
      # vim index.html
      <ul>
      	<li><a href='unit1'>第一章作業</a></li>
      </ul>
      
      // 開始送交資料
      # git add -A
      # git status
      On branch hw/unit1
      Changes to be committed:
        (use "git reset HEAD <file>..." to unstage)
      
              modified:   index.html
              new file:   unit1/index.html
      
      # git commit -a -m 'add class1 homework'
      [hw/unit1 780235f] add class1 homework
       2 files changed, 4 insertions(+)
       create mode 100644 unit1/index.html
      
  3. 合併從舊版次建立的分支行為,可能導致的合併錯誤整理:
    1. 我們在 hw/unit1 當中有修改了 index.html 喔,但是這個檔案似乎在 hw/unit2 面有被合併了! 因此,這個檔案似乎有點衝突喔!以下圖來看, D 點與 E 點的 index.html 可能有點衝突的問題! 我們現在想做的,就是將 E 融合到 D 上面!
                      ------ hw/unit2 ------>  B
                     /                    \
      ----- master -A----------------------C--------> D
                    \                         /
                     --- hw/unit1 ---> E---
      
    2. 舊版次合併的方式:其實跟一般合併相同而已:
      // 先回到 master 主線分支
      # git checkout master
      Switched to branch 'master'
      
      # git branch
        hw/unit1
        hw/unit2
      * master
      
      // 開始合併 hw/unit1 喔!
      # git merge hw/unit1
      Auto-merging index.html
      CONFLICT (content): Merge conflict in index.html
      Automatic merge failed; fix conflicts and then commit the result.
      
      # git status
      On branch master
      You have unmerged paths.
        (fix conflicts and run "git commit")
        (use "git merge --abort" to abort the merge)
      
      Changes to be committed:
      
              new file:   unit1/index.html
      
      Unmerged paths:
        (use "git add <file>..." to mark resolution)
      
              both modified:   index.html
      
      意思是說,大致上都合併成功了,但是 index.html 的合併失敗!因為有衝突啊!如果你查閱一下該檔案,就可以發現問題:
      # cat index.html
      <ul>
      <<<<<<< HEAD
              <li><a href='unit2'>第二章作業</a></li>
      =======
              <li><a href='unit1'>第一章作業</a></li>
      >>>>>>> hw/unit1
      
    3. 放棄這次的合併:如果出於某些原因,你必須要回頭去查一查哪邊出問題,所以想要放棄這次的合併時, 可以這樣做:
      # git merge --abort
      # cat index.html
      
      這樣,系統就可以放棄合併了!
    4. 將衝突解決,確認合併工作:這次,解決合併問題吧!
      # git merge hw/unit1
      Auto-merging index.html
      CONFLICT (content): Merge conflict in index.html
      Automatic merge failed; fix conflicts and then commit the result.
      
      # vim index.html
      <ul>
              <li><a href='unit2'>第二章作業</a></li>
              <li><a href='unit1'>第一章作業</a></li>
      </ul>
      
      // 改好之後,就在次的上傳合併資料吧!
      # git add index.html
      # git status
      On branch master
      All conflicts fixed but you are still merging.
        (use "git commit" to conclude merge)
      
      Changes to be committed:
      
              modified:   index.html
              new file:   unit1/index.html
      
      # git commit -a -m 'add homework class1 to master'
      [master 9b3ee0f] add homework class1 to master
      
      # git show-branch
      ! [hw/unit1] add class1 homework
       ! [hw/unit2] add homework unit2
        * [master] add homework class1 to master
      ---
        - [master] add homework class1 to master   <==合併送出的減號 (-) 出現了!
      + * [hw/unit1] add class1 homework
       +* [hw/unit2] add homework unit2
       +* [hw/unit2^] add homework unit2
      ++* [hw/unit1^] add tmp/ ignore
      
  4. 實做練習:
    1. 先確認目前使用的分支為 master 主線分支,而且確認 hw/unit5 那個習題已經完成!
    2. 建立名為 hw/unit3 的新的分支,且分支位置請選擇 hw/unit2 合併到 master 的那個時間點,亦即送交時間點必須要在 hw/unit5 合併之前的某個時段才行!
    3. 切換到該新分支,並且依據剛剛的處理方案,建立名為 unit3 的目錄,建立名為 unit3/index.html 的檔案, 修改 index.html 的連結內容
    4. 確認 hw/unit3 分支的狀態是 clean 的!很重要喔!
    5. 確認一切無誤,將 hw/unit3 這個分支合併到 master 主線上面,並且自行解決無法 merge 的檔案衝突問題!。

分支的刪除與標籤的製作

  1. 為什麼需要刪除分支:

    有時候我們只是單純的要測試某個時間點產生的分支狀態,測試完畢之後,該分支的資料並不需要儲存, 所以,該分支的狀態就可以被捨棄的意思。另外,某些時刻我們的程式改寫好像有問題,可以用其他方法解決, 因此,該分支也能夠撤除。當然,已經確認沒問題且合併到主線分支上了,那麼該分支似乎也沒必要持續存在啊!

  2. 分支的新增:
    # git log
    .....
    commit 9b3ee0fb960dcabbe77f1f45868e04763b1c45e7
    Merge: 1b03199 780235f
    Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
    Date:   Mon Sep 28 02:02:21 2020 +0800
    
        add homework class1 to master
    .....
    
    # git branch hw/unit4 9b3ee
    
    # git branch
      hw/unit1
      hw/unit2
      hw/unit4
    * master
    
    # git checkout hw/unit4
    Switched to branch 'hw/unit4'
    
    # mkdir unit4
    # echo "hehe" > unit4/index.html
    # git add -A
    # git commit -a -m 'add unit 4'
    [hw/unit4 e4ea47c] add unit 4
     1 file changed, 1 insertion(+)
     create mode 100644 unit4/index.html
    
  3. 分支的變更與刪除:
    # git checkout master
    Switched to branch 'master'
    
    # git branch
      hw/unit1
      hw/unit2
      hw/unit4
    * master
    
    # git show-branch
    ! [hw/unit1] add class1 homework
     ! [hw/unit2] add homework unit2
      ! [hw/unit4] add unit 4
       * [master] add homework class1 to master
    ----
      +  [hw/unit4] add unit 4
      -- [master] add homework class1 to master
    + +* [hw/unit1] add class1 homework
     ++* [hw/unit2] add homework unit2
     ++* [hw/unit2^] add homework unit2
    +++* [hw/unit1^] add tmp/ ignore
    
    # git branch -d hw/unit4
    error: The branch 'hw/unit4' is not fully merged.
    If you are sure you want to delete it, run 'git branch -D hw/unit4'.
    
    # git branch -D hw/unit4
    Deleted branch hw/unit4 (was e4ea47c).
    
    # git branch
      hw/unit1
      hw/unit2
    * master
    
  4. 標籤的製作:
    1. 為什麼要製作標籤 (Tag) 呢?通常原因是因為某個版本固定了,不再更動了,未來的變動則是掛上其他釋出版本。 例如 V1.0 釋出之後,可以將這個版本固定成為一個『標籤 (Tag) 』讓它不動這樣。說穿了,其實就是額外給個名稱, 讓操作使用這個專案的人,更清楚知道版本別這樣。
    2. 標籤的製作通常就是找一個送交的點,這個送交的點當然需要有點意義比較好。舉例來說, 我們將 hw/unit2 合併的那個點,可以視為第二單元已經完成的一個重要里程碑,因此,或許將它當成 v0.2 這樣的版本別! 來處理也可以的!實際作法有點像這樣:
      # git log
      .....
      commit 1b0319971db9a8c2327a8283faf61498151dc0d6 (hw/unit2)
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Mon Sep 28 00:18:09 2020 +0800
      
          add homework unit2
      .....
      
      # git tag -m "Tag version v 0.2" V0.2 1b0319
      
      # git tag
      V0.2
      
      # git log
      .....
      commit 1b0319971db9a8c2327a8283faf61498151dc0d6 (tag: V0.2, hw/unit2)
      Author: VBird Tsai 
      Date:   Mon Sep 28 00:18:09 2020 +0800
      
          add homework unit2
      .....
      
      # git show V0.2
      tag V0.2
      Tagger: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Mon Sep 28 06:24:40 2020 +0800
      
      Tag version v 0.2
      
      commit 1b0319971db9a8c2327a8283faf61498151dc0d6 (tag: V0.2, hw/unit2)
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Mon Sep 28 00:18:09 2020 +0800
      
          add homework unit2
      
      diff --git a/unit2/index.html b/unit2/index.html
      new file mode 100644
      index 0000000..d29f2cf
      --- /dev/null
      +++ b/unit2/index.html
      @@ -0,0 +1 @@
      +class02
      
      很簡單的做了個標籤,未來可以輕鬆的找到我們的入口點,而不用持續分析不同的分支或送交點。請注意喔, 標籤可以用來取代分支的名稱喔!也就是說,未來如果你想要切換到 V0.2 版本的話,可以這樣做:
      # git checkout V0.2
      
  5. 分析不同 commit 之間的檔案差異:
    1. 如果你想要知道兩次 commit 之間的檔案間的差異,除了前一章的 git cat-file -p flag 功能之外,其實也能夠透過簡易的 git diff 來處理! 基本上, git diff 可以列出該目錄底下與 clean 的狀態之間的檔案差異:
      # cd /var/www/html
      # vim server.php
      echo 'Its OK';
      
      # git diff
      diff --git a/server.php b/server.php
      index e29921d..510553b 100644
      --- a/server.php
      +++ b/server.php
      @@ -2,5 +2,5 @@
      diff --git a/server.php b/server.php
      index e29921d..510553b 100644
      --- a/server.php
      +++ b/server.php
      @@ -2,5 +2,5 @@
              foreach ( $_SERVER as $myindex => $myvalue ) {
                      echo "index is " . $myindex . ", value is " . $myvalue . "\n";
              }
      -
      +       echo 'Its OK';
       ?>
      
    2. 那如果你想要處理的是兩次 commit 之間的差異呢?依舊使用 git diff 進行喔!
      # git log
      commit 9b3ee0fb960dcabbe77f1f45868e04763b1c45e7 (HEAD -> master)
      Merge: 1b03199 780235f
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Mon Sep 28 02:02:21 2020 +0800
      
          add homework class1 to master
      
      commit 780235faf99eb3eff46d5b8b59f567f6eb5c80ea (hw/unit1)
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Mon Sep 28 01:41:49 2020 +0800
      
          add class1 homework
      
      ......
      commit a40d703f6c206f93ec409d93132a2ab4c1604c5f
      Author: VBird Tsai <dmtsai@mail.ksu.edu.tw>
      Date:   Thu Sep 24 05:35:25 2020 +0800
      
          add tmp/ ignore
      ....
      
      # git diff 78023 4a1c7e
      diff --git a/.gitignore b/.gitignore
      deleted file mode 100644
      index a3ad08c..0000000
      --- a/.gitignore
      +++ /dev/null
      @@ -1,2 +0,0 @@
      -cache/
      -tmp/
      diff --git a/index.html b/index.html
      index c5bfaf2..0b51bc2 100644
      --- a/index.html
      +++ b/index.html
      @@ -4,8 +4,5 @@
       </head>
       <body>
       myname is XXX xxx
      -<ul>
      -       <li><a href='unit1'>第一章作業</a></li>
      -</ul>
       </body>
       </html>
      diff --git a/unit1/index.html b/unit1/index.html
      deleted file mode 100644
      index 59545fe..0000000
      --- a/unit1/index.html
      +++ /dev/null
      @@ -1 +0,0 @@
      -my class1 homework
      
      也就是說,透過找到兩次 commit 的 flag 之後,加入到 git diff 後面即可分析囉!
    3. 已經到最新的 up to date 功能:因為 git 會根據分支的結合方式,主動的判斷到底這個分支有沒有加入到你的分支中了。 因此,如果你的分支都沒有重新增加新資料,那麼,當你要重複合併分支時,git 就會主動告知,已經合併成功了! 而且是最新的合併啊!
      # git show-branch
      ! [hw/unit1] add class1 homework
       ! [hw/unit2] add homework unit2
        * [master] add homework class1 to master
      ---
        - [master] add homework class1 to master
      + * [hw/unit1] add class1 homework
       +* [hw/unit2] add homework unit2
       +* [hw/unit2^] add homework unit2
      ++* [hw/unit1^] add tmp/ ignore
      
      # git merge hw/unit2
      Already up to date.
      
  6. 實做練習:
    1. 先確認目前使用的分支為 master 主線分支,而且確認 hw/unit3 那個習題已經完成!
    2. 確認 unit3 已經合併到 master 主線分支上。
    3. 將沒有用途的 hw/unit3 分支刪除
    4. 將 hw/unit3 合併的那一個送交點,設定標籤為 V0.3 版本。
    5. 將目前的環境回歸到 V0.3 的版本,進行簡單的目錄檔名檢查
    6. 回到原本的最新主線環境上。

參考資料