<h1>Git筆記--Git介紹</h1>
Git 是一個分散式版本控制系統,用於追蹤專案中所有檔案的歷史變動。它不是只備份整個檔案,而是記錄從一個版本到下一個版本,哪些地方做了什麼修改。
Git 可以做什麼?
- 記錄歷史:你可以看到誰、什麼時候、改了什麼。
- 版本切換:可以回到之前的版本,或切出分支做實驗。
- 多人協作:多人可以同時修改專案,Git 會幫你合併或提醒衝突。
- 備份與同步:把程式碼推到遠端(GitHub、GitLab 等)就像雲端備份。
<code>Repository</code> 就像你的專案「倉庫」,<code>Commit</code> 是每次「儲存記錄」,<code>Branch</code>是「分線開發」,<code>Merge</code> 是「整合成果」,<code>Remote</code> 是「雲端書櫃」,而 <code>Pull</code> / <code>Push</code> 則是「下載與上傳最新版本」。
---
一、基礎概念
---
<details><summary>1. <code>Repository</code>(Repo,儲存庫): 指專案的版本控制資料夾,Git 會在其中追蹤所有檔案的新增、修改與刪除狀態。</summary>
<div>
---
分為 本地儲存庫(存在於開發者的電腦中)與 遠端儲存庫(通常位於 GitHub、GitLab 等伺服器上)。
- <code>Local Repository</code>:在你電腦本地。
- 在本機使用 <code>git init</code> 創建後,Git 會生成一個隱藏資料夾:<code>.git/</code>
- <code>Remote Repository</code>:在伺服器(如 GitHub/GitLab)。
</div>
</details>
<details><summary>2. <code>Working Directory</code>(工作區) / <code>Staging Area</code>(暫存區) / <code>Repository</code>(版本庫)</summary>
<div>
---
<b><u>Git 的工作流程本質上是一場「四階段提交」,可分為四個區域、步驟: </u></b>:
運作流程: <code>Working Directory</code> → <code>Staging Area</code> → <code>Local Repository</code> → <code>Remote Repository</code>
| 區域 | 中文名稱 | 功能定位 | 操作指令 |
| --------------------- | ---- | ----------------- | --------------------- |
| <b>Working Directory</b> | 工作區 | 你實際編輯的檔案所在處(相當於草稿區)| <code>git add</code> 將修改送往暫存區 |
| <b>Staging Area</b> | 暫存區 | 暫存已準備提交的變更(相當於審稿區)| <code>git commit</code> 將內容提交成版本 |
| <b>Repository</b> | 版本庫 | 已正式提交的版本記錄(相當於出版的正式版本)| <code>git push</code> 推送到遠端版本庫(Remote Repository) |
<details style="border-left: 4px solid #1565C0;background-color: #0D1B2A;padding: 12px 16px;margin: 10px 0;border-radius: 4px;font-family: Arial, sans-serif;"><summary><b>Working Directory</b>、<b>Staging Area</b>和<b>Repository</b>在AndroidStudio中的對應位置</summary>
<div>
---
<h3>Working Directory(工作區)</h3>
平常我們編輯程式碼的地方,包含個目錄都是

---
<h3>Staging Area(暫存區)</h3>
在 Android Studio 中,Staging Area (暫存區) 並非一個獨立的實體視窗,而是作為「審核點」存在,其整合了Git操作、暫存區、暫存取管理的概念為一個視窗。
- 當開發者開啟 Commit 工具視窗時,即是在管理暫存區。
- 當在檔案上點擊右鍵選擇 Git → Add,或在 Commit 介面中勾選該檔案時,就是將它移入暫存區。
| 將檔案置入站存區的管理之下 | 操作、管理暫存區 |
| -------- | -------- |
|  | |
---
<h3>Repository(版本庫)</h3>
在 Android Studio 中看不到,但它是整個 Git 系統的核心,他在專案根目錄下隱藏的 <code>.git</code> 資料夾中,儲存所有提交(Commit)的歷史紀錄。

</div>
</details>
</div>
</details>
<details><summary>3. <code>Commit</code>(提交): 表示將目前的程式碼變更記錄至本地的版本歷史中,形成一個具備訊息與時間戳記的「版本快照」。</summary>
<div>
---
>[!Note][Git筆記--snapshot(快照)](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/git_snapshot)
它會把「暫存區(Staging Area)」中所有已標記的檔案狀態,存進「本地版本庫(Local Repository)」,可視為專案的「儲存點」,開發者可隨時回溯至該狀態。
```bash=
git commit -m "新增使用者登入功能"
```
</div>
</details>
<details><summary>4. <code>HEAD</code>: 代表了 Git 的「現在的位置的指標」(也就是正在查看或操作的那個版本)</summary>
<div>
---
在 Git 中有許多 <code>commit</code>(每一個都有唯一的 SHA-1 編號)。而 <code>HEAD</code> 就像一個書籤,它會指向某一個 <code>branch</code> 的最新 <code>commit</code>。 ==<code>HEAD</code> 類似於在地圖上的定位點,是 Git 用來記住「你現在在哪裡」的機制==。
- <code>HEAD</code> 指向某個分支: 代表「我正在這個分支上工作」---- <code>HEAD</code> 的通常狀態
- <code>HEAD</code> 直接指向某個 <code>commit</code>: 代表「我正在查看歷史版本」---- ==<code>Detached HEAD</code> 狀態==
>[!Note][Detached HEAD](https://titangene.github.io/article/git-detached-head.html) 狀態
>- 當 <code>HEAD</code> 直接指向某個過去的 <code>commit</code>(而不是指向分支)時,就進入了 <code>Detached HEAD</code> 狀態,此時我們可以查看、測試舊程式碼。
>- Git會發出警告 <code>You are in 'detached HEAD' state</code>
>- 這時候像是「時光旅行」到過去查看程式碼,但要小心,如果在這狀態下做修改又沒建立分支,做出的修改可能會「飄在空中」找不回來。
</div>
</details>
---
二、分支與合併
---
<details><summary>1. <code>Branch</code>(分支): 是從主要開發線(例如 <code>main</code> 或 <code>master</code>)分出的一條獨立開發軌跡,用於平行進行新功能開發、修復或實驗性變更。</summary>
<div>
---
分支是 Git 的強大核心功能,可以同時開多條分支,各自進行不同功能的開發,互不干擾,甚至可以在開發完成後再合併回主線。

```bash=
git checkout -b feature-branch
```
<details style="border-left: 4px solid #1565C0;background-color: #0D1B2A;padding: 12px 16px;margin: 10px 0;border-radius: 4px;font-family: Arial, sans-serif;"><summary><code>Feature Branch</code>(功能分支): 是專門用來開發「新功能」的分支,通常從主分支(main 或 master)切出來,</summary>
<div>
---
<code>Feature Branch</code> 通常只專注於某個功能的開發,完成後再合併回主分支。是團隊開發中最常用的分支策略之一。


</div>
</details>
</div>
</details>
<details><summary>2. <code>Switch</code>(切換): 切換目前所在的分支,或建立並切換到新分支。</summary>
<div>
---
<code>Switch</code>是「讓你移動 <code>HEAD</code>(目前工作指標)」到不同分支的指令。這樣就可以在不同開發線之間自由切換。

```bash=
git switch branch-name # 切換
git switch -c new-branch # 建立並切換
```
當使用Switch切換分支時會:
- HEAD 指標移動: 從指向 <code>main</code> 改為指向 <code>feature-login</code>
- 工作目錄內容改變: 新增了 <code>LoginActivity.kt</code> 和 <code>LoginViewModel.kt</code>
- <code>HomeFragment.kt</code> 消失了: 因為這個檔案只存在於 <code>main</code> 分支
</div>
</details>
<details><summary>3. <code>Merge</code>(合併): 將某一個本地的分支(Branch)上的修改內容,整合到另一個本地的分支中</summary>
<div>
---
<code>Merge</code> 是一個本地的操作,會保留分支的合併紀錄,產生一個新的「合併節點」,其目的是整合多條開發線的成果,使專案保持一致的進度與版本狀態。
例如: 在一個功能分支(<code>feature</code> / <code>login</code>)開發完畢,想把它合併回主分支(<code>main</code>)。
這時你就會使用 <code>git merge</code>。

```bash=
git merge feature-branch
```
<details style="border-left: 4px solid #1565C0;background-color: #0D1B2A;padding: 12px 16px;margin: 10px 0;border-radius: 4px;font-family: Arial, sans-serif;"><summary>Merge 的三種結果: Fast-Forward Merge (快轉合併)、Three-Way Merge (三方合併)、Merge Conflict (合併衝突)</summary>
<div>
---
Fast-Forward Merge (快轉合併)
---
- 發生條件: <code>main</code> 分支在分出 <code>feature</code> 後,完全沒有新的提交
- 結果: <code>main</code> 指標直接「快轉」到 <code>feature</code> 的位置,不產生新提交

---
Three-Way Merge (三方合併)(最常見)
---
- 發生條件: <code>main</code> 和 <code>feature</code> 兩邊都有新提交,需要真正的合併。
- 邏輯: Git 會根據共同祖先(Base Commit)、目前分支(HEAD)、要合併進來的分支(Merge branch)的差異,自動合併檔案與程式碼
- 結果: 產生一個新的「合併提交」(Merge Commit),但若同一段程式在兩個分支中都有不同修改,或一方刪除了檔案、另一方仍修改了該檔案,Git 就無法自動決定哪個版本應保留,此時便會產生 Merge Conflict(合併衝突)

---
Merge Conflict (合併衝突)
---
- 發生條件: 兩個分支修改了同一檔案的同一位置,Git 無法自動決定
- 結果: 合併暫停,需要手動解決衝突後才能完成

</div>
</details>
</div>
</details>
<details><summary>4. <code>Rebase</code>(變基): 將一個分支的基底改到另一個分支上</summary>
<div>
---
會重新排列歷史,使提交紀錄看起來像是線性進行。簡單來說,就是本來在隊伍中間分叉出去,現在你把自己的整段紀錄「拔起來」,接到目前的隊伍末端。
- 和 <code>Fast-Forward Merge</code> 差在,<code>Fast-Forward Merge</code> 是你已經在隊伍末端,然後直接宣稱你就是新的隊頭。

```bash=
git rebase main
```
>[!Warning]不要對公共分支做 Rebase
>Rebase 會改寫提交,若已推送到遠端共享分支,可能造成協作問題
>所以應該只對你自己的 feature 分支做 Rebase
</div>
</details>
<details><summary>5. <code>cherry-pick</code>: 將另一個分支中已存在的單一或多個 <code>commit</code>,複製並套用到目前所在的分支並且生成新的 <code>commit</code>。</summary>
<div>
---
也就是拿一個特定的 <code>Commit</code>,複製到另一個分支,而不是整個分支的歷史


</div>
</details>
---
三、遠端與協作
---
<details><summary>1. <code>Clone</code> / <code>Fork</code>: 將專案複製到自己的 本地/遠端庫</summary>
<div>
---
<code>Clone</code>和<code>Fork</code>都是用來下載整個遠端倉庫(含所有分支、提交歷史)的功能,差別在於:
- <code>Clone</code>將遠端倉庫下載到本地,使用 <code>Clone</code> 的話:
- <code>Push</code>/<code>Pull</code> 的對象都會是原始的遠端儲存庫
- 想要 <code>Push</code> 的話會需要有額外的寫入權限才行
- <code>Fork</code>將遠端倉庫下載到自己帳戶下的遠端倉庫,使用 <code>Fork</code> 的話:
- <code>Push</code>/<code>Pull</code> 的對象都會是自己帳號下的那一個「遠端複製庫」
- 想要 <code>Push/Pull</code> 的話會不需要有額外的權限
<b><u>開源貢獻時,通常是這樣的順序</u></b>:
1. 使用 <code>Fork</code> 把專案複製到你自己的 GitHub 帳號。
↓
2. 使用 <code>Clone</code> 把這個自己 <code>Fork</code> 的版本下載到本地電腦。
↓
3. 修改、<code>Commit</code>、<code>Push</code> 後推回你的遠端倉庫(<code>fork</code> 出來的那個)。
↓
4. 發出 <code>Pull Request</code>,請原專案維護者審核你的修改。
</div>
</details>
<details><summary>2. <code>Pull</code>(拉取): 將檔案的更新同步至本地</summary>
<div>
---
從遠端儲存庫下載最新的變更至本地端,保持版本同步。我們可以將<code>pull</code>視為<code>fetch</code> + <code>merge</code>的組合。
- <code>git pull</code> 就像是快速便利的自動模式,而 <code>git fetch</code> + <code>git merge</code> 則是手動模式,讓你有機會在合併前先檢查。
- 和 <code>Clone</code> 容易搞混,<code>Clone</code>是第一次完整下載,而<code>pull</code>是有專案後的後續更新。
</div>
</details>
<details><summary>3. <code>Push</code>(推送): 將本地的變動上傳到遠端</summary>
<div>
---
將本地提交的變更上傳至遠端儲存庫。
<b><u>Push 的其他模式</u></b>:
- <code>git push</code>(預設模式): 檢查當前的 commit,是否包含該分支的最新內容,是否為最安全的推送模式。
- <code>--force</code>: 無條件進行推送並覆蓋,使用上相當危險。
- <code>--force-with-lease</code>: 檢查當前 commit 的目標分支狀態,是否與本地記錄的遠端狀態一致(有人更新過就會不一致),是才允許推送。推薦替代 <code>--force</code>。檢查的是「狀態的一致性」。
- <code>--set-upstream</code>(<code>-u</code>): 將本地分支與遠端分支進行「綁定」(Tracking),通常用於新分支第一次推送。
- 本地的 <code>feature</code> 分支和遠端的 <code>origin/feature</code> 分支在預設情況下只是「名字一樣」,彼此並沒有實質的連結。
- 一旦綁定成功,以後在這個分支執行 <code>git push</code> 或 <code>git pull</code>,你不需要再輸入 <code>origin</code> 或分支名稱,Git 自動知道要去哪裡抓資料。
- --tags: 推送本的的所有 Tag,預設的 Push 只會推送分支,要推送 Tag 的話需使用此模式。
- --all: 將本地的所有分支全都推送到 Remote,普通的 push 只會推送「當前的分支」,但使用此選項的話可以一次推送全部的分支。
- --delete: 刪除指定的遠端分支或 Tag。
- 原子推送,--atomic,確保多個分支同時推送成功。,只要有一個分支推送失敗,全部都會被取消(All or nothing)。
>[!Caution]<code>git push --force</code> 需慎用。
><code>git push --foece</code> 是強行推送模式,這是在告訴 <code>Remote</code>:「別管你那邊的紀錄了,聽我的,我說現在是什麼樣子,就是什麼樣子!」,==<code>Remote</code> 會被迫刪除那些 <code>Commit</code>,強行「往後」跳回到你指定的節點==。需要嘗試使用 <code>git reflog</code>。
>[!Warning]注意事項
>| 狀況 | 建議做法 |
>| ------------------ | ---------------------------- |
>| 若遠端更新比你早 | 先 `git pull` 同步後再 <code>push</code>,避免衝突。 |
>| 若出現 merge conflict | 手動解決衝突後再 <code>commit</code>。 |
>| 若要檢查遠端狀況不想合併 | 使用 `git fetch`。 |
</div>
</details>
<details><summary>4. <code>origin</code>、<code>main</code>、<code>origin/分支名</code></summary>
<div>
- <code>origin</code>: 代表「遠端儲存庫(<code>remote repository</code>)」的名稱
- <code>main</code>: 代表「本地端」目前本地倉庫中的主要分支(<code>branch</code>)
- <code>origin/分支名</code>: 代表「遠端追蹤分支(<code>remote-tracking branch</code>)」,也就是==代表本地端上,最後一次從遠端倉庫抓取(<code>fetch</code>)或同步(<code>pull</code>)後,遠端該分支的狀態快照==。
<b><u>例如</u></b>:
- <code>origin/main</code> 代表遠端追蹤分支最後一次同步(<code>fetch</code>/<code>pull</code>)<code>遠端 main 分支</code>的結果。
- <code>origin/feature</code> 代表遠端追蹤分支最後一次同步(<code>fetch</code>/<code>pull</code>)<code>遠端 feature</code> 分支的結果
>[!Note]遠端追蹤分支(remote-tracking branch)
>是本地保存的遠端的分支的最新狀態快照,我們通常說只會說「遠端追蹤分支」但是其實「遠端追蹤分支」和「本地遠端追蹤分支」是同樣的東西
>也就是遠端分支在你電腦裡的鏡像,用來對照遠端現在長什麼樣
</div>
</details>
<details><summary>5. <code>fetch</code>: 從遠端抓取最新狀態到本地的「遠端追蹤分支」上,類似於對遠端「預覽」。</summary>
<div>
---
有些類似於<code>pull</code>同樣是抓檔案下來,但==和<code>pull</code>不同,<code>fetch</code> 是將資料同步到「本地的遠端追蹤分支」上而不是「本地分支」上==,因此不會改動本地的工作分支,==相當於在本地對遠端進行「預覽」==。
| 操作 | 是否建議先 fetch | 理由 |
| ------------ | ----------- | --------------------------------------------------------------------- |
| <code>git pull</code> | ✅ 強烈建議 | `pull` 實際上是 `fetch + merge`,但若要安全檢查建議先手動 <code>fetch</code> |
| <code>git merge</code> | ✅ | 確保遠端分支是最新的,避免基於過期版本合併 |
| <code>git rebase</code> | ✅ | 若不 <code>fetch</code>,可能導致你 <code>rebase</code> 在舊的 <code>base</code> 上,增加衝突風險 |
| <code>git push</code> | ✅ | 若遠端已更新,你的 <code>push</code> 可能被拒絕(non-fast-forward),先 <code>fetch</code> 才能確認是否需 <code>rebase</code> 或 <code>merge</code> |
>[!Warning]fetch 的重要性
>在進行 <code>rebase</code>、<code>merge</code>、<code>pull</code>、或開分支等操作前都應先執行 <code>git fetch</code>,來確保本地的遠端追蹤分支更新到最新狀態。
>就像在開車上路前,先打開地圖來更新地圖資訊一樣。
</div>
</details>
<details><summary>6. <code>Tracking Branch</code>(追蹤分支): 是一種本地分支的「關係」設定,它「追蹤」一個遠端分支的狀態,方便你進行 同步(<code>pull</code>/<code>fetch</code>/<code>push</code>)</summary>
<div>
---
是「本地分支標記了自己對應哪個遠端分支」的連結設定,也就是讓「這個本地分支知道自己的遠端庫是誰」的設定。
>[!Note]遠端追蹤分支(remote-tracking branch) 和 追蹤分支(Tracking Branch)
>遠端追蹤分支和追蹤分支是兩種名稱很像但完全不同概念,一個代表本地端對遠端的快照,是一種「狀態」,一個代表本地端對遠端的連結設定,是一種「設定」、「關係」
</div>
</details>
<details><summary>7. <code>upstream</code>(上游): 代表追蹤分支(Tracking Branch)所指向的那個遠端分支</summary>
<div>
---
是一種「角色」、使一種設定中的「設定欄位」,代表追蹤分支所指向的那個遠端分支,它是這段同步關係中的參考目標。
==Tracking Branch 內有 upstream 設定,upstream 設定的值指定了 Remote-Tracking Branch==
| 概念 | 定位 | 比喻 |
| --- | --- | --- |
| **Remote-tracking branch** | 狀態/實體 | 「照片」(有內容) |
| **Tracking branch** | 設定/關係 | 「通訊錄」(有連結設定) |
| **Upstream** | 設定欄位/屬性 | 通訊錄裡的「主管欄位」 |
| **Upstream 的值** (如 origin/main) | 目標/角色 | 「張經理」(被標記為主管的人) |
</div>
</details>
<details><summary>8. <code>Pull Request</code>(拉取請求,PR) / <code>Merge Request</code>(合併請求,MR): 是一種協作流程,外部開發者向專案維護者發出請求,請專案維護者 <code>Pull</code> 外部開發者的修改並檢查、審核、討論,以決定是否將這些修改合併到主分支中。</summary>
<div>
---
- <code>Pull Request</code>/<code>Merge Request</code> 是一樣的概念,只是在不同平台會叫不同的名子。
- 當完成某個功能分支的開發後,想要把這些修改「合併」到主分支(例如 <code>main</code> 或 <code>develop</code>),但希望讓其他人先檢查、審核、討論,就會建立一個 <code>Pull Request</code>。
- 類似於當你寫好一份報告(功能分支),想放進公司正式檔案(主分支),但在放進去之前,必須先交給主管審閱。主管審閱後同意,才會被正式合併。
</div>
</details>
---
四、除錯工具
---
<details><summary>1. <code>Tag</code>: 給某個 <code>Commit</code> 貼上自定義標籤(通常是用版本號),讓我們之後能快速找到那個版本</summary>
<div>
---
1. 輕量標籤 (Lightweight Tag): 單純的建立標籤
```bash=
git tag v1.0.0
```
==2. 附註標籤 (Annotated Tag)==: 建立並給標籤加上額外的附註、作者、日期、訊息等,通常會推薦使用附註標籤。
```bash=
git tag -a v1.0.0 -m "Initial release"
```
- <code>-a</code>: 註釋(通常是版本號)
- <code>-m</code>: 加上說明文字
<font size=4><b><u>常用操作</u></b></font>:
```bash=
# 查看所有標籤
git tag
# 查看特定標籤的詳細資訊
git tag -l "v1.*" # 列出所有 v1.x.x 版本
# 推送標籤到遠端
git push origin v1.0.0
# 推送所有標籤
git push origin --tags
# 刪除本地標籤
git tag -d v1.0.0
# 刪除遠端標籤
git push origin --delete v1.0.0
```
<font size=4><b><u>常用範例</u></b></font>:
```bash=
# 1. 完成新功能開發並測試完成
git commit -m "完成登入功能"
# 2. 準備發布,建立標籤標記這個版本
git tag -a v1.2.0 -m "新增登入功能"
# 3. 推送到遠端讓團隊成員看到
git push origin v1.2.0
# 4. 未來如果要查看或回到這個版本
git checkout v1.2.0 # 切換到該版本查看
```
</div>
</details>
<details><summary>2. <code>Stash</code>(暫存): 暫時把尚未 <code>commit</code> 的修改「藏起來」</summary>
<div>
---
<code>Stash</code> 會把已修改但未提交的檔案、已暫存 (staged) 的檔案,全部「打包起來」放進一個臨時區域,然後把工作區和暫存區還原成乾淨的狀態。
- 中文名和 <code>Staging Area(暫存區)</code> 聽起來很像,但實際上沒有太大的關係
- <code>git stash</code> 就像暫時把文件塞進抽屜裡,先去做別的事情,晚點再拿出來繼續改。
- 當我們臨時性的要去做別的事(例如: 緊急修復某個 Bug),就會使用 <code>Stash</code> 來進行暫存
- 「不想 <code>commit</code>,但又不想丟掉改動,就先 <code>stash</code> 起來。」
<font size=4><b><u>常用指令</u></b></font>:
```bash=
# 暫存
git stash
# 暫存並附加備註
git stash push -m "訊息"
# 顯示所有暫存項目
git stash list
# 顯示最新 stash 的差異摘要
git stash show
# 顯示完整差異內容(patch)
git stash show -p stash@{0}
# 套用最新 stash
git stash apply
# 套用特定 stash
git stash apply stash@{1}
# 刪除特定暫存項目
git stash drop stash@{0}
# 清空所有 stash
git stash clear
```
</div>
</details>
<details><summary>3. <code>Bisect</code>(對分): 是 Git 提供是一個「二分搜尋除錯工具」,用二分搜尋法快速找出哪一個 <code>commit</code> 引入了 bug。</summary>
<div>
---
當一個 Bug 出現,但不知道它是什麼時候、被哪個提交( <code>commit</code> )引入時,我們就會使用 <code>git bisect</code> 。
- <code>git bisect</code>的運作就像是玩「猜數字」遊戲一樣,它每次都將範圍縮小一半,能以最少的步驟找到目標。
<details style="border-left: 4px solid #1565C0;background-color: #0D1B2A;padding: 12px 16px;margin: 10px 0;border-radius: 4px;font-family: Arial, sans-serif;"><summary><code>git bisect</code>的操作步驟</summary>
<div>
---
步驟 1.啟動 <code>Bisect</code>: 告訴 Git「我要開始找 bug 了」
```bash=
git bisect start
```
步驟 2.標記壞的 <code>commit</code>: 告訴 Git「這個版本有問題」
```bash=
# 和Tag聯動使用
git bisect v1.2.0
# 直接標記
git bisect bad
# 或指定特定 commit
git bisect bad HEAD
```
步驟 3.標記好的 <code>commit</code>(過去某個正常的版本): 告訴 Git「這個版本是正常的」,這樣 Git 就知道問題必定出在這個「壞的」和「好的」兩個 <code>Commit</code> 之間
```bash=
# 和Tag聯動標記
git bisect good v1.0.0
# 或使用 commit hash
git bisect good a1b2c3d
```
步驟 4.Git 自動切換到中間點,在進行測試後標記「 good 」或「 bad 」: 透過對測試完的版本標記「 good 」或「 bad 」,Git 會持續二分,直到找出「第一個壞掉的 <code>commit</code>」
```bash=
# 如果這個版本有問題
git bisect bad
# 如果這個版本正常
git bisect good
```
步驟 5.找到問題後,結束 Bisect: 結束 Bisect 模式,回到原本的分支狀態
```bash=
git bisect reset
```
</div>
</details>
</div>
</details>
<details><summary>4. <code>Reflog</code>(Reference Logs,引用日誌): 是 Git 的檔案救援、保命機制,他會記錄「所有會導致 <code>HEAD</code> 或分支位置改變的操作」來使我們得以回朔操作。</summary>
<div>
---
<font size=4><b><u>常用指令</u></b></font>:
```bash=
# 查看所有操作記錄
git reflog
# 查看特定分支的 reflog
git reflog show main
# 查看最近 5 筆記錄
git reflog -5
# 根據時間查看
git reflog show HEAD@{2.days.ago}
# 恢復到特定操作點
git reset --hard HEAD@{3}
```
<font size=4><b><u>具體會被 <code>Reflog</code> 紀錄的操作</u></b></font>:
| 類別 | 範例指令 | 為什麼會記錄 |
| ---------------------- | ---------------------------------- | ------------------------- |
| **提交行為** | `git commit` | <code>HEAD</code> 從舊 <code>commit</code> → 新 <code>commit</code> |
| **切換分支** | `git switch`、`git checkout branch` | <code>HEAD</code> 指向新的分支 |
| **重設版本** | `git reset --hard`、`--soft` | <code>HEAD</code> 回到舊 <code>commit</code> |
| **合併操作** | `git merge`、`git rebase` | <code>HEAD</code> 移動至新的合併結果 |
| **回滾操作** | `git revert` | <code>HEAD</code> 指向新生成的回滾 <code>commit</code> |
| **建立/刪除分支** | <code>git branch <name></code> | <code>分支 reference</code> 變動 |
| **變更 Tag (Annotated)** | `git tag -a` | <code>tag reference</code> 變動(但不記錄在 <code>HEAD</code>) |
</div>
</details>
<details><summary>5. <code>Reset</code>: 改變 <code>HEAD</code> 和分支 <code>HEAD</code> 的位置,並根據選項決定是否重設暫存區與工作目錄</summary>
<div>
---
>[!Warning] <code>Reset</code> 並不會真的把 <code>Commit</code> 刪掉
>使用 <code>Reset</code> 並不會真的把 <code>Commit</code> 刪除,而是會1. 移動 <code>HEAD</code> → 2. 將舊的 <code>Commit</code> 隱藏 → 3. 放進「資源回收桶」中,在 90天(預設) 內還可以透過 <code>reflog</code> 找回;因此看起來就像被刪除一樣。
- <code>reset</code> 回到過去某個 <code>Commit</code> 時,你的 <code>Local Repository</code>(本地倉庫)的進度條會直接縮短,若在此時使用 <code>git push</code> 推送到 <code>remote</code>, Git 會因為和歷史對不起來而發生衝突。
<b><u>Reset的三種模式</u></b>:
| 模式 | 行為 | 暫存區 | 工作目錄 | 用途 |
| ------------- | ------------------- | ---- | ---- | -------------- |
| `--soft` | 只改 HEAD | ❌ 不變 | ❌ 不變 | 重新提交用 |
| `--mixed`(預設) | 改 HEAD + 暫存區 | ✅ 改變 | ❌ 不變 | 取消暫存 |
| `--hard` | 改 HEAD + 暫存區 + 工作目錄 | ✅ 改變 | ✅ 改變 | 完全回到舊狀態 |

</div>
</details>
<details><summary>6. <code>Revert</code>(回復): 產生一個新的 <code>commit</code> 來反轉某個已存在的 <code>commit</code> 的變更</summary>
<div>
---
- 類似於<code>reset</code> ,但 <code>revert</code> 不改動歷史,它保留原本的 <code>commit</code>
- 由於 <code>Revert</code>,的原理是產生一個新的 <code>commit</code>,而不是像 <code>Reset</code>直接將消除 <code>commit</code>,不會在 <code>Remote Repository</code> 因為歷史不同造成衝突,因此==可安全用於多人協作的分支==。
- <code>Revert</code> 是透過「反著做」來讓結果倒退回到想要的狀態,但歷史保持完整。

</div>
</details>
<details><summary>7. <code>Restore</code>: 把檔案的狀態,恢復成某個來源的版本</summary>
<div>
---
- 它處理和作用的是檔案狀態(本地的實際文件),不是處理 <code>commit</code> 歷史(已經提交到 <code>Repository</code> 的文件)
- 只適合用在尚未 <code>commit</code> 之前,已經<code>commit</code> 之後最好改用 <code>Reset</code> 或 <code>Revert</code>
```bash=
git restore file.txt
```
</div>
</details>
---
五、團隊協作
---
<details><summary>1. <code>Git Flow</code>: 是一套「分支管理策略」,教你「如何有條理地管理程式碼的版本與分支」</summary>
<div>
---
是一套使用 Git 的工作流程規範,類似於 [設計模式](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/DesignPattern),告訴大家「用 Git 時,最推薦的流程與分工方式」。
<b><u>Git Flow 定義了兩種「主要分支」+三種「輔助分支」:</u></b>
| 分支名稱 | 類型 | 用途 | 類比 |
| ----------- | ---- | -------------------- | ------------------ |
| `main` | 主要分支 | 永久儲存「正式」、「已上線」、「穩定」、「可發佈的程式碼」 | 正式上市的產品 |
| `develop` | 主要分支 | 所有新功能整合的地方,所有的新功能和錯誤修復都是先合併到這裡,是下個版本的準備區 | 研發中心 |
| `feature/*` | 輔助分支 | 用於開發單一新功能。從 <code>develop</code> 分支出來,完成後再合併回 <code>develop</code>。 | 研發中的各項新產品 |
| `release/*` | 輔助分支 | 準備要發佈的版本,用來修錯與微調,完成後必須合併回 <code>main</code> 和 <code>develop</code>。 | 準備上市的產品包裝與測試階段 |
| `hotfix/*` | 輔助分支 | 緊急修正上線版本的錯誤 | 出貨後緊急回收修正的版本 |
<details style="border-left: 4px solid #1565C0;background-color: #0D1B2A;padding: 12px 16px;margin: 10px 0;border-radius: 4px;font-family: Arial, sans-serif;"><summary><font size =4>Git Flow 的一般流程</font></summary>
<div>
---
步驟1. 從 <code>develop</code> 建立新功能分支 -- 在安全的獨立環境開發新功能。
```bash=
git checkout develop
git checkout -b feature/new-login
```
步驟2. 功能完成後合併回 <code>develop</code> -- 將新功能整合進主要開發版本
```bash=
git checkout develop
git merge --no-ff feature/new-login
git branch -d feature/new-login
```
步驟3. 當要發佈版本時建立 <code>release</code> 分支 -- 進行最終測試與版本號設定。
```bash=
git checkout develop
git checkout -b release/1.0.0
```
步驟4. 發佈完成後合併回 <code>main</code> 與 <code>develop</code> -- 確保 <code>main</code> 與 <code>develop</code> 都有相同最終版本。
```bash=
git checkout main
git merge --no-ff release/1.0.0
git tag -a 1.0.0
git checkout develop
git merge --no-ff release/1.0.0
git branch -d release/1.0.0
```
步驟5. 如果線上出現重大錯誤,建立 <code>hotfix</code> 分支 -- 修正完後同樣合併回 <code>main</code> 與 <code>develop</code>。
```bash=
git checkout main
git checkout -b hotfix/1.0.1
```
</div>
</details>
</div>
</details>
<details><summary>2. <code>Trunk-based Workflow</code>(主幹式工作流程,TBD): 是一種相較於 <code>Git Flow</code> 更簡潔的 Git 分支策略,所有開發人員都會把代碼直接整合到一個主要分支上</summary>
<div>
---
如果說 <code>Git Flow</code> 是一種嚴謹、多階段的生產線,那麼 <code>Trunk-based Workflow</code> 就是一種快速、持續、單一的流水線。
- 核心理念是 -- 「少分支、快整合、持續整合(CI)」
- 相較於Git Flow 模式可以把錯誤停留在 <code>feature</code> 或 <code>release</code> 分支,TBD 仰賴每個人去維持、管理、測試、維持穩定,因此 TBD 對個人的能力要求更高。
- 由於 TBD 模式只要有小錯誤就會立即汙染主幹,因此最好搭配 TDD(<code>Test-Driven Development</code>)。
<b><u>TBD 基本概念</u></b>:
| 元素 | 說明 |
| ---------------------------- | ------------------------------- |
| **主幹(Trunk/Main)** | 唯一長期存在的分支。所有人都往這裡整合。 |
| **短命分支(Short-lived Branch)** | 若需要開新功能,會建立小分支(幾小時或一天內合併回主幹)。 |
| **持續整合(CI)** | 每次提交(<code>commit</code>)都會自動測試、編譯,確保主幹永遠可用。 |
</div>
</details>
---
Q&A
---
<details><summary><font size=4>為甚麼 <code>Reset</code> 消除的順序是從 <code>Repository(儲存庫)</code> 開始而不是從 <code>Working Directory(工作區)</code> 開始?</font></summary>
<div>
---
描述:
---
乍看之下,<code>Reset</code> 的三種模式的消除順序(<code>Repository → Staging Area → Working Directory</code>)似乎不太合理
- 就變動的嚴重性來說,應該是<code>Working Directory</code> < <code>Staging Area</code> < <code>Repository</code>
- 所以照理來說<code>soft → mix → hard</code>消除的順序也應該要和風險性一致的從最低的<code>Working Directory → Staging Area → Repository</code>開始消除
但實際上卻是相反,是由 <code>Repository</code> 開始清除。
<font size=5>Ans</font>:
---
之所以會這樣做是因為<code>git reset</code>的運作邏輯:
- <code>git reset</code> 的第一個、也是唯一一個必定會發生的動作,就是將 <code>HEAD</code> 指標移動到您指定的目標 <code>Commit</code> 上。
- 一旦 <code>Repository</code> 的 <code>HEAD</code> 指標移動完成,接下來 Git 會根據您選擇的模式,來決定是否要用新 <code>HEAD</code> 所在 <code>Commit</code> 的內容,去「覆蓋」或「清空」外層的區域。
<b><u>也就是說,「嚴重性」的觀點,是建立在「<code>HEAD</code> 移動後」的前提下來進行的,因此</u></b> :
1. <code>--soft</code> 模式 (影響最輕):僅移動 <code>HEAD</code>。它只改動了 <code>HEAD</code>,完全不影響 <code>Staging Area</code> 和 <code>Working Directory</code>。
2. <code>--mixed</code> 模式 (中等影響):移動 <code>HEAD</code>,然後用新 <code>HEAD</code> 的內容來「清空」<code>Staging Area</code>。
3. <code>--hard</code> 模式 (影響最重):移動 <code>HEAD</code>,清空 <code>Staging Area</code>,然後用新 <code>HEAD</code> 的內容強行覆蓋 <code>Working Directory</code>。這是最「破壞性」的操作,因為它直接清除了您未提交的工作。
</div>
</details>
<details><summary><code>Reset</code>、<code>Revert</code>、<code>Restore</code> 之間差在哪裡?</summary>
<div>
---
簡單來講,就是三者的試用狀況不同。
- 想還原尚未 commit 的變動 → git restore (處理還沒存進 Git 的變動)
- 想把整個分支往回推 → git reset
- 想在不破壞團隊歷史的情況下撤銷? → git revert
</div>
</details>
---
參考資料
---
- [【Git】從零開始學習 Git - 30 天的學習筆記 ](https://ithelp.ithome.com.tw/m/users/20141010/ironman/4499)
- [新手也能懂的Git教學](https://medium.com/@flyotlin/%E6%96%B0%E6%89%8B%E4%B9%9F%E8%83%BD%E6%87%82%E7%9A%84git%E6%95%99%E5%AD%B8-c5dc0639dd9)
- [不熟 Git 嗎?好巧我也是,不如我們一起來學吧! ](https://ithelp.ithome.com.tw/users/20162483/ironman/6374)
- [Day 26 - [版本控管] 01-從 Git CLI 到 GUI](https://ithelp.ithome.com.tw/m/articles/10349851)
- [Git](https://git-scm.com/)
- [Git中文網](https://git.p2hp.com/)
- [Git 其然,Git 其所以然](https://ithelp.ithome.com.tw/users/20103676/ironman/2846)
- [[Git] 基礎觀念](https://ithelp.ithome.com.tw/m/articles/10240279)
- [[Git] 初始設定](https://ithelp.ithome.com.tw/m/articles/10240965)
- [[Git] 常用指令表](https://ithelp.ithome.com.tw/m/articles/10241407)
- [[Git] 應用情境](https://ithelp.ithome.com.tw/m/articles/10242283)
- [淺入 Git:detached HEAD](https://titangene.github.io/article/git-detached-head.html)