---
# System prepended metadata

title: Git
tags: [Git]

---

# 前言
何謂版本控制系統？
版本控制系統(Version Control System)，主要用於控制與管理程式碼的版本。


為什麼需要版本控制系統?
舉例來說，以往沒有使用版本控制系統時，每次的程式異動，只能以複製專案目錄的方式進行程式備份，但長期下來整個儲存區會過於雜亂，且不易比對版本間的程式碼差異，也不便於多人同時進行開發，整體效率低下且過度消耗儲存空間。
![folder (1)](https://hackmd.io/_uploads/r1I18AXpC.jpg)


而『版本控制系統』是以快照或差異方式儲存不同版本間的差異，除了不會過度消耗儲存空間外，也可以有效紀錄每次新增、修改、刪除了哪幾行程式碼，進行版本提交時，也能註明修改目的、負責的工程師等資訊。

![git save (1)](https://hackmd.io/_uploads/SkprHCX6C.jpg)

## 優點
1. 方便管理程式碼版本，避免傳統雜亂的複製專案目錄控制版本。
2. 可紀錄每次程式異動，是由哪位工程師修改的程式碼
3. 同時接收多個需求時，可建立不同的分支(branch)，讓多個工程師同時進行作業，加速整個團隊的開發效率
4. 可快速的比對兩個版本的程式碼差異

## 缺點
1. 如遇cmd指令較多的版本控制系統，沒有使用其他GUI輔助的話，較難上手

</br>

# Git
Git是一種分散式的版本控制系統，除了呼叫傳統的遠端儲存庫(Remote Repository)外，分散式架構讓每個工程師可以在自己的PC或NB中，建立本機儲存庫(Local Repository)，方便工程師在沒有網路連線的時候，或是多人同時開發時，可以不受外在因素的影響持續進行開發。

![Local Repository (1)](https://hackmd.io/_uploads/HJTepu4nA.jpg)

# Storage(儲存區）
Git的儲存大致可區分為<u>Working Directory</u>、<u>Staging Area</u>、<u>Local Repository</u>、<u>Remote Repository</u>等四個儲存區。

* Working Directory(工作目錄)
工程師平時開發的工作目錄
* Staging Area(暫存區)
暫存欲`commit`檔案的儲存區
* Local Repository(本機儲存庫)
每位工程師NB/PC所儲存的個人儲存庫(.git資料夾)
* Remote Repository(遠端儲存庫)
團隊共用儲存於伺服器上的遠端儲存庫，如：GitHub、GitLab等

![notion-第 2 页2](https://hackmd.io/_uploads/rkIX-X3pR.jpg)



# Git 常用指令
git的所有指令數量比較多，而實際常用的指令大概只佔其中一部分，簡單列出一些普遍常用的指令

* `git init`
初始化Git

* `git remote -v`
查詢所有已配置的Remote Repository及其URL

以下<remoteName>皆為別名，方便使用者操作，非實際Remote Name

* `git remote add <remoteName> <remoteUrl>`
新增Remote Repository及其URL

* `git clone <remoteUrl>`
將Remote Repository的程式<span style="font-weight: bold;color: red;">複製</span>到Local Repository以及Working Directory。
Clone的Remote Repository，預設會命名為`origin`

* `git pull <remoteName> <branchName>`
將Remote Repository的程式<span style="font-weight: bold;color: red;">同步</span>到Local Repository以及Working Directory。
實質為 `git fetch` + `git merge`，`merge`方式為`fast-forward`

* `git fetch <remoteName> <branchName>`
將Remote Repository下載至Local Repository

* `git checkout <branchName>` or `git checkout <commit-hash>`
將終端機切換至指定Branch或Version

* `git add <folderPath>` or `git add <fileName>`
將指定的資料夾或檔案，新增至Staging Area。
通常使用`git add .`將所有異動新增至暫存(含子目錄），舊版Git使用`git add --all`

* `git commit -m <message>`
將指定的異動檔案，提交至Local Repository

* `git push <remoteName> <branchName>`
將指定的異動檔案，推送至伺服器上的Remote Repository

* `git status`
查看目前異動的檔案狀態

* `git log`
查看歷史的異動日誌

* `git reset`
復原`git commit`的提交，可分為<u>mixed</u>、<u>soft</u>、<u>hard</u>三種模式。
    * <p>--mixed</p>
        預設，刪除暫存區的檔案，工作目錄的檔案不變。原先Commit的檔案會復原至工作目錄。
    * <p>--soft</p>
        暫存區及工作區的檔案皆不刪除，原先Commit的檔案會復原至暫存區。
    * <p>--hard</p>
        暫存區及工作區的檔案皆刪除。
            
![notion](https://hackmd.io/_uploads/SJYz6p9hR.jpg)    
* `git branch`
可進行分支相關的操作，如：新增、刪除或查詢分支等。

* `git merge <branchName>`
將指定branch合併到當前分支。

`git branch`與`git merge`指令的基本操作，可以參考[這篇文章](https://hackmd.io/@Lion-Le/rJOHUpXTR)
</br>
            
* `git rebase <branchName>`
以類似「移植」的方式合併分支，比較像直接修改分支的歷史提交紀錄。

`git merge`與`git rebase`的差異，可以參考[這篇文章](https://hackmd.io/@Lion-Le/BJCUrBnTC)


</br>
            
若某功能開發到一半時，需緊急修復線上Bug問題，可以使用`git stash`方式保存目前所有的異動內容。
* `git stash`
保存目前暫存目錄(Staging Area)的所有檔案
* `git stash list`
查看已`git stash`的清單
* `git stash pop`
取出保存的檔案
* `git stash drop`
清除最近一次的`git stash`
* `git stash clear`
清除所有的`git stash` 


</br>
            
如果覺得指令不易熟記或操作不方便，也可以參考Git官方推薦的一系列GUI(圖形化使用者介面)軟體
(https://git-scm.com/downloads/guis)
如：GitHub Desktop、SourceTree等
![截圖 2024-09-12 晚上7.45.05](https://hackmd.io/_uploads/r1pPVUea0.png)
       
       
       
## 指令Q&A
Q. `git fetch`與`git pull`兩者差在哪?
A. `git fetch`是將Remote Repository的程式下載回Local Repoesitory，而 `git pull` 則是 `git fetch` + `git merge`，以`fast-forward`的方式進行`merge`。
如果不想以fast-forward的方式merge，可以先執行`git fetch`指令，再自行處理merge。
    </br>

Q. `git clone`與`git pull`兩者差在哪?
A. `git clone`主要用於本機第一次建立該專案的時候．若本機已有該專案，只是需要同步成Remote Repository的版本，則使用`git pull`進行同步。
</br>
    
Q. 要推送至Remote Repository時，發現Commit太多次，想紀錄乾淨點，可以如何處理?
A. `git reset`拆掉重新commit，或是用`git rebase`合併
</br>
    
Q. 一般的merge與使用`fast-forward`的merge差在哪?
A. `fast-forward`主要用於指定合併分支是基於當前分支出去的最新提交，並且當前分支也沒有新的提交時，可使用`fast-forward`合併，合併後歷史為線性呈現。
一般方式的合併用於當前分支也有新版本時，會產生一個新的合併提交，合併後仍保留原合併分支的結構，詳細可參考[這篇文章](https://hackmd.io/@Lion-Le/BJCUrBnTC)。
</br>

# File LifeCycle(檔案生命週期)
依據上述指令，可將異動的檔案推送至不同階段的儲存區，而不同階段的檔案，也各有不同的狀態，使用`git status`指令可以確認目錄中每個檔案的狀態，主要分為<u>Changes to be committed</u>、<u>Changes not staged for commit</u>、<u>Untracked files</u>等三種狀態。

![file lifecycle (2)](https://hackmd.io/_uploads/SyaeVkVpC.jpg)


# 總結
簡單統整了Git的概念、常用指令、儲存流程及檔案生命週期等。

與其他版本控制系統相比，如:TFS、CVS、SVN等。Git最大優勢在於可以很容易的與GitHub、GitLab等系統整合，其過程不需使用其他任何工具。

同時Git的分散式系統架構，可以在本機環境保存一份完整的Repository，並且涵蓋所有歷史版本，讓工程師可以自由在本機進行開發、暫存(commit/stash)、分支或合併等操作，不受任何外在因素的影響。

若使用傳統的集中式架構，則需仰賴Remote Server，本機環境通常只會保留當前的檔案狀態，而歷史版本則保存在Remote Server上，在無法連線至Remote Server的情況下，會大幅度地影響到工程師的開發。

並且集中式的系統架構，通常只能在Working Directory保有一個異動的版本，無法像集中式的系統架構，可以在本機環境做到保有多個異動版本。
</br>
    
# 參考資料
https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F
          
https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
            
https://git-scm.com/docs/git-stash
    
https://gitbook.tw/chapters/using-git/working-staging-and-repository
            
https://gitbook.tw/chapters/branch/merge-with-rebase
</br>
    
>[!Tip] 
文章內容皆為個人整理的觀點，以及整理後的個人想法，如內容有錯誤或疑慮的部分，歡迎提出討論，收到後會盡快修正！
