---
# System prepended metadata

title: Blog 1｜深入淺出 GIT
tags: [部落格]

---

# Blog 1｜深入淺出 GIT

第一篇來介紹一下 GIT，我第一次開始認真學習並且使用 GIT 技術，也是研究所畢業後的事情，在面試找到工作後到職日前，把 GIT 的網路上教程好好惡補一下 XD

以前在學校只有使用基本的 git add -> git commit -> git push 而已，說來其實有點慚愧，認真去了解後會發現 GIT 的許多設計都很有哲理，GIT 的有些指令或許有些複雜，其實只需要掌握一些基本原則，是一個非常容易使用且方便的版本管理工具

### **(1) GIT 基本操作**
Git 在把改動打包成 commit 的時候，需要經過兩個步驟

- ```git add``` : 把改動放到暫存區
- ```git commit``` : 把暫存區的改動用 commit message 包成一個 commit

暫存區的概念和這兩個步驟，看起來會有些複雜，但了解後其實是很方便且貼心的設計。有時同時有非常大量的改動，全部包成一個 commit 可讀性是很差的，不好 code review 日後也會很難追蹤，這種兩階段的機制能夠很好管理，每一個 commit 需要包含哪些部分。

![image](https://hackmd.io/_uploads/H1SZ3sVZA.png)

### **(2) GIT branch 的使用**
使用 GIT 時，有一個基本但非常實用的功能是 branch。在 GIT 中，`branch 是一個指向 commit 的 pointer`，他被放在目錄 ```.git/refs/heads/``` 當中，因此新增/刪除/切換 branch，都是非常快速且輕量化的操作。

透過 ```git checkout [-b] <branch_name>```，我們可以快速於不同任務當中切換。

![image](https://hackmd.io/_uploads/HJxADm8-R.png)

### **(3) GIT reset v.s. revert**
當把改動打包成 commit 後，有時後悔想要拆除，就要使用 reset 或是 revert 指令。

- ```git reset``` 會真的把 commit 拆除掉，又分成三類
    - (1) ```git reset --soft <commit_id>``` : disk 和 staging 仍會有該改動
    - (2) ```git reset --mixed <commit_id>``` : disk 仍會有該改動
    - (3) ```git reset --hard <commit_id>``` : 該改動全部被移除
- ```git revert``` 則是會建立一個和原改動相反的 commit

這兩個指令要視情況使用，原則上想要拆除剛建立的 commit 並保持整體改動歷史乾淨，會選擇 ```git reset``` 重新打包才對。而若是已將該 branch 推送到遠端的 repository，或是想要很明確在改動歷史中，知道曾經有這個 change 但後來決定要移除，就會選擇 ```git revert```。

這兩種做法還有一個差異，關於是否可以直接撤銷遠端上方的 commit，使用 revert 的方式可以直接 ```git push``` 更新遠端，但若是 reset 的方式要使用 ```git push -f``` 來更新。



### **(4) GIT merge v.s. rebase**
在使用 branch 開發功能完畢後，需要把這個 branch 上的改動 merge 回主分支，這時候可以分成 merge 或是 rebase 兩種操作。

- ```git merge``` : 建立一個新 commit 同時指向兩個 branch
    - 若方向為 A merge B，則 A 這個 branch 會被指到最新建立的 commit 上，B 保持不變
- ```git rebase``` : 將我這個 branch 上獨有的 commit，一一搬移到對方 branch 上
    - rebase 常被用來增加改動歷史的可讀性



### **(5) 一個常見的協作流程**
這裡介紹一種最常用、最好用，團隊共同使用 gitlab 來開發的方法。

- (1) 將 gitlab 上的 ```git clone``` 回本地，其中主分支為 ```main``` branch
- (2) 使用 ```git checkout -b my-feature``` 切換到開發分支
- (3) 開發並打包 commit，透過 ```git add``` 和 ```git commit```
- 在 push 回 gitlab 前，假設有其他人也同時更新 gitlab 上的 ```main``` branch
- (4) ```git checkout main && git pull origin main```，把 gitlab 上的 ```main``` branch 同步回本地
- (5) ```git checkout my-feature && git rebase main```，整理除了 ```origin/main``` 之外的額外改動
- (6) ```git push origin my-feature```，將 ```my-feature``` 推送到 gitlab 上
- (7) 於 gitlab 的網頁上，使用 Merge Request 按鈕，發送一個 merge 的請求
- (8) 於 gitlab 的網頁上 Code Review，檢查是否可合併，或是有些工具可把改動拉回本地
- (9) 於 gitlab 的網頁上，Merge 
- (10) 清理 Gitlab 和本地中的 unnecessary branch

![image](https://hackmd.io/_uploads/rJR9YTV-0.png)

### **(6) GIT FLOW**
這裡參考 [A-successful-git-branching-model](https://nvie.com/posts/a-successful-git-branching-model/)，節錄一些要點

- 相比於其他 version control 工具，git 中的 merging/branching 都十分 cheap 且 simple
- Git 本質去中心化，但可以中心化式管理
- 兩條主分支 (Master, Develop)
    - **Master** : 處於 **production-ready** 的狀態，只有在完成 release 與 hotfix 後，才會變動
    - **Develop** : 開發用主線，所有對於需求的新增、修改、測試都在此分支。開發完成後才會用 release 與 master 進行同步
- 三條支援分支 (Feature, Release, Hotfix)
    - Feature : 由 develop 分離，一次開發一個功能，完成功能開發後就會 merge 回 develop
    - Release : 當預定的功能都開發完成時，會開一條前綴為 release 的分支，完成後 merge 回 master 與 develop
    - HotFix : 當交付的軟體回報有問題時，會開一條前綴為 hotfix 的分支，完成後 merge 回 master 與 develop
  ![image](https://hackmd.io/_uploads/ByopFpEbA.png)


### GIT 和 Perforce 的比較

我目前所在的公司，是使用 perforce 作為版本控制工具，真心覺得不是很好用。一樣為版本控制工具，perforce 是 1995 年被創立的，而 GIT 是 2005 年首度發布。兩套工具的社群和使用人數有非常顯著的不同，特別值得一提的是 perforce 只有商業版，一般使用者是沒辦法在自己的環境去練習 perforce 的操作的，而 GIT 有以 GNU GPL 開源授權，光是這一點就很好去做選擇了。

底下列出一些使用 perforce 上的痛點
- Perforce 沒有 branch 來多工處理不同任務
    - 若要同時處理不同 issue，需要同時開多個目錄，sync sandbox 回本地 (類似 git clone)
    - 浪費 disk 空間，也浪費 syncing 的時間，開發上也不容易切換
- Perforce 的 ChangeList 是以檔案作為單位
    - 一次 checkin/code review，只能以檔案為單位，看改動前後的差異
    - 不支援在一次 code review 中，用 commit message 把改動拆成更小單位
    - 不支援在一次 code review 中，對一個檔案 partial commit
- 同一個 sandbox 只能 shelve 一個改動
    - git 能夠於本地端暫時打包成 commit
    - git stash 可以存多個改動
- 對任何檔案操作前，都需要事先 "p4 add/edit/delete <path_to_file>"