# Git【凡人修G傳之春水指法篇】<font color=#EE82EE size=3>收發自如,隨意化用</font>
<!-- **[:arrow_right: 看此筆記的原始碼 :arrow_left:](/gC-As-9ESBS7WPPXyoI7Yg)** -->
## Git 基礎知識
[Git【凡人修G傳之築基篇】](https://hackmd.io/@UmEXPPDuRqO4GLkq657i-g/BJHkx00HF)
---
## 安裝環境
[<img src="https://gitforwindows.org/img/git_logo.png" width=40>](https://gitforwindows.org/) [Git Bash](https://gitforwindows.org/) - Git CLI (命令視窗)
[<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/88/TortoiseGit_logo.svg/70px-TortoiseGit_logo.svg.png" width=40>](https://tortoisegit.org/download/) [TortoiseGit](https://tortoisegit.org/download/) - 主要用於顯示檔案狀態
---
## Bash CMD <font color=#FF4040 size=3>(Basic)</font>
```
cd <dirctory> // change directory
ls // list directory
cd.. // go back last directory
up arrow // last cmd
tab // help to quick search name of directory or file
clear // clear your terminal screen
exit // exit your terminal
history // list all of cmd history
histroy <num> // list num of newest cmd
```
---
## Git 一般版控 <font color=#FF4040 size=3>(SOP)</font>
#### Git 一般SOP【方法一】
###### 方法一. 先【Stash 隱藏目前工作】再【更新 + 合併】, 最後【Add + Commit】 + 【同步上傳】:
###### <font color=#FF0000>※ 功能製作一半時,但是遠端已有新項目更新需要搭配使用,則選擇【方法一】。</font>
```
// 檢查各檔案的狀態 (於根目錄下)
// . 指當前目錄下所有檔案
> git status .
// 隱藏全部檔案
// 可以在執行一次 git status . 確認一下工作區是否清空了
// 注意!!! 如果有 Untracked Files 將不列入隱藏區
// 如果需要則可以帶入參數 --include-untracked 或 -u (git stash -u)
> git stash
// 確定工作區清空後,就可以拉取遠端更新地端 (自行決定是否使用 rebase)
> git pull origin master --rebase
// 從遠端更新完成後,取出隱藏區的檔案 (可能會有衝突)
// 如有衝突則...手動修復合併...
> git stash pop
NOTE: 至於後續從隱藏區取出的上次未完成的檔案,是否要直接提交則自行判斷,
如果沒有,則做到這邊就好,相反的如果有要直接更新至遠端,就接續以下。
```
```
// 將更改過的檔案全部加入暫存區 (可以自行挑選要 add 的檔案)
// . 指當前目錄下所有檔案
> git add .
// 註解說明提交的檔案 (完成 devBranch 的 commit 後,如沒挑檔則會清空工作區)
> git commit -m "Your Commit"
// 以下指令為更新至遠端 (自行判斷是否需要更新至遠端)
> git push origin master
```
#### Git 一般SOP【方法二】
###### 方法二. 先【Add + Commit】再【更新 + 合併】, 最後【Add + Commit】 + 【同步上傳】:
###### <font color=#FF0000>※ 無例外的話,確定功能都完成了,單純進行合併並且更新至遠端,則選擇【方法二】。</font>
```
// 檢查各檔案的狀態 (於根目錄下)
// . 指當前目錄下所有檔案
> git status .
// 將更改過的檔案全部加入暫存區 (可以自行挑選要 add 的檔案)
// . 指當前目錄下所有檔案
> git add .
// 註解說明提交的檔案 (完成 commit 後,如沒挑檔則會清空工作區)
> git commit "Your Commit"
// 完成 commit 後,拉取遠端更新地端 (自行決定是否使用 rebase)
// 如有衝突則...手動修復合併...
> git pull origin master --rebase
// 將已完成修復後的衝突檔案進行 add
// . 指當前目錄下所有檔案
> git add .
// add 後,繼續合併 (以此類推直至完成)
> git rebase --continue
// 完成合併後,會自動開啟 vim,如需要則可以在原有的 commit 添加說明...無的話直接 => :q!
// 以下指令為更新至遠端 (自行判斷是否需要更新至遠端)
> git push origin master
```
## Git 分支版控 <font color=#FF4040 size=3>(SOP)</font>
#### Git 分支合併SOP
###### 將完成功能的 `devBranch` 分支合併至 `master` 中,以下:
[:book:] 如要取消 rebase => `git rebase --abort`。
```
// 先確保工作區為清空狀態,如果沒問題就切換分支開始製作
// 開始新增功能修改等等...完成後開始準備提交
> git checkout devBranch
// 將更改過的檔案或新增的檔案全部加入暫存區 (可以自行挑選要 add 的檔案)
// . 指當前目錄下所有檔案
> git add .
// 註解說明提交的檔案 (完成 devBranch 的 commit 後,如沒挑檔則會清空工作區)
> git commit "Your Commit"
// 切換至 master 分支,先將地端的 master 更新至最新 (自行決定是否使用 rebase)
> git pull origin master --rebase
// 完成 master 更新後,在切換至 devBranch
> git checkout devBranch
// 將最新的 master 合併至 devBranch (主要是為了避免直接在 master 進行衝突合併,可以自行判斷)
// 如有衝突則...修復手動合併...
> git rebase master
// 將已完成修復後的衝突檔案進行 add
// . 指當前目錄下所有檔案
> git add .
// add 後,繼續合併 (以此類推直至完成)
> git rebase --continue
// 完成合併後,會自動開啟 vim,如需要則可以在原有的 commit 添加說明...無的話直接 => :q!
// 此時 devBranch 已經完成更新與合併,最後切換至 master 分支
> git checkout master
// 此時在 master 分支,開始將 devBranch 合併至 master (剛剛已經有將 master 合併至 devBranch 了,所以基本上是無痛合併)
> git merge devBranch
// 將 devBranch 合併至 master 完成後...
// 以下指令為更新至遠端 (自行判斷是否需要更新至遠端)
> git push origin master
```
##### 以上如果<font color=#FF006F>分支整併完畢後</font>,依照需求如要刪除已合併分支則刪除,如果<font color=#FF006F>沒有要刪除 `devBranch` 分支,但是要更新的話</font>,以下。
```
> git push origin devBranch // 進行該分支的遠端更新
```
---
## Git 克隆/複製品 <font color=#FF4040 size=3>(Clone)</font>
#### Git Clone 預設
[:book:] `git clone <remote_repo>`。
```
git clone https://github.com/michaelo/happy_git.git
```
#### Git 指定特定分支進行 Clone
[:book:] `git clone -b <branch> <remote_repo>`。
```
git clone -b dev https://github.com/michaelo/happy_git.git
```
## Git 狀態 <font color=#FF4040 size=3>(Status)</font>
[:book:] `git status <path>`。
```
git status .
```
## Git 隱藏區 <font color=#FF4040 size=3>(Stash)</font>
#### Git 存入隱藏區
```
git stash
```
#### Git 從隱藏區的柱列取出最近隱藏的內容
```
git stash pop
```
#### Git 隱藏區列表
```
git stash list
```
## Git 暫存區 <font color=#FF4040 size=3>(Add Stage)</font>
#### Git 存入暫存區
[:book:] `git add .` 表示此目錄包含子目錄全部存入,無指定。
```
git add ./myFolder/my_file.txt
git add .
```
#### Git 從暫存區撤銷
[:book:] `git reset` 表示全部撤銷,無指定。
```
git reset my_file.txt
git reset
```
## Git 提交 <font color=#FF4040 size=3>(Commit)</font>
#### Git 提交
[:book:] `-m` 允許多個註解。
```
git commit -m "Your Commit"
git commit -m "1. First Commit" -m "2. Second Commit"
```
#### Git 後悔提交 <font color=#FF006F size=2>(想要拆掉重做)</font>
```
> git log --oneline // 顯示如下
```
```
e12d8ef (HEAD -> master) this is my commit 2
661080b this is my commit 1
```
```
> git reset HEAD^ // 從以上可以看到剛剛提交的 HEAD 在 e12d8ef, 但是我要退回去一個 661080b 表示拆掉或撤銷 (e12d8ef)
```
#### Git 修改提交註解
[:book:] `--amend` 表示修正、修改。
```
git commit --amend -m "Your Commit"
// 如果當下已經提交至遠端, 先確認你目前 commit 為最新的並且 amend 後, 可執行以下強制更新遠端
// ※備註: -f 不要亂用, 必要確認你當前 commit 為最新 (否則遠端會被覆蓋)
git push origin master -f
```
## Git 重置 <font color=#FF4040 size=3>(Reset => For Stage: Unstage/Cancel, For Commit: GoTo/GoBack)</font>
#### Git 查看 Commit 狀況
```
git log --oneline
```
#### Git 退回 Commit <font color=#FF006F size=2>(需先執行 log 查看, 才能知道退回至哪一個)</font>
[:book:] `^` 表示「前一次」的意思,所以每一個 `^` 符號就會退回1次。
[:book:] `~` 表示「輸入要退回次數 <font color=#0000FF size=3>(預設無輸入則1)</font>」的意思,所以`~3`就是退回3次。
```
git reset HEAD^
git reset HEAD~1
```
[:book:] `git reset <commit id>`預設為`--mixed`。
- `--mixed (default)` => 保留修改檔案(指的是工作區中的檔案),並且撤銷全部暫存區檔案。<font color=#FF006F size=2>(未追蹤的新檔案不納入)</font>
- `--soft` => 保留修改檔案(指的是工作區中的檔案),並且不會撤銷暫存區檔案。<font color=#FF006F size=2>(未追蹤的新檔案不納入)</font>
- `--hard` => 不保留修改檔案(指的是工作區中的檔案),強制還原至指定 commit 有的檔案。<font color=#FF006F size=2>(未追蹤的新檔案不納入)</font>
```
git reset HEAD^^ --soft
git reset HEAD~2 --hard
```
## Git 遠端 <font color=#FF4040 size=3>(Remote => origin)</font>
#### Git 新增遠端
[:book:] `git remote add <name> <url>`。
```
git remote add origin https://github.com/michaelo/happy_git.git
```
#### Git 更名遠端
[:book:] `git remane <old name>:<new name>`。
```
git remote rename origin gitnet
```
#### Git 移除遠端 <font color=#FF006F size=2>(如果有遷移伺服器, 或是本地端不在追蹤此遠端)</font>
```
git remote rm origin
```
#### Git 查看遠端
[:book:] `-v`表示 verbose <font color=#0000FF size=3>(冗長意思, 白話一點就是"詳細資訊")</font>。
```
git remote -v
git remote
```
#### Git 綁定預設遠端分支 <font color=#FF006F size=2>(於第一次的 commit 設置)</font>
[:book:] `git push -u <remote> <branch>`。
[:book:] `-u` 表示 upstream。
```
git push -u origin master
```
## Git 引用日誌 <font color=#FF4040 size=3>(Reference log)</font>
#### Git 查看引用日誌 <font color=#FF006F size=2>(主要可用於 reset )</font>
[:book:] `reflog` 表示reference log。
[:book:] 多筆情況下可以使用`Enter`顯示下一筆,反之輸入`q`則離開。
```
git reflog // 顯示如下
```
```
a68395d HEAD@{0}: reset: moving to HEAD^^
69b4a9f HEAD@{1}: reset: moving to HEAD^
:
```
#### Git 使用 reflog 退回或還原
[:book:] 可搭配`--mixed`, `--soft`, `--hard`。
```
git reset HEAD@{1}
```
## Git 分支 <font color=#FF4040 size=3>(Branch)</font>
#### Git 查看分支
[:book:] `-a`表示全部清單。
```
git branch -a
git branch
```
#### Git 建立分支
[:book:] `git branch <branch name>`, 指令如下。
```
git branch devBranch
```
#### Git 遠端直接建立分支
[:book:] `master:devBranch` 意思就是在遠端上建立有`master`內容的`devBranch`新分支。
```
git push origin master:devBranch
```
#### Git 獲取遠端分支
```
// git fetch <遠端主機名> <遠端分支名稱>:<本地分支名稱>
git fetch origin master:myBranch
```
#### Git 切換分支
```
git checkout devBranch
```
#### Git 刪除分支
[:book:] `-d` 一般刪除,`-D` 強制刪除。
```
git branch -d devBranch
git branch -D devBranch
```
#### Git 刪除遠端分支
[:book:] `:` 表示將空的內容推上至分支,即為刪除。
```
git push origin :devBranch
```
## Git 反追蹤 <font color=#FF4040 size=3>(Untrack)</font>
#### Git 移除追蹤特定【檔案】
[:book:] `git rm --cached <file name>。`, 指令如下
[:book:] `--cached` 表示快取 <font color=#0000FF size=3>(白話一點就是 Git 追蹤區)</font>。
```
git rm --cached ./my_file.txt
```
#### Git 移除追蹤特定【資料夾】
[:book:] `git rm --cached <directory>。`, 指令如下
[:book:] `-r` 表示recursive files <font color=#0000FF size=3>(白話一點就是遞迴該資料夾中的所有檔案)</font>。
```
git rm --cached -r myFolder/
```
#### Git 反追蹤後, 並且更新遠端
```
// git add <your file>
> git add ./my_file.txt
> git commit -m "Untrack File"
// git push <remote> <branch>
> git push origin master
```
---
## Git 切割樹 <font color=#FF4040 size=3>(Subtree)</font>
#### Git Subtree SOP <font color=#FF0F8F size= 3>(subtree 是基於分支作為切割管理)</font>
```
// 同步更新
> git add .
> git commit -m ""
> git push origin master
> git subtree split --prefix=Path --branch upm_Test
> git push origin upm_Test
// 新Tag
> git add .
> git commit -m ""
> git push origin master
> git subtree split --prefix=Path --branch upm_Test
> git tag v1.0.0_Test upm_Test
> git push origin upm_Test v1.0.0_Test // tag only
```
---
## Git 子模組 <font color=#FF4040 size=3>(Submodule)</font>
#### Git Submodule SOP <font color=#FF0F8F size= 3>(submodule 是基於 Repository 作為子管理)</font>
```
// [Add] 原主專案已有 git, 但是要新增已存在的文件作為 submodule
// 步驟1. (先將 submodule 清空並且 git 到另一個 repository)
> cd submodule_dir
> git rm --cached . -r
> git init (in submodule_dir)
> git add remote add origin <submodule.git>
> git add .
> git commit -m "your submodule commit"
> git push -u origin <branch> (submodule)
// 步驟 2.
> cd main_dir
> rm submodule_dir -rf (請先確保 submodule 已經 push 到 git 的 repository)
> git submodule add <remote.submodule.git> <local.submodule.clone.path>
> git add .
> git commit -m "add new submodule"
> git push origin master (main)
// [Add] 原專案 + submodule 都尚未 git
// 步驟1. 建立 submodule git
> cd submodule_dir
> git init (in submodule_dir)
> git add remote add origin <submodule.git>
> git add .
> git commit -m "your submodule commit"
> git push -u origin <branch> (submodule)
// 步驟 2.
> cd main_dir
> rm submodule_dir -rf (請先確保 submodule 已經 push 到 git 的 repository)
> git init (in main_dir)
> git add remote add origin <main.git>
> git add .
> git commit -m "your main commit"
> git push -u origin <branch> (submodule)
> git submodule add <remote.submodule.git> <local.submodule.clone.path>
> git add .
> git commit -m "add new submodule"
> git push origin master (main)
// [Push] 主專案 + Submodules (會 push 兩次: 一次 = submodule, 另一次 = main)
> cd submodule_dir
> git add . (in submodule_dir)
> git commit -m "modified submodule"
> git push origin master (submodule)
> cd main_dir
> git add .
> git commit -m "update submodule into main"
> git push origin master (main)
---
// [Update] 主專案 + Submodules
> git pull --recurse-submodules origin <branch> --rebase
or
> git pull --recurse-submodules origin <branch> --merge
---
// [Update] 主專案
> git pull origin <branch> --rebase
or
> git pull origin <branch> --merge
// [Update] Submodules
> git submodule update --remote --rebase
or
> git submodule update --remote --merge
---
// [Clone] 主專案 + Submodules
> git clone <main.git>
> git submodule update --init --recursive
or
> git clone --recursive <main.git>
```