<style>
.reveal table, .reveal table th, .reveal table td {
border: 1px solid;
font-size: 33px;
text-align: center;
vertical-align: middle;
}
</style>
# 如何使用 Git? (本地篇)
---
## 目錄
- Git基本介紹
- 如何開始使用
- 環境設置
- 檔案管理
- Commit管理
- 分支管理
- 暫存工作區
---
## Git基本介紹
----
### 那些年一起玩單機遊戲
<img src="https://i.imgur.com/p7FZbA3.gif" height="400">
----
### Git 行為描述
| Git | Game Boy |
| -------- | -------- |
| Commit | 存檔 |
| Checkout | 讀檔 |
| Branch | 分歧點 |
| 遠端倉庫(Repository) | 遊戲平台 |
---
## 如何開始使用
----
### 安裝 Git
<li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px;margin-bottom:10px;">可以到<a href="https://git-scm.com/downloads">官網</a>選擇目前作業系統環境進行安裝</li>
<li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">Mac OS 作業系統可以使用 HomeBrew 安裝</li>
```=
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install git
```
----
### 初始化資料夾
```=
# 建立空資料夾
mkdir git-demo
# git 初始化
git init git-demo
```
---
## 環境設置
----
### 查看 Git 設定
<li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">查看 global 的 config (全域)</li>
```=
git config --list (預設是global)
git config --global --list
vim ~/.gitconfig
```
<li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">查看 local 的 config (專案)</li>
```=
cd 專案路徑
git config --local --list
vim 專案路徑/.git/config
```
----
### 常用的設定
<li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">設定用戶名及信箱</li>
```=
git config --global user.name <username>
git config --global user.email <email>
```
<li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">命令的別名</li>
```=
git config --global alias.<aliasname> <commandname>
# example
git config --global alias.st status
```
<p style="text-align:start;color:red;font-size:25px;padding-left:50px;margin-top:30px;">* 如果 --global 改成 --local 就是當前專案的設定 </p>
---
## 檔案管理
----
<img src="https://i.imgur.com/bp5hKoX.png" width="580" height="500">
----
### 未被追蹤的檔案
<img src="https://i.imgur.com/y3mEGSZ.png" width="800" height="160"/>
----
### 被更動但尚未要提交的檔案
<img src="https://i.imgur.com/VE6UUqP.png" width="800" height="160"/>
----
### 將要提交的檔案
<img src="https://i.imgur.com/GescWTb.png" width="800" height="160"/>
<p style="text-align:start;font-size:25px;padding-left:75px;font-weight:600;">* 在此狀態用 git reset HEAD 會還原原始狀態(未追蹤檔案、更動尚未被提交)</p>
----
### 查看檔案
```=
# 查看目前 unstaged 檔案
git status
# 查看已提交的檔案內容
git show <hash id>
# 查看尚未提交的檔案內容
git diff <file>
# 查看歷史紀錄
git log
```
----
### 新增/提交檔案
```=
# 將全部的檔案設為staged
git add . 或者 git add -A
# 將單一的檔案設為staged
git add <file>
# 提交訊息
git commit -m <message>
```
----
### 刪除檔案
<li style="text-align:start;padding-left:60px;font-weight:600;font-size:35px">前置條件為已經進入 git 版控的歷史紀錄</li>
```=
# 遠端與本地端都刪除檔案
git rm <file>
# 遠端刪除檔案,但本地端依然保留著
git rm --cached <file>
```
----
### 檔案還原
<li style="text-align:start;padding-left:60px;font-weight:600;font-size:35px">前置條件為尚未提交的狀態</li>
```=
# 還原未修改前的檔案內容(單一檔案)
git checkout -- <file>
# 將所有未提交的檔案還原未修改的內容
git reset --hard HEAD
```
----
### 忽略檔案
<div style="display:flex;justify-content:space-between">
<p style="text-align:start">新增 .gitignore 檔案,把該目錄想忽略的檔案路徑寫在該檔案裡面。</p>
<img src="https://i.imgur.com/L7oesL4.png" width="400" height="430" style="margin-left:30px;">
</div>
---
## Commit 管理
----
### 什麼是 Commit?
<ul style="margin-left:25px;">
<li style="text-align:start;font-weight:600;font-size:33px">為提交後的檔案“標記訊息”</li>
<li style="text-align:start;font-weight:600;font-size:33px">每一個 commit 內容只記錄"修改"的程式內容,並"不是完整"的code</li>
</ul>
<img src="https://i.imgur.com/cM1ayI1.png">
----
### 關鍵字 HEAD
<li style="text-align:start;padding-left:45px;font-weight:600;font-size:35px">HEAD 為 git 的關鍵字,代表目前 commit 所在的位置
</li>
<li style="text-align:start;padding-left:45px;font-weight:600;font-size:35px">HEAD^ 或 HEAD~1 代表上一個 commit 位置
</li>
<li style="text-align:start;padding-left:45px;font-weight:600;font-size:35px">HEAD^^ 或 HEAD~2 代表上兩個 commit 位置
</li>
<p style="text-align:start;font-size:35px;margin-left:90px;margin-top:0px;">以此類推...</p>
<img src="https://i.imgur.com/Eci51RV.png" width="850" height="150">
----
### Reset 還原
<li style="text-align:start;padding-left:45px;font-weight:600;font-size:30px">透過 reset 可以隨心所欲還原最初的內容</li>
<li style="text-align:start;padding-left:45px;font-weight:600;font-size:30px">可以搭配 HEAD 讓我們不用死記 commit id</li>
```=
# 回到上一次 commit,還原為 modified、untracked 檔案
git reset HEAD^
# 回到上一次 commit,還原為 changes to be commited 檔案
git reset --soft HEAD^
# 回到上一次(或是指定) commit,並強制清除第一個已提交內容
git reset --hard HEAD^/<commit id>
```
<p style="color:red;font-size:30px;text-align:start;margin-left:40px;">*建議在操作前先將工作區的程式暫存 (stash) 起來</p>
----
### 找回消失的 commit
<ul style="margin-left:10px;">
<li style="text-align:start;font-weight:600;font-size:30px">使用 git reflog 來查看 HEAD 的歷史紀錄,至少可以保留一個月</li>
<li style="text-align:start;font-weight:600;font-size:30px">透過 git reset --hard <commit id> 還原</li>
</ul>
<img src="https://i.imgur.com/eqcehou.png" height="400">
----
### 修改已提交的內容
<p style="text-align:start;margin-left:30px;font-size:33px;">有時候 commit 訊息打錯,想要重新修改訊息,以下有三種方式</p>
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">第一種</li>
```=
git reset --soft HEAD^
git commit -m '訊息內容'
```
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">第二種</li>
```=
git commit --amend -m
```
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">第三種</li>
```=
git rebase -i <commit id>
```
----
### 何謂 cherry-pick
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">cherry-pick 為少量操作(複製貼上) commit</li>
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">每一個 commit 為紀錄修改的片段程式碼代表一個物件</li>
```=
git cherry-pick <commit id>
```
----
### 何謂 rebase
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">重新定義基準點</li>
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">在操作特定 commit 時,需要加上 -i (interactive) 參數進行互動模式</li>
```=
git rebase -i <commit id>
```
<img src="https://i.imgur.com/YpCMzVZ.png" height="350">
----
### 何謂 revert
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:30px;">實作概念很像是數學運算的 100 + (-100) = 0</li>
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:30px;">當發現某個 commit 有問題想要拿掉,而且是前面已經很多 commit 的情況</li>
```=
git revert <commit id>
```
<img src="https://i.imgur.com/INTGt4P.png" style="margin-left:20px;">
----
### 互動模式(ㄧ)
<p style="text-align:start;font-size:33px;">當使用 cherry-pick/rebase/revert 操作 commit 順序時,有時會遇到衝突要解決,操作方式都一樣</p>
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:25px;list-style:none;">1. 遇到衝突顯示畫面</li>
<img src="https://i.imgur.com/rebawA6.png">
----
### 互動模式(ㄧ)
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:25px;list-style:none;">2. 透過 git status 查看檔案狀態</li>
<img src="https://i.imgur.com/bf7YvgU.png">
----
### 互動模式(ㄧ)
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;list-style:none;">3. 解完衝突</li>
```=
git add -u
git cherry-pick/rebase/revert --continue
```
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;list-style:none;">4. 放棄操作</li>
```=
git cherry-pick/rebase/revert --abort
```
----
### 互動模式(二)
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:25px;list-style:none;">1. 有時操作時會出現未修改變更的提示</li>

----
### 互動模式(二)
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:25px;list-style:none;">2. 使用 git status 查看狀態</li>

----
### 互動模式(二)
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;list-style:none;">3. 允許未變更的 commit</li>
```=
git commit --allow-empty
```
<li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;list-style:none;">4. 放棄操作</li>
```=
git cherry-pick/rebase --abort
```
<p style="font-size:30px;text-align:start;color:red;margin-top:40px;margin-left:20px;">*不能使用 --continue 原因是未解決衝突,如果要繼續只能用 --allow-empty</p>
---
## 分支管理
----
### Commit tree
<ul>
<li style="text-align:start;font-weight:600;font-size:35px;">在 Git 裡面,每一個 commit 都是一個節點,當把這些節點一個一個串起來最後組成就是 commit tree</li>
<li style="text-align:start;font-weight:600;font-size:35px;">每個節點(commit id)都是獨一無二的,都可以另外長出自己的分支</li>
</ul>
```=
o---o---o feature 2
/
o---o---o---o---o---o---o---o---o---o---o develop
\ /
o---o---o---o feature 1
```
----
### 何謂分支
<ul>
<li style="text-align:start;font-weight:600;font-size:35px;">建立分支其實就是把 commit 貼上標籤,相反的刪除分支就是把標籤撕掉一樣</li>
<li style="text-align:start;font-weight:600;font-size:35px;">就算刪除分支該 commit 也沒有因此刪除也可以透過 reflog 查看歷史紀錄救回來</li>
</ul>
<div style="display:flex;justify-content:center;">
<img src="https://i.imgur.com/bw3RBAW.png" height="300">
</div>
----
### 查看/切換分支
```=
# 顯示本地端所有分支
git branch
# 顯示本地端與遠端所有分支
git branch -a
# 切換分支
git checkout <branch name>
```
----
### 建立分支
```=
# 建立分支
git branch <new branch name>
# 建立分支並切換
git checkout -b <new branch name>
# 指定 commit id 建立分支並切換
git checkout -b <new branch name> <commit id>
# 指定遠端的分支建立並切換
git checkout -b <new branch name> origin/<branch name>
```
----
### 刪除分支
```=
# 刪除分支
git branch -d <branch name>
# 強制刪除分支
git branch -D <branch name>
# 刪除後的分支復原
git branch <new branch> <commit id>
```
<img src="https://i.imgur.com/6qwIpAY.png" height="177">
----
### rebase 合併分支
<li style="text-align:start;font-weight:600;font-size:35px;padding-left:50px;">重新定義分支基準點</li>
```=
git checkout feature/xxx
git rebase develop
```
----
### rebase (情況一)
<li style="text-align:start;font-weight:600;font-size:35px;padding-left:20px;list-style:none;">1. 首先往上找兩個分支的交會點</li>
<img src="https://i.imgur.com/PTK4JuN.png">
<li style="text-align:start;font-weight:600;font-size:35px;list-style:none;">2. 再把 develop 有 feature 沒有的 commit 接到後面</li>
<div style="display:flex;justify-content:center;">
<div>
<span style="font-size:30px;">一開始</span>
<img src="https://i.imgur.com/VXJvilB.png" width="500" height="100">
</div>
<div>
<span style="font-size:30px;">結果</span>
<img src="https://i.imgur.com/PSgNAEO.png" width="500" height="100">
</div>
</div>
----
### rebase (情況二)
<li style="text-align:start;font-weight:600;font-size:35px;padding-left:20px;list-style:none;">1. 首先往上找兩個分支的交會點</li>

<ul>
<li style="text-align:start;font-weight:600;font-size:35px;list-style:none;">2. 把 develop 有 feature 沒有的 commit 接到後面,<span style="color:red;">再把本身分支的 commit 接到最後面</span></li>
</ul>
<div style="display:flex;justify-content:center;">
<div>
<span style="font-size:30px;">一開始</span>
<img src="https://i.imgur.com/tt5sB2H.png" width="500" height="100">
</div>
<div>
<span style="font-size:30px;">結果</span>
<img src="https://i.imgur.com/8Hef719.png" width="500" height="100">
</div>
</div>
----
### merge 合併分支
<li style="text-align:start;font-weight:600;font-size:35px;padding-left:50px;">根據 commit 的 timestamp 不同來進行合併動作</li>
```=
# FastForward 合併不會多一個 merge commit
git merge --ff <branch name>
# No FastForward 合併會多一個 merge commit
git merge --no-ff <branch name>
```
----
### merge 合併分支
<li style="text-align:start;font-weight:600;font-size:30px;padding-left:170px;">No FastForward 合併結果</li>
<img src="https://i.imgur.com/SbPtxhD.png" height="450">
----
### 復原合併刪除後的分支
<ul style="margin-left:70px;">
<li style="text-align:start;font-weight:600;font-size:30px;">由於合併會紀錄來源分支的 commit id,還記得前幾章提到用 commit id 建立branch</li>
</ul>
```=
git checkout -b feature/add_disassemble_frontend 5413927
```
----
### rebase 與 merge 比較
<table>
<thead>
<tr>
<th>Merge</th>
<th>Rebase</th>
</tr>
</thead>
<tbody>
<tr>
<td>容易理解</td>
<td>不容易理解</td>
</tr>
<tr>
<td>當發生衝突的時候,全部在 Merge Commit 一次解完</td>
<td>Rebase 的過程中,每次 commit 發生衝突都要解一次衝突</td>
</tr>
<tr>
<td>commit tree 會有兩個 parents,不容易 trace code</td>
<td>commit 路徑單純,容易 trace code,或回到指定版本</td>
</tr>
<tr>
<td>過多的 Merge Commit 會看起來很亂</td>
<td>分支乾淨一致</td>
</tr>
</tbody>
</table>
---
## 暫存工作區
----
### 常用指令
```=
# 列出暫存區所有清單
git stash list
# 將目前 modify 的檔案暫存,如果是 untracked 檔案需先 add
git stash
# 將在暫存區最新的 pop 出來
git stash pop
# 指定的編號 pop 出來
git stash pop stash@{num}
# 將所有暫存區清空
git stash clear
# 指定的編號刪除
git stash drop stash@{num}
```
---
## 冷知識
----
### HEAD 也有縮寫
<li style="text-align:start;font-weight:600;font-size:30px;padding-left:50px;">可以用 @ 來代替 HEAD</li>
```=
git reset HEAD^
git reset @^
```
----
### ORIG_HEAD 是什麼
<p style="font-size:35px;text-align:start;margin-left:50px;">當你在做一些比較「危險」的操作(例如像 merge、rebase 或 reset 之類的),Git 就會把 HEAD 的狀態儲存,讓你隨時可以跳回危險動作之前的狀態。</p>
<li style="text-align:start;font-weight:600;font-size:30px;padding-left:50px;">當你誤刪掉某一隻commit</li>
```=
git reset --hard <commit id>
```
<li style="text-align:start;font-weight:600;font-size:30px;padding-left:50px;">ORIG_HEAD 可以馬上復原</li>
```=
git reset --hard ORIG_HEAD
```
----
### .git 資料夾是什麼

---
## 情境題
----
### (情境一) 重新整理 commit 順序
```=
# 原來順序
P1 -> P2 -> P3 -> P4 -> P5
# 調整後的順序
P1 -> P2 -> P4 -> P5 -> P3
```
----
### 解法
```=
# 先回到未改變順序的節點
git reset --hard P2
# 在用 cherry-pick 調整順序
git cherry-pick P4 P5 P3
```
----
### (情境二) 將多個 commit 合併為一個
```=
# 原來的 commit tree
P1 -> P2 -> P3 -> P4
# 整理後的 commit tree
P1 -> P2 -> P5
```
----
### 解法
```=
# 開啟 rebase 互動模式
git rebase -i P2
```
<img src="https://i.imgur.com/tyBilhs.png" height="420">
----
### (情境三) 切換版本
<li style="text-align:start;font-weight:600;font-size:33px;padding-left:50px;list-style:none;">1. 切換 commit 並不會清除內容</li>
```=
git checkout <commit id>
```
<img src="https://i.imgur.com/U5KwXE7.png" height="350">
----
### (情境三) 切換版本
<li style="text-align:start;font-weight:600;font-size:33px;padding-left:50px;list-style:none;">2. 切換 commit 並清除內容</li>
```=
git reset --hard <commit id>
```
<img src="https://i.imgur.com/07slICU.png" height="80">
----
### (情境四) 已被 git 追蹤的檔案如何移除
```=
1. 本地端跟遠端一起刪掉檔案
git rm <file>
2. 本地端保留,遠端刪掉檔案
git rm --cached <file>
```
----
### (情境五) 更新分支並合併分支
<li style="text-align:start;font-weight:600;font-size:33px;padding-left:50px;list-style:none;">1. 使用 rebase 重新定義 feature 起始節點</li>
```=
git checkout feature/xxx
git rebase develop
```
<li style="text-align:start;font-weight:600;font-size:33px;padding-left:50px;list-style:none;">2. 開發完要合併回 develop</li>
```=
git checkout develop
git rebase feature/xxx
```
---
### 相關連結
- [連猴子都能懂的git](https://backlog.com/git-tutorial/tw/reference/config.html)
- [Git book](https://zlargon.gitbooks.io/git-tutorial/content/)
- [30 天精通 Git 版本控管](https://github.com/doggy8088/Learn-Git-in-30-days/blob/master/zh-tw/README.md)
- [為你自己學Git](https://gitbook.tw/chapters/config/user-config.html)
- [Learning Branching](https://learngitbranching.js.org/?NODEMO)
{"metaMigratedAt":"2023-06-14T19:12:37.412Z","metaMigratedFrom":"Content","title":"如何使用 Git? (本地篇)","breaks":true,"contributors":"[{\"id\":\"5344d19f-c240-4fe4-bd63-c70af739e6c4\",\"add\":32857,\"del\":17655}]"}