工作以後發現是git指令常常忘記又要一直查,所以這邊整理我遇到的問題。
我有很多不需要的改動,要怎麼清空?
這邊介紹最容易搞混的三個指令:git reset, git restore, git clean -fd

影響區域
以上張圖是我自己畫的,箭頭指向的位置表示影響區域:
git restore是把 Working Directory 的Modified (Tracked) Files清除,恢復成 Index 的狀態- 情境:我只是亂改了一堆檔案,但 add 過的東西我想留著
git reset --hard HEAD是把 Index 和 Working Directory 都強制對齊到 HEAD commit- 注意上圖的例子都是reset搭配HEAD,事實上也可以直接指定要回到哪一個hash的狀態:
git reset --hard <hash> // 小心使用,非常暴力
- 注意上圖的例子都是reset搭配HEAD,事實上也可以直接指定要回到哪一個hash的狀態:
git clean -fd- 只有這個指令可以處理Untracked Files
- -f 代表force,一定要-f才能真正刪除
- -d 代表會recurse into untracked directories,如果有untracked files躲在資料夾裡面就要用這個
- 如果想要安全一點可以先用
git clean -fdn # n = dry-run,不真的刪
補充:git reset的三種模式
- —soft:只後悔 commit,不後悔
git add - —mixed(預設):後悔 commit,也後悔
git add - —hard:全部後悔
| 模式 | HEAD | Staging Area (Index) | Working Directory |
|---|---|---|---|
--soft | ✅ 移動 | ❌ 不動 | ❌ 不動 |
--mixed(預設) | ✅ 移動 | ✅ 清空 | ❌ 不動 |
--hard | ✅ 移動 | ✅ 清空 | ✅ 清空 |
git reset --soft HEAD在我有很多不需要的改動,要怎麼清空?這個情境沒有任何作用,因為把HEAD移動到HEAD沒有退回任何commit,再加上--soft是 staging area 和 working directory 都不動,所以整體沒有作用。
--soft 只有在指定 <hash>、且你刻意想保留 staged 狀態(例如改 commit message、拆 commit)時才有意義;相較之下,--mixed 更符合大多數「反悔 commit」的實際需求。
其他情境
-
把 file.txt 的內容,強制換成 <hash> 這個 commit 裡的版本
git checkout <hash> -- file.txt git restore --source=<hash> file.txt // 這是新版 Git 推薦的用法,比 checkout 更安全 -
不小心commit不需要的檔案,想把檔案從版本控制中完全移除
git rm --cached <file> git commit
觀念
Fetch & Pull
git fetch是只fetch當前branch的upstream branch還是全部都會fetch?- Answer: 全部的upstream branch都會fetch
- 問題同上,那
git pull呢?- Answer:
git pull=git fetch+git merge <目前 branch 的 upstream branch> - 所以從第一點知道,
git fetch是全部的branch都會fetch,但是git merge只會merge「目前 branch 所追蹤的 upstream branch」
- Answer:
git reflog (reference log)
怎麼看
你會看到類似這樣的輸出:
```
a1b2c3d HEAD@{0}: reset: moving to HEAD~1
d4e5f6g HEAD@{1}: commit: add login api
h7i8j9k HEAD@{2}: checkout: moving from main to feature/login
```
| 部分 | 意思 |
|---|---|
a1b2c3d | 當時 HEAD 指向的 commit |
HEAD@{1} | reflog 索引(時間序,0 是最新) |
reset / commit / checkout | 你做了什麼事 |
怎麼用
-
情境 1:
git reset --hard之後後悔git reset --hard HEAD~1幹,我要那個 commit
解法:
git reflog找到 reset 前的那一筆,例如:
d4e5f6g HEAD@{1}: commit: add login api救回來:
git reset --hard d4e5f6g -
情境 2:分支刪掉了
git branch -D feature/login解法:
git reflog找到那個分支最後的 commit:
h7i8j9k HEAD@{3}: commit: finish login feature救分支:
git checkout -b feature/login h7i8j9k -
情境 3:rebase 完全亂掉
git rebase main結果衝突解到有問題。
解法:
git reflog找到 rebase 前那筆:
x1x2x3x HEAD@{5}: checkout: moving from main to feature回去:
git reset --hard x1x2x3x