---
title: 'Git 學習紀錄'
disqus: hackmd
---
## Git基礎
### Git指令練習網站
* `https://github.com/pcottle/learnGitBranching`
### VsCode 安裝Git相關套件
* Git Graph
* Git History
* GitLens — Git supercharged
### 常用git log
* git log --oneline --graph
* --oneline : 是 --pretty=oneline --abbrev-commit 的簡短用法
* --graph : 以 ASCII 在 log 輸出旁邊畫出分支的分歧及合併。
* git log 參數參考網址:http://jamestw.logdown.com/posts/238719-advanced-git-log
---
### 修改最近一次的commit紀錄
* git commit \-\-amend -m "XXXXX"
* git commit \-\-amend
* 沒有加上 **-m** 會跳出一個視窗來編輯訊息(vim編輯器)
---
### 追加調整內容( test.txt ) 到最近一次的commit
1. git add test.txt
2. git commit --amend --no-edit
* --no-edit:不編輯commit訊息
3. git push -f :強制覆蓋遠端分支
---
### git 復原檔案
* git checkout xxx 復原該份檔案
* git checkout . 復原所有檔案
* 概念:拿暫存區的檔案,覆蓋至工作區
* git checkout HEAD~2 xxx
* 拿兩個版本前的檔案,覆蓋至當前工作目錄,同時修改暫存區的內容
---
### 將commit 拆掉重做
* git **reset e12d8ef^**
* 表示指向**e12d8ef**這個commit的前一次
* git reset **HEAD^** \ git reset **master^**
* **指定退回哪個commit** : git reset f55f49d
* --mixed` 模式(`預設`),將目前commit至f55f49d中所修改的東西,全部退至**工作目錄**
---
### Commit拆出來的檔案
* `--mixed` 模式(`預設`):
* 丟回**工作目錄**
* 暫存區的檔案丟掉,但不會動工作目錄的檔案,Commit拆出來的檔案會留在工作目錄,但不會留在暫存區
* `--soft` 模式:
* 工作目錄、暫存區都不會被丟掉,只有HEAD移動
* Commit拆出來的檔案會直接放在**暫存區**
* `--hard` 模式:
* 將工作目錄、暫存區都丟棄,直接退回該commit時的狀態
---
### 救回 hard模式下,reset的某個commit
1. git log -g \ git reflog : 查看紀錄
2. 找到該commit的SHA-1碼
3. git reset --hard 0929fe2 #指向0929fe2,同時會復原該0929fe2 之前所做的commit紀錄
---
### HARD 是什麼東西?
* 是一個指標,指向某一個分支
* 通常把HEAD當作「目前所在分支」
---
### 只Commit 一個檔案的部分內容
* git add -p :
* 選y:將整個檔案已修改的部分全部加入到暫存區
* 選e:只想送出部分修改到暫存區,會開啟一個編輯器
* 將不想加入的部分,刪除後,離開即可
---
### 當前用戶信息
* 基礎指令:git **config**
* 顯示當前使用的**所有 Git 配置**:git config --list
* **當前倉庫**配置:git config **--local** --list
* **全局**配置:git config **--global** --list
* **系統範圍**配置:git config **--system** --list
* 查看**當前用戶信息**:
* git config `user.name`
* git config `user\.email`
* **全局配置用戶信息**:
* git config **--global** `user.name`
* git config **--global** `user.email`
* **全局**更改
* git config --global `user.name` "Your Name"
* git config --global `user.email` "your.email@example.com"
* **當前倉庫**更改
* git config `user.name` "Your Name"
* git config `user.email` "your.email@example.com"
## 分支基礎
### 常用指令
* git branch 查看分支
* git branch cat 新增分支 >> 名為cat
* git branch -m cat tiger 分支改名 >> 將分支名cat,修改成tiger
* git branch -M main 分支改名
* git branch -d tiger 刪除分支 >> 將分支 tiger刪除
* 如果tiger 分支還沒有被完全合併,會出現提示訊息:The bracnh "tiger" is not fully merged
* git branch -D tiger 強制刪除分支
* git checkout tiger 切換成tiger分支
* git checkout -b sister 創建並切換成 sister 分支
___
### 概念:
1. 分支只是指向某一個Commit的指標(重要)
2. 分支只是指向某ㄧ個commit,刪除指標並不會讓commit消失
3. 分支合併:合併「分支指向的那個Commit」=> 合併Commit(合併分支)
---
### git 切換分支
* 做了兩件事
1. 更新暫存區、工作目錄:該分支會指向那個Commit的內容來更新,但在分支切換前所做的修改則還是會停留在工作目錄
2. 變更HEAD位置
---
### git 如果調整到一半切換分支會發生什麼事情
* 狀況:目前在tiger分支,修改index.html(modified)、增加cat3.html(Untracked)
* git checkout master
* 確認 index.html的修改、cat3.html的增加是否還存在
* 結果:index.html的修改、cat3.html的增加 都停留在工作目錄,不受分支切換的影響
* 使用 git stash 來暫存狀態,在切換分支
___
### git stash 暫存目前狀態
* 使用git stash
* git stash -u 可以包括 untracked的狀態
* 還原暫存 git stash pop
* 瀏覽 git stash 列表 git stash list
* 最後存的檔案會在最上面喲!也就是 stash{0}
* 清除最新暫存 git stash drop
* 指定刪除的暫存 git stah drop stash{0} 刪除第一個暫存
* 清除全部暫存 git stash clear
---
### git merge xxx 合併分支
* 狀況:master 合併tiger分支,目前tiger新增cat1.html\cat2.html
* 步驟:
1. 切換成master分支:git checkout master
2. 合併tiger分支:git merge tiger
* 合併後結果:master 分支增加cat1.html\cat2.html
* tiger 分支來自master,合併分支時 >> git會自動選用「快轉模式」合併
* 不使用快轉模式:git merge `--no-ff` xxx
___
### git merge XX 合併分支-2
* 將tiger分支與dog分支合併
* tiger分支與dog分支 來自master分支
* git會產生一個額外的commit來處理
* 這個commit會指向二個commit,標記來自哪兩個分支
* 步驟
1. git checkout tiger
2. git merge dog
> Merge made by the 'recursive' stragtegy.
dog1.html | 0
dog2.html | 0
2 files changed, 0 insertions(+),0 deletion(-)
create mode 100644 dog1.html
create mode 100644 dog2.html
3. 編輯合併的訊息(自動出現Vim編輯器)
4. 離開編輯,完成合併
* tiger 會指向新的commit,內容來自dog分支
* Commit會指向cat跟dog分支
* HEAD會隨著tiger分支前進,dog分支的HEAD停留在原地
---
### 取消分支合併
* 指令: git merge --abort
---
### 救回沒有合併的分支
* 目標:刪除tiger分支後,救回tiger分支
* 步驟:
1. git branch -D tiger #刪除tiger分支,並提示是否要刪除未合併的分支(-d)
* 紀錄刪除訊息:Deleted branch tiger (was 705804d).
2. git branch new_tiger 705804d #建一個新分支,指向705804d這個commit
* 分支概念:分支只是指向某ㄧ個commit,刪除指標並不會讓commit消失
### 另一種合併方式(rebase)
* git rebase:重新定義分支的參考基準
* base:你這分支從哪裡生出來的
* rebase:等於修改歷史基礎
* 組合 new_tiger 與 dog 分支 <font color="#9400d3"> #兩個分支的base都是master分支</font>
* 步驟:
1. git checkout new_tiger
2. git rebase dog <font color="#9400d3">#將dog分支當做我新的參考基準</font>
>First, rewinding head to replay your work on top of it...
Applying: cat1 test1
Applying: cat2 test2
3. 將new_tiger分支會接到dog分支上面
* rebase與merge的明顯差異:rebase合併分支的話,Git不會特別做出一個專門用來合併的Commit
### Rebase 概念
* 流程圖
```mermaid
graph TD
A[b174a5]-->B[c68537]
C[28a76d HEAD\tiger]-->D[32bc96]-->E[053fb2 dog]-->F[b69eb6]
B-->G[e12d8e Master]-->H[XXXX]
F-->G
style A fill:#f6e6a1,color:#008000
style B fill:#f6e6a1,color:#008000
style G color:#f00
style E color:#f00
style C color:#f00
style D color:#007fff
```
* 說明
1. 「先拿c68537這個commit接到053fb2這個commit上」,因為c68537的原本的上一層Commit是<font color="#f00">e12d8e</font>,現在要接到<font color="#f00">053fb2</font>上,需要<font color="#f00">重新計算</font>這個Commit(<font color="#008000">c68537</font>)的SHA-1值,重新計算的值是<font color="#007fff">32bc96</font>
2. <font color="#008000">b174a5</font>要接到32bc96上面,重新計算的SHA-1值是<font color="#007fff">28a76d</font>
3. 原本tiger是指向b17a5這個Commit,<font color="#9400d3">現在要改指向</font>最後做出來那一個新的Commit(<font color="#007fff">28a76d</font>)
* 舊的commit(b174a5\c68537)
* 未立即被刪除,有一天會被git的回收機制處理掉
* 誰Rebase誰有差?
* 會使commit的歷史順序不同
### 取消Rebase
1. 使用Reflog:查看紀錄
1. git reflog
:::info
bd578fa (HEAD -> new_tiger) HEAD@{0}: rebase finished: returning to refs/heads/new_tiger
bd578fa (HEAD -> new_tiger) HEAD@{1}: rebase: cat2 test2
54eb930 HEAD@{2}: rebase: cat1 test1
92efbb7 (dog) HEAD@{3}: rebase: checkout dog
705804d HEAD@{4}: checkout: moving from master to new_tiger
:::
2. 發現到:705804d HEAD@{4}: checkout: moving from master to new_tiger <font color="#008000">=></font> <font color="#9400d3">做rebase前的最後動作</font>
3. git reset 705804d --hard 回到rebase前的狀態
2. 使用ORID_HEAD
* ORID_HEAD:特別紀錄點,會紀錄危險操作
* 分支合併、Rebase都是「危險操作」
* 步驟:
1. git checkout dog
2. git rebase new_tiger
3. git reset <font color="#f00">ORIG_HEAD</font> --hard <font color="#9400d3">#回到rebase前的狀態</font>
3. rebase發生衝突時,取消rebase
* git rebase --abort
### 合併發生衝突的處理
* 需求:將new_tiger分支與dog分支合併
* 針對各分支的index.html相同位置,做出調整,以便觸發合併衝突
* 步驟:
1. git checkout new_tiger
2. git merge dog <font color="#f00">#發生衝突</font>
:::danger
uto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
:::
3. git status
:::info
<font color="#000000">On branch new_tiger
You have unmerged paths.
(fix conflicts and run \"git commit\")
(use \"git merge --abort\" to abort the merge)
Changes to be committed:</font>
<font color="#008000">new file: dog1.html
new file: dog2.html</font>
<font color="#000000">Unmerged paths:</font>
<font color="#000000">(use "git add <file> ..." to mark resolution)</font>
<font color="#f00">both modified: index.html</font>
:::
4. 分支衝突處理:<font color="#f00">對 index.html做內容選擇</font>,是要用new_tiger分支或dog分支的內容
5. 處理完後:git <font color="#f00">add</font> index.html
6. 完成分支合併:git commit `-m` "xxxx"
* git reset ORIG_HEAD `--hard` <font color="#9400d3">#解除merge合併</font>
### Rebase 衝突處理
* 需求:new_tiger 重新定義基準 git rebase dog
* 發生衝途:rebase過程會中斷
* 步驟:
1. git checkout new_tiger
2. git rebase dog <font color="#f00">#發生衝突</font>
:::danger
<font color="#000000"> First, rewinding head to replay your work on top of it...
Applying: cat1 test1
Applying: cat2 test2
Applying: TEST merge ERROR car
Using index info to reconstruct a base tree...
<font color="#f00">M index.html</font>
Falling back to patching base and 3-way merge...
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
<font color="#f00"> error: Failed to merge in the changes.</font>
Patch failed at 0003 TEST merge ERROR car
<font color="#e6b800"> hint: Use 'git am --show-current-patch' to see the failed patch</font>
Resolve all conflicts manually, mark them as resolved with
<font color="#008000">"git add/rm <conflicted_files>"</font>, <font color="#007fff">then run "git rebase --continue".</font>
You can instead skip this commit: run "git rebase --skip".</font>
To abort and get back to the state before "git rebase", run "git rebase --abort".</font>
:::
3. 處理發生衝突的檔案:處理index.html
4. 處理完後:git add index.html
5. 繼續Rebase(完成):git rebase --continue
### 非文字檔發生衝突(Merge)
* 狀況:new_tiger分支合併dog分支,各分支都有一個同名的圖片檔
* 步驟:
1. git checkout new_tiger <font color="#f00">#發生衝突</font>
:::danger
<font color="#000000">warning: <font color="#f00">Cannot merge binary files: testimage.png </font>(HEAD vs. dog)
CONFLICT (add/add): Merge conflict in testimage.png
Auto-merging testimage.png
Automatic merge failed; fix conflicts and then commit the result. </font>
:::
2. git status
:::info
On branch new_tiger
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Changes to be committed:
<font color="#008000">new file: dog1.html
new file: dog2.html</font>
Unmerged paths:
(use "git add <file>..." to mark resolution)
<font color="#f00">both added: testimage.png</font>
:::
3. 選擇要使用new_tiger或dog分支的圖片
* 使用new_tiger分支:git checkout <font color="#f00">--ours</font> testimage.png
* 使用dog分支:git checkout <font color="#f00">--theirs</font> testimage.png
4. 處理完後:git <font color="#f00">add</font> testimage.png
5. 完成分支合併:git commit -m "xxxx"
### 從過去的某一個commit開啟分支
* 流程圖
```mermaid
graph TD
A[921fbb7f]-->B[4cbf089b]
C[705804d4]-->D[ab7eca6c]
B-->G[4f559e1b]-->H[bea0a519]-->I[0929fe29]
D-->G
style H fill:#f00
```
* 狀況:從bea0a519,開啟一個新分支
* 法一:
1. 切換至bea0a519 Commit:git checkout bea0a519
2. 建立切換至bird分支:git checkout -d bird
* 法二:git branch bird bea0a519
## 修改歷史紀錄
### 修改歷史訊息
* 需求:dbfccb4的訊息(cat) 調整
* 訊息調整,<font color="#f00"> git會產生新的Commit來代替舊的</font>
* 步驟:
1. git rebase -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font>
:::info
<font color="#000000"> pick dbfccb4 cat
pick d07dee5 dog1
pick 0aaeb33 dog2
</font>
:::
2. 編輯跳出的視窗,使用reword指令
:::info
<font color="#000000"> <font color="#f00"> reword</font> dbfccb4 cat
pick d07dee5 dog1
pick 0aaeb33 dog2
</font>
:::
3. 離開Vim編輯後,開始進行Rebase,會跳出Vim來編輯新Commit訊息
* 將cat 調整成 new cat
4. 存檔離開後,完成Commit的訊息調整
### 將多個Commit合併為一個Commit
* 流程圖
```mermaid
graph TD
A[639b2f8]-->B[ef30bbd]-->C[a91ced5]-->D[8b5ee11]-->E[b226606]-->F[a2e0040]
style A fill:#008000
style B fill:#008000
style C fill:#007fff
style D fill:#007fff
style E fill:#007fff
```
* 將639b2f8和ef30bbd合併為一個commit、8b5ee11和b226606合併一個一個commit
* 注意:互動模式的紀錄由上而下是從舊到新
* 步驟
1. git rebase -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font>
:::info
<font color="#000000">pick b226606 cat1
pick 8b5ee11 cat2
pick a91ced5 edd1
pick ef30bbd dog1
pick 639b2f8 dog2
</font>
:::
2. 編輯跳出的視窗,使用squash指令
:::info
<font color="#000000">pick b226606 cat1
<font color="#f00">squash</font> 8b5ee11 cat2
<font color="#f00">squash</font> a91ced5 edd1
pick ef30bbd dog1
<font color="#f00">squash</font> 639b2f8 dog2
</font>
:::
3. 上面的修改會發生下列的事
1. 639b2f8會跟前一個Commit ef30bbd 合併
2. a91ced5跟 8b5ee11合併,再跟前一個Commit b226606 合併
4. 離開Vim編輯後,開始進行Rebase,會跳出Vim來編輯新Commit訊息
* 有兩個合併Commit的操作,需要做兩次的Commit的訊息編輯(編輯第一個Commit訊息)
* 編輯Commit的訊息 >> Git會產生新的Commit來代替舊Commit
* 新流程圖
```mermaid
graph TD
A[4984b96]-->B[dbfccb4]-->C[a2e0040]
style A fill:#008000
style B fill:#007fff
```
* b226606\8b5ee11\a91ced5 合併後的commit => dbfccb4
* 639b2f8\639b2f8 合併後的commit => 4984b96
### 將一個Commit拆解成多個Commit
* 需求:拆解4984b96這個Commit
* 步驟:
1. git rebase -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font>
:::info
<font color="#000000"> pick dbfccb4 cat
pick 4984b96 dog</font>
:::
2. 編輯跳出的視窗,使用edit指令
:::info
<font color="#000000"> pick dbfccb4 cat
<font color="#f00">edit</font> 4984b96 dog</font>
:::
3. 拆Commit:git reset HEAD^ <font color="#9400d3">#當前HEAD的位置是在4984b96這個Commit</font>
4. 發現 dog1.txt\dog2.txt,拆出<font color="#008000">是放在工作區</font>且<font color="#007fff">Untracked狀態</font>
5. 將上面這兩個檔案分別Commit
6. 繼續rebase(完成):git rebase --countinue
### 在某些Commit之間再加新的Commit
* 需求:在b6a5fbd(dog1)跟dbfccb4(cat)這兩個Commit之間,在多增加兩個Commit
* 步驟:
1. git reabse -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font>
:::info
<font color="#000000"> <font color="#9400d3">pick dbfccb4 cat</font>
<font color="#9400d3">pick b6a5fbd dog1</font>
pick fd3cf39 dog2</font>
:::
2. 編輯跳出的視窗,使用edit指令 <font color="#9400d3">#需停在dbfccb4 cat</font>
:::info
<font color="#000000"> <font color="#f00">edit</font> dbfccb4 cat
pick b6a5fbd dog1
pick fd3cf39 dog2</font>
:::
3. 添加兩份檔案,並分別Commit
4. 繼續rebase(完成):git rebase --countinue
### 調整Commit順序
* 將1b632fb(dog1)\6060645(dog2)移動至dbfccb4(cat)後面
* 步驟:
1. git rebase -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font>
:::info
<font color="#000000"> pick dbfccb4 cat
pick 8ca3152 edd1
pick 6060645 dog1
pick 1b632fb dog2</font>
:::
2. 編輯跳出的視窗,調整Commit順序
<font color="#000000"> pick dbfccb4 cat
<font color="#f00"> pick 6060645 dog1
pick 1b632fb dog2</font>
pick 8ca3152 edd1</font>
3. 存檔離開後,完成Commit順序調整
### 刪除某些Commit
* 需求:刪除有dog訊息的Commit
* 步驟:
1. git reabse -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font>
:::info
<font color="#000000"> pick dbfccb4 cat
pick bd27a88 dog1
pick 3206c6d dog2
pick 82ab1ab edd1</font>
:::
2. 編輯跳出的視窗,使用<font color="#008000">drop指令</font>或<font color="#007fff">刪除該行</font>
:::info
<font color="#000000"> pick dbfccb4 cat</fon>
<font color="#000000"> drop 3206c6d dog2</font>
pick 82ab1ab edd1</font>
:::
3. 存檔離開後,完成Commit的刪除
### Revert 指令
* 概念:做一個新Commit,來取消不要的Commit
* 需求:取消最後這次的Commit(dog3)
* git revert HEAD --no-edit
* 取消Revert
* 法ㄧ:再開一次revert >> git revert HEAD --no-edit
* 法二:使用reset >> git reset HEAD^ --hard
### 什麼時候使用Revert
* 使用時機:多人協作專案
* 團隊的開發政策,可能無法使用reset
* 使用Revert指令做出一個「取消」的操作,對其他人來說不算是「修改歷史」,而是新增一個Commit
* Reset\Rebase\Revert 差別
| 指令 | 改變歷史紀錄 | 說明 |
| -------- | -------- | -------- |
| Reset | 是 | 把⽬前的狀態設定成某個指定的 Commit 的狀態,通常適⽤於尚未推出去的 Commit。 |
| Rebase | 是 | 不管是新增、修改、刪除 Commit 都相當⽅便,⽤來整理、編輯還沒有推出去的 Commit 相當⽅便,但通常也只適⽤於尚未推出去的 Commit。 |
| Revert | 否 | 新增⼀個 Commit 來反轉(或說取消)另⼀個 Commit 的內容,原本的Commit 依舊還是會保留在歷史紀錄中。雖然會因此⽽增加 Commit 數,但通常比較適⽤於已經推出去的 Commit,或是不允許使⽤ Reset 或 Rebase 之修改歷史紀錄的指令的場合。 |
## 標籤 Tag
### 使用標籤
* 標籤:「標籤(tag)」是⼀個指向某⼀個 Commit 的指標。
* 當做貼紙⼀樣看待,它就是貼在某個 Commit上的東⻄。
* 什麼時候使用標籤?:通常在開發軟體有完成特定的⾥程碑,例如軟體版號 1.0.0 或是 beta-release 之類的,這時候就很適合使⽤標籤做標記。
* 有兩種標籤:
* 輕量標籤(lightweight tag)=> 個⼈使⽤或是暫時標記⽤途
* 有附註標籤(annotated tag)=> 是軟體版號之類的⽤途
* 差異:訊息量的不同
* 展現標籤訊息指令: git show <font color="#996b1f"> 標籤名</font> <font color="#9400d3">=></font> git <font color="#f00">show</font> <font color="#996b1f"> big_cats</font>
#### 輕量標籤(lightweight tag)
* 概念:為輕量標籤僅是⼀個指向某個 Commit 的指標, <font color="#f00"> 沒有含有其
它的資訊</font>, <font color="#9400d3">所以 Git 比較推薦使⽤有附註的標籤(annotated tag)</font>。
* 需求:在44e9fe1(add lion and tiger)上,打上一個big_cats的標籤
* 指令:git <font color="#f00"> tag</font> <font color="#996b1f"> big_cats</font> <font color="#008000">44e9fe1 </font>
* git歷史紀錄 git log --oneline
:::info
<font color="#000000"> 9c5fd18 (HEAD -> master) add fish 1
e62a0b0 add pig1
<font color="#f00">44e9fe1 <font color="#996b1f"> (tag: big_cats)</font> add lion and tiger</font>
1683520 dog3</font>
:::
#### 有附註標籤(annotated tag)
* 需求:在e62a0b0(add pig1)上,打上一個big_pigs的標籤,並加上訊息 "Big Pigs are comming"
* 指令:git <font color="#f00">tag</font> <font color="#996b1f"> big_pigs </font><font color="#008000"> e62a0b0</font> <font color="#f00">-a -m "Big Pigs are comming" </font>
* -a:請 Git 幫你建立有附註的標籤
* -m:在做⼀般的 Commit ⼀樣輸入的訊息
#### 刪除標籤
* 需求:刪除<font color="#996b1f"> big_cats</font>這個標籤
* 指令:git tag <font color="#f00">-d</font> <font color="#996b1f"> big_cats </font>
* <font color="#f00">-d</font>:刪除標籤
* 刪除後訊息: Deleted tag 'big_cats' (was 44e9fe1)
## 其它常見狀況題與冷知識
### ⼿邊的⼯作做到⼀半,臨時要切換到別的任務
* 法一:Commit目前進度
* 步驟:
1. git add --all
2. git commit -m "not finish yet"
3. git reset HEAD^
* 法二:使用Stash
* 目前new_tiger分支狀態 git status
:::info
<font color="#000000"> On branch new_tiger
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
  <font color="#f00">modified: cat1.hml
  modified: cat2.hml
  modified: index.html</font>
no changes added to commit (use "git add" and/or "git commit -a")</font>
:::
* 步驟:
1. git <font color="#f00">stash</font> <font color="#9400d3">#將上述檔案的修改先「存」起來</font>
* <font color="#008000">Untracked 狀態</font>的檔案 <font color="#9400d3">沒有辦法被Stash</font>, <font color="#f00">需要額外使用 -u 參數</font>
2. git status 確認狀態
:::info
<font color="#000000"> On branch new_tiger
nothing to commit, working tree clean</font>
:::
* 檔案存到那邊去?
* git stash --list
* stash@{0}: WIP on new_tiger: fe4d60e TEST
* stash@{0} 是這個Stash的代名詞
* 撿Stash回來用
1. 確認目前Stash 列表 git stash --list
2. git stash <font color="#f00">pop </font>stash@{0}
* pop:將stash@{0}拿出來,套用在目前的分支上,並將剛套用過的Stash刪除
* 如果沒有指定套用的Stash,會從編號最小的開始
* 刪除Stash:git stash <font color="#f00">drop</font> stash@{0}
* 套用Stash:git stash <font color="#f00">apply</font> stash@{0}
* 套用+刪除Stash:git stash <font color="#f00">pop </font>stash@{0}
### 去除某一個檔案 filter-branch
* 需求:去除config/daatbase.yml
* 使用filter-branch:git filter-branch --tree-filter "rm -f config/daatbase.yml"
### 回復剛剛filter-branch 造成的結果
* 指令:git reset refs/original/refs/heads/master --hard
### 撿別的分支的Commit過來合併 cherry-pick
* 概念:不是將原有的Commit剪過來貼上,而是比較像是複製Commit的內容過來
* 接到new_tiger的內容需要重新計算,產生新的Commit
* 原本fish分支的Commit不會有變化,還是在原來的地方
* 需求:挑選fish分支的<font color="#008000">62451b2(add whale)</font>和<font color="#007fff">38d07c7(dd dolphin)</font>,合併至new_tiger分支
* 指令:git <font color="#f00">cherry-pick</font> 38d07c7 62451b2
### 撿過來但不先合併
* 需求:撿過來的Commit,<font color="#f00">先放置在暫存區</font>
* 添加 <font color="#f00">--no-commit</font>參數:git cherry-pick <font color="#f00">--no-commit</font> <font color="#008000">38d07c7 </font>
### 怎麼樣把檔案真正的從 Git 裡移掉
* 需求:完全刪除config/database.yml
* 步驟:
1. 添加 <font color="#f00">-f參數</font>: git filter-branch <font color="#f00">-f </font>--tree-filter <font color="#9400d3">"rm -f config/database.yml"</font>
2. 處理資源回收的事項
1. rm .git/refs/original/refs/heads/master
* 清除備份點
2. git reflog expire --all --expire=now
* 要求 Reflog 現在立刻過期(不然預設要等 30 天)
3. git fsck --unreachable
* 看到很多 Unreachable 的物件
4. git gc --prune=now
* 啟動 Git 的資源回收機制
3. 檢查⼀下是否都清除 git fsck
:::info
<font color="#000000">Checking object directories: 100% (256/256), done.
Checking objects: 100% (16/16), done.</font>
:::
### 本地分支更名 to 遠端分支
* 前提:**確認該分支本地是否有或為最新**
* 步驟:
1. 本地分支更名:`git branch -m oldName newName`
2. 刪除遠端分支:`git push --delete origin oldName `
* 或至gitlab\gitHub 將遠端分支刪除
3. 將更名後的分支推上:`git push -u origin newName`
### 遠端分支更名 to 本地分支更名
* 步驟:
1. 本地分支更名:git branch -m oldName newName
2. 設定目前分支的上游分支:git branch **-u** origin/dev
### 遠端倉庫-操作
* 查看當前遠程倉庫:git remote **-v**
* 添加倉庫:git remote **add** \<remote_name> \<remote_url>
* 刪除倉庫:git remote **remove** \<remote_name>
* **調整**:
* **倉庫名**(`remote_name`):git remote **rename** \<old_name> \<new_name>
* **倉庫**(`remote_url`):git remote **set-url** \<remote_name> \<new_remote_url>
### 修改舊有的commit
* 使用 **rebase**
* **情境**:當前有3個commit 需要 **對第一個commit** 增加\修改內容
* **commit**:
* **abc123** - 第一個提交
* def456 - 第二個提交
* ghi789 - 第三個提交(`最新的提交`)
* 向第一個提交 `abc123` 增加内容
* 步驟:
1. git rebase -i HEAD~**3**
* HEAD~3 表示對最近 3 個提交進行變基。
3. 編輯**目標**提交
* 編輯器會開啟一個**commit列表**,顯示以下內容:
```
pick abc123 First commit
pick def456 Second commit
pick ghi789 Third commit
```
* 將第一個commit **pick** 改為 **edit**
```
edit abc123 First commit
pick def456 Second commit
pick ghi789 Third commit
```
* **pick** abc123 First commit => **edit** abc123 First commit
* **儲存並退出編輯器**
3. **修改第一個提交**:
* rebase將**暫停在第一個提交**,終端將提示類似以下信息
```
Stopped at abc123... First commit
You can now amend the commit.
```
* **添加修改**:git add \<file1\> \<file2\>
* **提交修改**:使用 **--amend** 選項
* `git commit --amend`
4. **繼續rebase**:git rebase `--continue`
5. **循環 3/4的操作**,直到rebase完成
6. 强制推送(如果已推送到遠端):git pusg -f
* **中斷**:`git rebase --abort`
### origin/dev被rabase或force push後的處理
* 情境:
* `dev分支`被rabase或force push
* 當前有`f1` \ `f2`分支從`dev分支`中**切出**
* 說明:
* 本地的 `dev` 已經跟遠端歷史**不再共享共同祖先** (diverged)
* 因此你必須重設它,並且讓下游分支(`f1`, `f2`)**重新 rebase 到新的 dev 上**
* 作法:`f1` \ `f2`分支要手動 rebase 到新的基準(base)上
* 每個對應的分支都要個別手動調整
* 步驟:
1. **更新遠端資訊**
* git fetch origin
2. rebase `f1分支`
* git **checkout** `f1`
* git **rebase** `origin/dev`
3. rebase `f2分支`
* git **checkout** `f2`
* git **rebase** `origin/dev`
4. 處理dev
* git **checkout** dev
* git **reset** --hard `origin/dev`
* 把本地的`dev分支`**強制重設**成跟`遠端的dev`**一模一樣**
5. 處理`f1` \ `f2`分支的遠端 => rebase強制推送
* git push -f origin f1
* git push -f origin f2
* 若擔心風險,可以在 rebase 前備份:
* git branch backup/f1 f1
* git branch backup/f2 f2
### 本地分支與遠端分支有衝突
* **遠端分支為主**,二種方式:
* git pull `--rebase`
* **底層邏輯**:
1. 將本地commit暫時移除
2. 拉取遠端最新的commit
3. 把本地commit套用在最新的遠端上
* 需要處理衝突
* git pull origin \<brancj-name
* 將遠端分支的commit merge到本地,**產生一個新的merge commit**
* **本地分支為主**,強推
* git push -f
## git config 常見設定
* 使用者資訊
* git config --global user.name "你的名字"
* git config --global user.email "你的信箱"
* 編碼設定
* git config --global i18n.commitEncoding utf-8
* git config --global i18n.logOutputEncoding utf-8
* 編輯器設定
* 使用 git commit 時會開啟 VSCode 編輯 commit 訊息
* git config --global core.editor "code --wait"
* 憑證儲存方式
* git config --global credential.helper store # 儲存純文字憑證
* git config --global credential.helper cache # 暫存憑證(預設 15 分鐘)
* git config --global credential.helper manager # Windows 使用者
* git config --global credential.helper osxkeychain # macOS 使用者
* 顯示log美化
* git config --global alias.lg "log --oneline --graph --decorate --all"
* 設定換行符號(跨平台)
* git config --global core.autocrlf input # macOS / Linux
* git config --global core.autocrlf true # Windows
###### tags: `Git`