## 前言
這篇文章主要紀錄我學習git的過程及分享,會介紹Git基本概念還有本地端基本指令的操作,主要是面向初次使用git的使用者
> 以下內容主要參考chatgpt、bard、bing回答進行修改
## Git 介紹
Git 是一種分散式版本控制系統,用於跟蹤文件和文件夾的更改。它允許用戶創建、提交和推送更改到遠程存儲庫(remote repository),或者直接在本地工作。
### 版本控制的原理
版本控制是一種記錄文件歷史狀態的方法,可以讓用戶回溯、比較和還原不同版本的文件。版本控制系統可以分為集中式和分散式兩種類型。

>圖片出處:[Scriptcrunch](https://scriptcrunch.com/295/)
#### GIT分散式版本控制
- 分散式版本控制系統,如 Git,是指每個用戶都有一個完整的文件和歷史記錄的副本,稱為本地倉庫(repository)。
- 用戶可以在本地進行各種操作,如創建、提交、分支、合併等,而不需要聯網
- 用戶還可以與其他用戶的倉庫進行同步,通過推送(push)和拉取(pull)操作來交換修改
- 分散式版本控制系統的優點是速度快,支持離線工作,分支管理靈活,缺點是學習曲線較陡,命令較多,衝突解決較複雜
#### SUBVERSION(SVN)集中式版本控制
- 集中式版本控制系統,如 SVN,是指所有的文件和歷史記錄都存儲在一個中央服務器上,用戶需要聯網才能訪問和操作
- 用戶可以從服務器上簽出(checkout)最新的文件,或者提交(commit)自己的修改到服務器上
- 集中式版本控制系統的優點是管理簡單,權限控制容易,缺點是依賴於網絡和服務器的可用性,且不支持本地操作
#### Git(分散式) vs SVN(集中式)
Git 和 SVN 都是目前最流行的兩種版本控制系統,它們各有優缺點,適用於不同的場景和需求。以下是它們的一些主要功能的差異和比較:
| 特點 | Git | SVN |
|--------------------------------|----------------------------|---------------------------------|
| 存儲方式 | 分散式 | 集中式 |
| 文件和歷史記錄存儲位置 | 本地倉庫 | 中央服務器 |
| 存儲依賴性 | 不需要聯網 | 需要聯網 |
| 操作 | 創建、提交、分支、合併等 | 簽出、提交 |
| 本地操作 | 支持本地操作 | 不支持本地操作 |
| 管理簡單性 | 學習曲線較陡 | 管理簡單 |
| 權限控制 | 相對複雜 | 容易 |
| 離線工作 | 支持離線工作 | 不支持離線工作 |
| 分支管理 | 靈活且強大的分支管理 | 較簡單且較受限制 |
| 衝突解決 | 較複雜 | 較簡單 |
| 速度 | 快速 | 一般速度 |
### Git 的重要性
- 它提供了可靠的版本控制,開發人員可以使用 Git 來跟踪他們的代碼更改。這樣,如果他們不小心刪除了或修改了某些代碼,他們可以輕鬆回滾到以前的版本。
- 它支持多人協作,團隊可以使用 Git 來協作開發軟體。這樣,每個開發人員都可以在自己的本地工作,然後將更改推送到中央存儲庫。
- 可移植性強,可以與不同的遠端儲存庫協作,像是GitHub、GitLab等。
---
## Git指令介紹
### `init`
- `init`是Git版本控制系統中的一個重要指令,它用於初始化一個全新的Git存儲庫(repository)
- 當您想要開始使用Git來跟蹤和管理代碼版本時,通常需要在項目目錄中運行git init來創建一個新的Git存儲庫
```git=
git init //初始化新的Git存儲庫
```
- 執行前 :

- 執行後 :

---
### `config`
- `config` 指令用於設定Git的配置選項,這些設定控制Git的各種方面,包括用戶信息、顏色顯示、命令別名等
- Git配置有三種作用範圍:本地(local)、全域(global)和系統(system)
- 不同的作用範圍影響Git配置的適用範圍
#### 本地(Local)配置
- 作用範圍:僅適用於當前倉庫。
- 使用場景:當你想為特定的倉庫設定特殊的配置,例如特定的用戶名或電子郵件地址。
- 設定方式:在Git倉庫目錄中使用 git config --local。
- 配置文件位置:.git/config 於當前倉庫目錄中。
#### 全域(Global)配置
- 作用範圍:適用於用戶的所有倉庫。
- 使用場景:當你想設定通用於所有倉庫的配置,如用戶名和電子郵件。
- 設定方式:使用 git config --global。
- 配置文件位置:一般位於用戶主目錄下的 .gitconfig 或 .config/git/config 文件。
#### 系統(System)配置
- 作用範圍:適用於系統中所有用戶的所有倉庫。
- 使用場景:通常由系統管理員進行設定,用於設定整個系統級別的Git配置。
- 設定方式:使用 git config --system。
- 配置文件位置:依賴於Git安裝的位置,通常是 /etc/gitconfig。
#### 使用範例
- 設定本地用戶名:`git config --local user.name "Your Name"`
- 設定全域電子郵件:`git config --global user.email "your_email@example.com"`
- 設定系統級的核查模式:`git config --system core.fsckObjects true`
- 透過 `git config --<local or global or system> --list` 可以查看不同範圍的配置
#### 注意事項:
- 如果同一設定在多個作用範圍都存在,Git會按照以下優先順序選擇:本地 > 全域 > 系統
- 對於沒有管理員權限的普通用戶,建議主要使用本地和全域配置
- 修改系統配置需要管理員權限,並會影響到系統上所有用戶的Git操作
---
### `status`
- `status` 指令是 Git 中一個非常常用和重要的指令,它提供了關於當前倉庫狀態的詳細信息
- 這個指令主要用於顯示工作目錄和暫存區的狀態,幫助用戶了解哪些更改已經被暫存(staged)、哪些更改還沒有被暫存,以及哪些文件還沒有被Git追蹤
#### 使用 git status 的主要特點:
1. 未暫存的更改(Unstaged Changes):
- 這部分顯示自上次提交(commit)後,工作目錄中已修改但尚未暫存的文件。
- 這些更改需要使用 git add 指令來暫存。
2. 暫存的更改(Staged Changes):
- 這部分顯示已經使用 git add 暫存,但還未提交的更改。
- 這些更改將會被包含在下一次提交中。
3. 未追蹤的文件(Untracked Files):
- 這些是Git之前沒有紀錄的新文件。
- 可以使用 git add 將它們加入暫存區,使其成為被追蹤的文件。
4. 分支信息:
- git status 通常會在開頭顯示當前所在的分支和它與上游分支(通常是遠端分支)的差異,如"ahead"或"behind"。
5. 建議的Git指令:
- 在某些情況下,git status 會提供一些有用的指令建議,比如如何暫存更改或如何回退到上一個提交狀態。
#### 情境演練:
1. `git init` 後新增一個file1.txt
- `git status`因為file1.txt 還沒加入到暫存區,會顯示未追蹤的文件(Untracked Files)

2. 將file1.txt 透過`git add`加入到暫存區
- `git status`file1.txt 加入到暫存區,會被包含在下次`commit`

3. `git commit`提供後,新增file2.txt,同時修改file1.txt 內容
- 修改的file1.txt 會變成modified且Unstaged Changes狀態
- 新增的file2.txt 則是untracked file

4. `git add`兩個檔案會修改成Staged Changes
- file1.txt 上次提交時已存在所以是modified
- file2.txt 上次提交時不存在所以是new

---
### `add`
- `add` 是 Git 版本控制系統中一個核心的指令,用於將修改過的文件添加到暫存區(staging area
- 這是進行提交(commit)之前的一個重要步驟,因為只有暫存的更改才會被包含在下一次提交中。
#### 主要功能
1. 暫存修改:
- 將工作目錄中的修改(包括新文件和修改過的文件)添加到暫存區。
- 只有暫存的更改才會被下一次的 git commit 捕捉。
2. 更新暫存區:
- 如果一個文件已經被追蹤(之前已經提交過),git add 可以用來將新的修改添加到暫存區。
3. 開始追蹤新文件:
- 對於新建的未追蹤文件,使用 git add 可以開始追蹤這個文件。
#### 使用方式
1. 將單個文件添加到暫存區:
```git=
git add file1.txt //這將file1.txt中的修改添加到暫存區中,使它們準備好被提交
```

2. 將多個文件添加到暫存區:
```git=
git add file1.txt file2.txt //這將file1.txt中的修改添加到暫存區中,使它們準備好被提交
```
3. 將目錄中的所有修改添加到暫存區:
```git=
git add <目錄名> //這會將指定目錄下的所有更改(包括子目錄)添加到暫存區。
```
4. 將所有修改添加到暫存區:
```git=
git add . //使用.代表當前目錄,這將將所有修改和新增的文件添加到暫存區。
```
5. 互動式添加(Interactive Add):
```git=
git add -i //這個模式允許您一個接一個地檢視文件更改,並選擇是否將它們添加到暫存區。
```
#### 注意事項
- `git add` 不會影響倉庫中的提交歷史,它僅將更改添加到暫存區
- 使用 `git add` 之後,如果對暫存的文件進行了進一步的修改,需要再次執行 `git add` 來更新暫存區的內容
- 對於已被刪除的文件,使用 `git add` 可以將這個刪除操作添加到暫存區
---
### `rm`
- `git rm` 是 Git 中的一個命令,用於從工作目錄和索引(暫存區)中移除文件
- 這個命令不僅從 Git 的追蹤列表中刪除文件,還會從工作目錄中刪除該文件,使其不再出現在未來的提交中。
#### 主要功能
1. 從工作目錄和暫存區移除文件:
- 使用 `git rm` 會同時從當前的工作目錄和暫存區中刪除指定的文件。
2. 準備文件刪除的提交:
- 這個命令將刪除操作暫存,準備將這些更改包含在下一次提交中。
- 例如:使用 `git rm` 命令移除工作目錄中的文件之後,將這次刪除操作暫存(staged)以便進行下一次的提交(commit),用於記錄和追蹤對文件系統所做的更改。
3. 停止追蹤文件,但保留在工作目錄:
- 使用 `--cached` 選項可以讓 Git 停止追蹤該文件,但文件仍然保留在您的工作目錄中。
#### 使用方式
1. 刪除單個文件:
```git=
git rm <文件名> //例如,git rm file.txt 會從 Git 中移除 file.txt 並將此更改暫存。
```
- 單純使用`rm` 刪除檔案

- `rm` 像`add`一樣可以包含在commit

2. 刪除多個文件:
```git=
git rm <文件1> <文件2> ... //這樣可以一次刪除多個文件。
```
3. 使用萬用字符刪除多個文件:
```git=
git rm '*.txt' //這會刪除所有以 .txt 結尾的文件。
```
5. 僅從 Git 中移除,但保留文件在工作目錄中:
```git=
git rm --cached <文件名> //這個命令會從 Git 的追蹤列表中移除文件,但不會從工作目錄中刪除它。
```
#### 注意事項:
- 使用 `git rm` 前確保您確實想要刪除這些文件。一旦提交後,這些文件就會從 Git 歷史中刪除。
- 如果只是想從暫存區移除更改(而不是從工作目錄中刪除文件),應該使用 `git reset`。
- 在執行 `git rm` 之後,需要進行提交操作以將這些更改永久記錄到版本歷史中。
- `git rm` 是一個強大的工具,適用於當您需要從版本控制中完全移除文件時。正確使用此命令可以幫助保持您的倉庫乾淨和有序。
---
### `commit`
`git commit` 是用於將更改從暫存區提交到 Git 倉庫中的命令。這個步驟記錄了版本歷史的快照,並允許開發者為所做的更改提供描述性消息。
#### 基本用法
```git=
git commit -m "提交信息"
```
- `-m "提交信息"`: 提交時附帶的消息,用於解釋這次提交所包含的更改。
#### 常用選項
- **添加所有更改(包括新文件和刪除的文件)到提交中**
```git=
git commit -a -m "提交信息"
```
`-a` 選項自動將所有已追蹤的修改和刪除的文件添加到暫存區。
- **使用文本編輯器撰寫提交信息**
```git=
git commit
```
如果不使用 `-m` 選項,Git 將打開配置的文本編輯器,讓您在那裡撰寫提交消息。
- **更改上一次提交**
```git=
git commit --amend
```
如果需要更正最後一次提交的消息或更改,可以使用 `--amend` 選項。
- **跳過暫存區**
```git=
git commit -am "提交信息"
```
結合 `-a` 和 `-m` 選項可以跳過 `git add` 步驟,直接將所有已追蹤的文件更改提交。
#### 注意事項
- 前提:在使用 `git commit` 之前,需要先使用 `git add` 將想要提交的更改加入暫存區。
- 提交消息的重要性:良好的提交消息對於瞭解項目歷史和更改原因至關重要。
#### 操作範例
1. 這個例子先將 `file1.txt` 添加到暫存區,然後進行提交,並為這次提交提供了一個簡短的描述
2. 接著在目錄中新增一個 `file2.txt`檔案並添加到暫存區,然後進行提交,並為這次提交提供了一個簡短的描述
```git=
git add file1.txt
git commit -m "修正了 file1.txt 中的錯誤"
git add file2.txt
git commit -m "新增 file2.txt"
```
- file1.txt 提交前

- file2.txt 提交後,會新增兩個commit object

### branch
- `branch` 指令在 Git 中用於管理分支,包括創建、列出、重命名和刪除分支
- 分支使得同時在多個不同的功能上工作變得更加容易。
#### 常用選項
- 創建分支
```git=
git branch <分支名稱>
```
創建一個新的分支,但不會自動切換到該分支。
- 列出所有分支
```bash=
git branch
```
列出倉庫中的所有本地分支。當前分支會以星號 (*) 標記。
- 切換分支
```bash=
git checkout <分支名稱>
```
切換到指定的分支。
- 創建並切換到新分支
```bash=
git checkout -b <分支名稱>
```
創建一個新的分支並立即切換到該分支。
#### 重命名分支
- 當前分支重命名
```bash=
git branch -m <新分支名稱>
```
- 非當前分支重命名
```bash=
git branch -m <舊分支名稱> <新分支名稱>
```
#### 刪除分支
- 刪除本地分支
```bash=
git branch -d <分支名稱>
```
`-d` 選項用於安全刪除,只能刪除已合併到上游的分支。
- 強制刪除本地分支
```bash=
git branch -D <分支名稱>
```
`-D` 是 `-d` 的強制版本,可以刪除尚未合併的分支。
#### 注意事項
- 在創建或切換分支之前,最好保證工作目錄是乾淨的(沒有未提交的更改)。
- 刪除分支前,確保不再需要該分支上的更改,特別是未合併的分支。
#### 範例
- 目標 : 建立一個bugfix branch,並checktout到該branch 進行提交
- 初始狀態

- `git branch bugFix` :創建名為 `bugFix` 的新分支

- `git checkout bugFix` : checktout到 `bugFix` 分支

- `git checkout bugFix` : 在 `bugFix` 分支進行提交

### `merge`
- `git merge` 指令用於將兩個或多個開發歷史合併在一起。
- 這通常用於將一個分支的更改合併到另一個分支中。
#### 基本合併
```bash=
git merge <分支名稱>
```
- 將指定分支的更改合併到當前分支。
- 需要先切換到目標分支,然後執行此命令。
#### 合併時解決衝突
- 如果合併過程中出現衝突,Git 會標記出衝突的文件。
- 需要手動解決這些衝突,然後繼續合併過程。
#### 合併策略
- **Fast-forward 合併**:
- 如果被合併分支是目標分支的直接後續,Git 將進行快進合併,簡單地更新指針。
- **Three-way 合併**:
- 如果分支歷史有所偏離,Git 將創建一個新的合併提交。
#### 避免快進合併
```bash=
git merge --no-ff <分支名稱>
```
- 使用 `--no-ff` 選項可以強制創建一個新的合併提交,即使快進合併是可能的。
#### 合併提交
- 完成合併後,如果進行了 three-way 合併,Git 將創建一個新的合併提交。
#### 注意事項
- 在合併之前,確保您的工作目錄是乾淨的(無未提交的更改)。
- 在合併重要分支之前,建議進行備份。
- 解決衝突時,謹慎操作,以免丟失重要更改。
#### 示範
1. 建立新的 `branch`,叫做 `bugFix`
2. 用 `git checkout bugFix` 切換到 `bugFix branch`並進行修改後`commit`

3. `git checkout` 切換回 `main branch`並進行修改後`commit`

4. 執行`git merge bugFix` 將 `bugFix branch` `merge` 到 `main branch`
5. 這時會產生新的`commit object` `C4`,`main branch` 會指向`C4` `bugFix` 仍然指向`C2`,執行`git branch -f master C4`可以讓`bugFix`指向`C4`

### `rebase`
- `git rebase` 是 Git 中的一個強大工具,用於重寫提交歷史,通過將一系列提交從一個分支上轉移到另一個基底分支上
- `rebase` 是 `merge branch` 的第二種方法`rebase` 就是取出一連串的 `commit`,"複製"它們,然後把它們接在別的地方
- `rebase` 的優點是可以建立更線性的 `commit history`, repo 中的 commit log 或者是 commit history 會更加簡潔好看
#### 基本重排
```bash=
git rebase <基底分支>
```
- 將當前分支的更改重新應用於指定的 `<基底分支>` 上。
- 這會使得當前分支的提交在基底分支的最新提交之上。
#### 互動式重排
```bash=
git rebase -i <基底分支>
```
- 啟動互動模式,允許用戶選擇每個提交要進行的操作(如重排、刪除、修改等)。
#### 使用 `rebase` 進行衝突解決
- 在 `rebase` 過程中,如果遇到衝突,需要手動解決這些衝突,然後繼續重排過程。
#### 中斷重排
```bash=
git rebase --abort
```
- 如果重排過程中遇到問題,可以使用此命令中斷重排。
#### 完成重排
```bash=
git rebase --continue
```
- 在解決衝突後,使用此命令繼續重排過程。
#### 注意事項
- `git rebase` 會改變提交歷史,應謹慎使用,尤其是對於已經推送到共享倉庫的分支。
- 在進行重排之前,確保您的工作目錄是乾淨的(無未提交的更改)。
- 不建議在公共分支上使用 `rebase`,因為它會重寫歷史。
#### 示範
1. 建立 `bugFix branch`並`commit`
2. `checktout`回 `main branch` 再 `commit`

4. 再次`checktout`到 `bugFix branch`,接著執行`git rebase main` 讓這個 `bugFix branch` 延續到 `main branch` 上

5. `checktout`到 `main branch`,接著執行`git rebase bugFix`,會執行`fast-forward merge`

## 總結
這篇文章簡單介紹了分散式與集中式版本控制系統的區別,還有git 在本地端的指令介紹與實際操作,git 還有遠程倉庫的操作與更多進階功能,礙於篇幅有限就待之後文章繼續介紹
## 參考資料
- https://learngitbranching.js.org/?locale=zh_TW
- https://github.com/k88hudson/git-flight-rules/blob/master/README_zh-TW.md
- https://scriptcrunch.com/295/
- chatgpt
- bard
- bing ai