# Git 常用指令備忘: checkut / reset / stash
## checkout
用來移動 HEAD 指標的指向,並且變更工作目錄( working tree files )的內容。
checkout 只會修改 HEAD 指標的位置與工作目錄內容,而不會異動到 branch 指標、 commit 和暫存區(staging area)的內容。
- `git checkout <branch name>` 切換分支。將 HEAD 指向指定的 branch,即把工作目錄內容替換為 branch 中的最新檔案
- `git checkout <commit id>` 將 HEAD 指向特定 commit,並將工作目錄替換為該 commit 內容
- `git checkout <file name>` 將指定檔案回復為未修改,即暫存區內或是最後一次提交的版本
- `git checkout .` 將工作目錄整個回復為未修改,即暫存區內或是最後一次提交的版本
:::info
`git checkout <file name>`、`git checkout .` 會把工作目錄內未提交的修改都清除掉,使用上要三思!
:::
當我們將 HEAD 指向某個 commit,而不是 branch 時,如果我們再提交 commit 時,這些 commit 將不會被連在任何 branch 上,而是處在斷頭的狀態( detached HEAD )。
斷頭狀態下建立的 commit 會比較難在被指向,必需使用 `git checkout <commit id>` 才能找到他。
### 解決 commit 斷頭的方法:
為他建立一個新的 branch 指向他,再將這個 branch 合併到工作分支上:
```bash
// 建立指向斷頭的 commit 的新分支 temp,如果正在該 commit 上,<commit id> 可以省略
$ git branch temp <commit id>
// 先回到工作分支
$ git checkout develop
// 將 temp 合併回工作分支
$ git merge temp
// 將 temp 分支刪除
$ git branch -d temp
```
:::info
要尋找 commit id,可以使用 `git reflog` 列出一個月內下過的 git 指令紀錄。
:::
## reset
移動 branch、HEAD 指標,並替換暫存區、工作目錄的內容。
`git reset <commit id>` 將目前 branch 指標指向特定 commit,並將拆出來的檔案丟回工作目錄。
`git reset HEAD^` 將目前分支指標退到 HEAD 所指向的前一個 commit,並將 commit 拆出來的檔案丟回工作目錄。其中,`HEAD^` 代表的是往前一個 commit,也可以用 `HEAD~1`; `HEAD^^` 往前兩個 commit,也可用 `HEAD~2`
如果目前正在 master 分支上,`git reset HEAD^` 與 `git reset master^` 的效果是一樣的。
:::info
如果對剛做完的 reset 反悔了,可以用這個指令回到 reset 前的 commit。
```bash
$ git reset ORIG_HEAD
```
:::
reset 有三種模式:mixed、soft、hard,沒有加參數時預設就是 mixed mode。三個模式都會移動 branch 指標,但其中差異是:
- `--mixed` 將 commit 拆出來的檔案放在工作目錄
- `--soft` 將 commit 拆出來的檔案放在暫存區
- `--hard` 將 commit 拆出來的檔案丟掉
例:
`$git reset master^`
將 master 往前回到上一個 commit,並將 commit 拆出來的檔案丟到工作目錄
`$git reset master^ --soft`
將 master 往前回到上一個 commit,並將 commit 拆出來的檔案移至暫存區
`$git reset master^ --hard`
將 master 往前回到上一個 commit,並將工作目錄及暫存區的變更刪除。
:::info
口語上使用"拆" commit 較好理解,但實際上 commit 並沒有被拆掉,只是移動了 branch 指標,並將工作目錄或暫存區的檔案替換。
:::
#### `git reset HEAD <file name>`
將暫存區內的檔案移回工作目錄。如果沒有指定檔案,就是將整個暫存區退回工作目錄。
## stash 儲存檔案修改的快照
#### `git stash`
將目前修改過的被追蹤檔和暫存的變更的快照保存到一個 stack 中。使用 `git stash` 儲存變更後,working tree files 會回到未修改的狀態。
當我們開發到一半尚未 commit,但希望可以 checkout 到其他位置時,可以用 `git stash` 將未 commit 的修改先暫存起來,待之後 checkut 回來在回存之前的進度。
> 如果沒有變更的狀態下,使用 `git stash` 會出現 `No local changes to save`
:::info
要 stash untracked 的檔案,要加上參數 -u
:::
#### `git stash list`
列出 stash 中保存的快照
```htmlmixed=
stash@{0}: WIP on develop-kris: c5fe7cc uBike 改版型 & 新增搜尋功能 / 天氣資訊日期顯示優化修改
stash@{1}: WIP on develop-kris: c5fe7cc uBike 改版型 & 新增搜尋功能 / 天氣資訊日期顯示優化修改
stash@{2}: WIP on develop: 6142006 [修改] 減少不必要的調整
(END)
```
#### `git stash apply <stash name>`
將 stash 回存到 working tree files。如果沒有指定 stash name,預設使用最新的。回存的 branch 沒有限定要跟儲存時是同一個,例如在 branch develop 下儲存的 stash,可以在 branch master 回存。
#### `git stash pop <stash name>`
套用 stash 並將它從 stash list 中移除。沒有指定 stash name,預設使用最新的。
#### `git stash drop <stash name>`
刪除某一筆 stash
---
## Get Branches that contain specific commit
```bash=
$ git branch --contains <sha1-commit-hash>
```
- 沒有加上參數,僅會列出本地分支
- 加上 -r 僅列遠端分支
- 加上 -a 本地遠端皆列出