# [Udemy - Git] The Git & Github Bootcamp
###### tags: `Udemy 課程筆記` `前端筆記` `Git`

## 4, 26. The Committing Workflow Overview
### Git 可以分成三個區塊
- Working Directory 工作區
- Staging Area 暫存區
- Repository 儲存庫

#### 要如何讓 Git 知道目前的檔案?
初始化後,當有檔案被更新且儲存變動後,就需要使用 `git add`,讓檔案推到暫存區 Staging Area,這時再使用 `git commit` 就可以對暫存區的檔案寫 commit,完成 commit 後 Git 就會記錄此次記錄。
##### 註 `git add` 可以選擇多個檔案:`git add fileA fileB ...`,也可以全選:`git add .`。
## 5, 38. Fixing Mistakes With Amend
使用 `git commit --amend` 可以針對「上」一次 commit 更改。
#### 如果 commit 完之後發現漏了 `git add` 一些檔案怎麼辦?
- 直接 `git add fileName...` 後用 `git commit -amand`,這時候會出現上一次的 commit 的畫面
- 可以更改 commit,如果只是補檔案不更改 commit 的話就可以儲存了,因為已經有用 `git add` 把漏掉的檔案加入到暫存區了

(`git add` 漏了一個檔案)

(所以在 `git commit` 的時候也漏掉了)

(馬上用 `git add fileName` 把漏掉的檔案推到暫存區後再輸入 `git commit --amend`,就會到 commit 的編輯畫面。可以發現 test2.txt 也加入到暫存區了)

(如果沒有要更改 commit 的話就可以直接儲存了 `:wq`)
#### 如果想要移除目前暫存區的檔案要怎麼辦?
使用 `git reset fileName` 就可以把該檔案移出暫存區。

(不小心多選了 test2.txt 了)

(`git reset fileName` 把 test2.txt 移出暫存區了)
## 5, 39. Ignoring Files w/ .gitignore
可以在 `.gitignore` 裡面放入不想要被 Git 偵測的檔案(如 API keys、node_modules 等等)。不過要記得要建立在 `.gitignore` 之前的檔案並不會遵照 `.gitignore` 的規則(所以 Git 還是會抓到它們),這時就要先用 `git rm --cached` 把它們請出 Git 之後 `.gitignore` 才會把它們隱藏。
`.gitignore` 的[範例](https://gitbook.tw/chapters/using-git/ignore)
```javascript=
// 檔案名稱 .gitignore
// 忽略 secret.yml 檔案
secret.yml
// 忽略 config 目錄下的 database.yml 檔案
config/database.yml
// 忽略 node_modules 目錄及該目錄下的所有檔案
node_modules'/'
// 忽略所有 db 目錄下附檔名是 .sqlite3 的檔案
'/'db'/'*.sqlite3
// 忽略所有附檔名是 .tmp 的檔案
*.tmp
// 當然你要忽略自己也可以,只是通常不會這麼做
.gitignore
```
也可以從 [gitignore.io](https://www.toptal.com/developers/gitignore) 快速得到 `.gitignore` 檔案的模板。
## 5, 46. More Practice With Branching

(Git 預設的 master branch,及兩條以 master branch 第二個 commit 為基準建立的 branches(Ethel & Fitz))
#### 什麼是 branch(分支)?
如果把 commit 想成一個點(spot),那麼 branch 就可以想成串連 commits 的線。在每一條 branch (線)上都有屬於自己的 commit。
在合併(merge)以前是不會相通的,換句話說:branch 上的改動(commit)就是屬於該 branch 的,所以好好利用這個特性可以有效地把 project 分成若干的小 branch 分給不同的人實作完成 project。
#### master?還是 main?
master 是 Git 預設的 branch,main 則是 Github 在 2020 年開始設定的預設 branch 名稱。(所以現在 Github 預設的 branch 是 main)
#### 什麼是 head?

(`git log` 時可以看到 head)

head 就像是一個標記,它指向出目前 branch 最新的 commit。
藉由更換 branch 可以讓 head 指向該 branch 最新的 commit,使檔案更換到該 commit 時的狀態。
#### 該如何建立新的 branch?
`git branch branchName` 即可建立新的 branch,建立後可以用 `git branch` 查看目前所有的 branches。
#### 該如何更換 branch?
`git switch existedBranchName` 或者 `git checkout existedBranchName` 就可以更換到該 branch了。

(有兩個 branches:master & moreOptions,目前 head 在 master,所以會顯示 master 這條線最近的 commit 時的檔案狀態)

(`git checkout moreOptions` 切換 branch,所以 head 會指向 moreOptions 這條 branch 上最新的 commit,也會顯示該 commit 時的狀態)

(切換回 master 後就只會顯示該 branch 最新的 commit 狀態)

(有兩條 branch,藉由更換 branch 就可以取得該檔案在不同 branch 上最新的狀態)
#### 如何刪除 branch?
`git branch -d branchName` 即可刪除該 branch。
#### 如何刪除遠端的 branch?

(ref. [How to delete remote branches in Git](https://www.educative.io/edpresso/how-to-delete-remote-branches-in-git))
因為是遠端的資料,所以使用 `git push origin`(取得遠端的位置),再加上 flag `--delete remoteBranchName`。
範例
```shell=
git branch -a
# *master
# test
# remote/origin/master
# remote/origin/test
git push origin --delete test
# To <URL of your repository>.git
# - [deleted] test
```
#### 可以建立新的 branch 時馬上就把 head 指向該新建立的 branch 嗎?
`git switch -c branchName` or `git checkout -b branchName` 即可達到建立加指向該 branch 的需求。
## 6, How Git Stores HEAD & Branches
對一 repo `git init` 初始化後,在該 repo 就會新增一個隱藏的資料夾 .git。在此資料夾記錄 Git 所需要的資料。

(目前的 branch 為 test,所以在 .git/heads 變會指向 test)

(現在把 branch test 換成 branch main,所以 .git/heads 會指向 main)

(打開 .git/heads/main 發現 main 裡面保存著該 branch 最近一次的 commit 編號。)

(切換回 test,打開 .git/heads/test 發現 test 裡面也保存 test 這條 branch 最近的 commit 編號)
## 7, 53. An Introduction To Merging
透過 `git merge branchName` 可以將指令的 branch 與目前 HEAD 指向的 branch 合併。

(需要釐清的點:merge 是合併 branch,並不是特定的一個 commit;總是會與目前 HEAD 指向的 branch 合併)

(目前 HEAD 指向 Master 這條 branch,Bugfix 則是以 Master 為基準創造出來的 branch,並擁有自己的 commits)

(`git merge bugfix` 就可以把當前的 branch Master 與 bugfix 合併,這種只是在同一條線只補缺的點叫做「快轉模式(Fast Forward)」
#### 如果基準的 branch 後來又新增 commit,之後還可以 merge 嗎?
可以的,但是這時 Git 會在基準的 branch 上新增 commit 記錄(通常就不會再自行更改 commit 內容,會保留預設的內容:merge commit ...)。




#### 雖然同事與我在不同的 branch,但是要 merge 的時候發現我跟同事有更動到相同的 code 怎麼辦?
雖然 Git 很厲害,會自動偵查更新的部分,但是當同時更新相同的部分時,「人類」就需要告訴 Git 要怎麼處理這些相同的變動。

(會出現錯誤)

解決衝突(conflict)的心法
- 打開衝突的檔案
- 自行決定這個衝突(也就是相同地方的更動)要怎麼處理。比方來說選 A 或者兩者皆保留
- 移除 Git 在檔案中留下的衝突註解
- 最後就可以 commit 讓 Git 做版控
==雖然處理衝突在工作中是很常遇見的事情,但是最好還是養成開工前 pull 最新的版本下來,以便獲得最新的版本,這樣子就可以減少處理衝突的機會。==
## 8, 63. A Guide To Reading Diffs
`git diff` 可以查看檔案的不同之處。
### 先來解析 `git diff` 回傳的到底是什麼東東:
1. 比對的檔案

(佔位第一個為「A」,佔位第二個為「B」)
2. 比對檔案的編號(可以不同理它)

3. 註記

(「A」為 -,「B」為 +)
4. 區塊

(Git 不會顯示全部的檔案內容,而是把有更動的地方分成一個區塊顯示)
5. 區塊頭

(區塊頭由 @@ @@ 包覆,-3,4 則代表這個區塊的第四行開始擁有 - 開頭的註記為「A」的改動,反之 +3,5 則代表該區塊的第五行開始擁有 + 開頭註記為「B」的改動)
6. 改變的內容

(- 為 A,+ 為 B)
#### 要怎麼查看未推到暫存區(staging area)與目前 commit 的差異?
`git diff`

(`git diff` 只會發現「未」在暫存區的檔案)

(沒有把更動 `git add` 推到暫存區,所以 `git diff` 偵測的到該更動。但當更動推到暫存區,`git diff` 變偵測不到了)
#### 要怎麼比對更動(不管有沒有把更動推到暫存區)與目前 commit 的不同?
`git diff HEAD`

#### 要怎麼比對目前暫存區的更動與目前的 commit?
`git diff --staged` or `git diff --cached`

(記得要先把更動推到暫存區,這樣子 Git 才可以把暫存區的更動與目前的 commit 比對)
#### 要怎麼針對特定的檔案比對?比方來說有 a,b 及 c 的更動,但我現在只想要先看 a 的比對結果


(預設會比對全部有更動的檔案,並顯示結果)

(`git diff HEAD number.txt` 所以只會比對 number.txt 這個檔案)
#### 可以比對兩支 branches 嗎?
可以,`git diff branchA branchB` or `git diff branchA..branchB`。


(`git diff branchA..branchB -- fileName` 可以限定要比對哪個檔案)
#### 可以只比對兩個 commits 嗎?
可以 `git diff commit1..commit2` 如果要比對特定檔案 `git diff commit1..commit2 -- fileName`。

## 9, 74. Stashing Basics: Git Stash Save & Pop
`git stash` 可以保存 working directory(也就是未在暫存區)及 staging area(暫存區)的改動,讓我們可以不用 commit(在 Git 留下正式記錄),之後需要的時候也可以把 stach 的東西拿出來使用。

假設目前正在處理 master,但是被老闆叫去處理 puppy,在這種情況就可以使用 `git stash` 讓 Git 幫忙保存做到一半的更動。
#### 要怎麼把更動暫時保存起來?

`git stash` or `git stash save`。
#### 要怎麼把保存的東西叫出來呢?

`git stash pop` 會把最新的 stash 拿出來並刪除 stash 裡面的資料。
#### 不小心在錯誤的 branch 開工了,但未把更動推進暫存區(staging area),有辦法把工作區(working directory)的更動帶到其他 branch 嗎?
有的,借用 `git stash + git switch branchName + git stash pop` 的組合技。
1. 把目前未進暫存區的改動藏起來 `git stash`
2. 切換到正確的 branch `git switch branchName` / `git checkout branchName`
3. 把藏起來的更動放出來 `git stash pop`
4. 正確的 branch 的 working directory 就會有改動了
*ref: [moving changed files to another branch for check-in](https://stackoverflow.com/questions/7217894/moving-changed-files-to-another-branch-for-check-in)*
## 10, Undoing Changes & Time Traveling
使用 `git checkout + 各種不同的參數` 可以進入 Detached HEAD 的狀態,讓 HEAD 暫時指向過去某一個 commit 的時間點,達到將檔案恢復到該點的狀態。
#### 什麼是 Detached HEAD?

(回到過去時 Git 會顯示的提示)

(正常來說 Git 的 HEAD 會指向目前的 branch,而 heads/branchName 這個檔案會保存該 branch 最近的 commit)

(更準確來說,是 `repo/.git/HEAD` 中會保存當前的 branch,然後 `repo/.git/ref/heads/branchName` 會保存該 branch 最新的 commit hash)


(但是當我們回到過去的 commit 時,HEAD 會暫時指向該 commit)
這也是為什麼我們可以回到過去某個 commit 的原因,而 Detached HEAD 只是這個狀態的名稱。
下方是課程的簡報,可以更清楚地看出流程。

(HEAD 通常指向 branch,而不是 commit)



(所以當我們在 branch 上新增 commit,HEAD 還是會指向 branch,而不是 commit)




(用 `git checkout previous commit`,HEAD 就會暫時指向過去的那一個 commit)
#### 怎麼回到過去的某一個 commit?
`git checkout previous commit` 就可以讓 HEAD 暫時指向該 commit,達到檔案恢復到該 commit 時的樣子。
#### 那要怎麼切換回來呢?
`git checkout branchName`,因為 HEAD 基本上是指向 branch 的,只要更換 branch,那麼 HEAD 就會指向該 branch,並在 heads/branchName 保存該 branch 最新的 commit,這樣子檔案就會回到該 branch 最新的版本了。
#### 每次在同一條 branch 回到過去後想要回來都得輸入 `git checkout 該 branchName` 有點麻煩,有沒有快速回到上一個 branch 的方法?
`git checkout -` or `git switch -`, `-` 代表上一個 branch。
#### 有可以不用貼上過去 commit 的 hash 就回到過去的方法嗎?
`git checkout HEAD~1`... HEAD 表示當前的 commit,HEAD~1 則是表示上一個,以此類推。




(直接用 `git checkout commit hash` 回到過去某一個 commit)


(也可以用 `git checkout HEAD~X` 以目前 HEAD 為基準數來第 X 個回到 X)
#### 使用 Git 強制把改動全部恢復到最近一次的 commit(就不用一直 `cmd + z`)
`git checkout HEAD fileName` or `git checkout -- fileName` 可以強制把改動取消,把檔案恢復到最近的 commit 的狀態(不管有沒有推到暫存區與否)。

(沒有推入暫存區,用 `git checkout -- fileName` 強制刪除改動把檔案恢復到最近一次 commit 的狀態)


(原本的改動已經透過 `git add .` 推入暫存區了,但是用 `git checkout HEAD fileName` 直接強制取消,並且移除記錄)
#### 怎麼取消 commit?
`git reset commitHash` 可以把該 commitHash 以後的 commit 移除,但是==更動還是會存在,只是 Git 的 commit 記錄被刪除,提供我們把更動存放在另一個 branch。==



(在 Git 上會刪除 `git reset commitHash` 以後的 commit,但是更動還是會保留)


(delete this commit 已經被刪除,但是改動還是在 `git status`)
#### 我想要把 commit 及更動一起刪除,要怎麼做?
`git reset --hard commit`,把 commit 以及更動通通刪除。


#### `git revert` 另一種會告知同事取消 commit 改動的辦法
雖然與 `git reset` 相同,`git revert` 都可以取消 commit 的改動,但是 `git revert` 是會留下 commit 告知其他人我取消什麼 commit 的改動的。




(`git reset` 及 `git revert` 的比較)
==多人協作時最好還是使用 `git revert` 取消 commit 的改動,因為這個會留下新的 commit 告知其他人「我這邊取消 X commit 的改動了!」,避免其他人不知道為什麼 commit 對不起來的情況!==
## 11, 91. What Does Github Do For Us?
Github 是一個讓我們可以把 Git repo 放在網路上的平台,透過此網路及 Github 的幫助,我們可以在不同的電腦取得 Git 的檔案,也可以透過 Github 與同事協作專案。
所以當本地端有更動時,要記得推到 Github 上,讓雲端也有同步的改動(commits)。


#### 要怎麼從 Github 上取得別人的專案?
`git clone https/ssh` 都可以把他人的專案 clone 下來。clone 下來的檔案就會有 Git 了,所以不需要再 `git init` 初始化。



#### 明明已經都新增了 SSH Keys 為什麼在 push 之前還需要再重複輸入帳號密碼?
因為在 2021 年八月開始 Github 就不再支援使用帳號密碼 push 檔案,而需要使用 SSH Keys。
當完成 SSH Keys 設定的時候還需要把目的地設定為 `SSH` 形式,這樣子就可以省略 push 前的驗證了。

#### 那要怎麼把本地的檔案丟上去 Github 呢?
##### 假如本地已經有一個 Git repo 的話
- 在 Github 上建立新的 repo
- `git remote` 讓本地的 Git repo 與 Github 上的 repo 連結
- `git push` 使得 Github 上的 repo 可以與本地端的 Git repo 同步




(開頭是由本地端推向雲端,所以本地端需要事先處理 `git remote`)
##### 假如本地還沒有 Git repo,但是想直接在 Github 上建立 Git repo,之後再 clone 到本地端
- 在 Github 上建立新的 Git repo
- `git clone` 到本地端
- `git push` 更動,使得 Github 上的 repo 可以與本地端的 Git repo 同步





(因為是由雲端開頭,本地端 `git clone` 所以不需要再額外處理 `git remote`)
## 11, 97. A Crash Course on Git Remotes
`git remote add name url/ssh` 讓本地端的 Git repo 知道 push 的目的地是在哪裡。

(name 常用就是使用 `origin`,所以就不用特別取名)
#### `origin` 就是 `url/ssh` 的代名詞,所以 `git push` 時就不用再寫落落長的 `url/ssh` 了。


==`git push origin branchName` 如果 Github 上的 repo 沒有該 branch,便會先建立該 branch。==

(在本地建立 branch: new_empty)

(`git origin new_empty` 因為 Github 上沒有該 branch,所以直接建立)
****
#### 每次都要打 `git push origin branchName` 好麻煩,有沒有可以省略重複 `origin branchName` 的辦法?
第一次 push 的時候輸入 `git push -u origin branchName` 如果之後還需要從相同的 local branch push 就可以直接輸入 `git push`。因為 `-u` 已經告訴 Git 之後 `git push` 的目的地(origin)跟 branch(branchName)是哪裡了,就可以省略不再輸入。


#### 有辦法查看目前 remote 的詳細資料嗎?
`git remote -v` 可以看到 remote 的詳細資料(http/ssh)。
#### 有辦法更改 remote 的 http/ssh 嗎?
[有的](https://stackoverflow.com/questions/2432764/how-to-change-the-uri-url-for-a-remote-git-repository)
```javascript=
git remote set-url origin new.git.url/here
```
## 12, Fetching & Pulling

(clone 至本地後又新增 commits,在雲端的 Git 並不會同步更新)
#### 那要怎麼回去看一開始 clone 下來的檔案狀態?

(`git checkout origin/branchName` 就會以 detached HEAD 的狀態讓 HEAD 回到當初 clone 的 commit)
#### `git clone` 只會把 default 的 branch clone 下來,但是雲端 Git 上還有其他的 branches,有辦法在本地取得那些 branches 嗎?

(`git clone` 後本地只有雲端 Git 預設的 branch: main)
- `git switch remote-branchName` 本地 Git 會自動把雲端 Git 上對應的 branch 新增到本地裡
- `git checkout --track remoteName/branchName` 也會做到一樣的事情

(`git checkout --track origin/food` 把雲端的 branch: food 新增到本地中)
## 12, 110. Git Fetch: The Basics
`git fetch` 直接取得雲端上 Git 的改動,並切在本地新增對應的分支。
`git fetch <remote> <branch>` 如果不輸入 remote 就會是預設的 origin。


(雲端 master 的更動會存在於 `origin/master` 中,與本地的 master 並不相關)
## 12, 112. Git Pull: The Basics
`git pull <remote> <branch>` 不僅會下載該雲端上 branch 的改動,還會順便與當前 branch 合併(merge)。
- 如果沒有同步好的話可能會造成衝突
- it matters where runs `git pull <remote> <branch>`!因為指令的 branch 會與當前的 branch 合併



(`git pull origin/master`,雲端的 master 的改動會下載到本地,並切與當前 branch: master 合併)
#### 每次都要打 remote/branchName 有點麻煩,有沒有縮寫?
有的,可以只打 `git pull`,此時 Git 會根據當前本地的 branch pull 對應雲端的 branch。

(本地目前的 branch 決定 pull 雲端上哪條 branch)

(本地目前在 master,`git pull` === `git pull origin/master`)
## 15, 141. Comparing Merging & Rebasing
假如我現在與同事共同協作一個專案,我從主要的 branch 分支出來處理我負責的部分。結果過一陣子同事已經把他完成的東西更新至主要的 branch 了,為了讓我能夠清楚目前主要的版本長什麼樣子,所以我 `git merge` 主要的 branch,讓我自己的 branch 也可以有主要 branch 的改動。





==但是我的 branch 就會出現一堆因為 merge 所導致的 commit。==
#### 如果想「不要留下一堆 `git merge` 後留下的合併記錄」就可以用 `git rebase`(安靜地合併)。

(與 `git merge` 相同,要注意「何處」使用 `git rebase`,因為當前的 branch 會與 `git rebase targetBranchName` 合併)
#### 如果遇到衝突的時候該怎麼解決?
Git 會報錯提醒,這個時候我們需要決定在衝突點留哪一個更動。與一般的 `git merge` 解除衝突後推到暫存區後 commit 不同,我們推到暫存區後要下 `git rebase --continue` 繼續執行 rebase 的任務。

(這個時候就要仔細看 Git 顯示的提示,如果想放棄 rebase 就下 `git rebase --abort`)
發生衝突了:
* 如果知道該留下哪個改動
* 處理衝突
* 把有衝突的檔案 `git add fileName` 推到暫存區
* `git rebase --continue` 讓 Git 繼續執行 rebase 的任務
* 如果不知道該怎麼改
* 取消 rebase `git rebase --abort`
* 與同事討論該留下哪個改動
#### `git rebase` 以及 `git merge` 的差別?
* `git rebase`
* 會與目標 branch 合併,但是不會留下合併的記錄(commit)
* 起點 branch 的 commits 會加在目標 branch 最後新的 commit 之後
*`git merge`
* 合併的時候會有記錄(commit)留下記錄
#### 什麼時候該用 `git rebase`?什麼時候適合 `git merge`?
如果當前這條 branch 需要與他人共同使用的話就不要用 `git rebase`,因為其他人 pull 後會只看到「歷史被改變的結果」,並不會知道你在哪裡 `git rebase`。
如果自己的話就可以用 `git rebase`,讓自己工作的 branch 不會因為要 pull 最新的 master 而造成一堆 `git merge` 留下來的合併記錄。
## 16, 148. Rewording Commits With Interactive Rebase
`git rebase` 加上 `-i` 可以對當前 branch 的 commit 修改(如:修改 commit 名稱等...)。
`git rebase -i HEAD~number(由 HEAD 開始數到第幾個 commit)` 可以讓我們更改 commit。
==當自己的 branch 要與主要 branch 合併前最好都先 `git log --oneline` 查看 commit 需不需要整理。==

常用的指令:
* `-pick`:預設指令,告訴 Git 說這個 commit 沒問題,請繼續保留
* `-reword`:保留 commit 的改動,但是修改 commit message
* `-fixup`:保留該 commit 的改動,但是該 commit message 會被刪除,改動會自動與前一個最近的 `-pick` commit 合體
* `-drop`:捨棄該 commit 及變動
#### 怎麼更改 commit message 呢?

(我想要修改 commit hash `cbee26b` 的 commit message)
* `git rebase -i HEAD~9` 叫出從目前 HEAD 開始數到第九個 commit,並進入 rebase 的修改頁面

* 找到想要修改的 commit,也就是 `cbee26b`(這邊要從 commit hash,不要對原本 `git log --oneline` 的順序,因為順序會顛倒 -> 最新的在最下面)
* 把原本預設的 `pick` 改成修改 commit message 的 `reword`

* 隨後就會進入修改 commit 的頁面

* 更改儲存後就會發現原本的 commit message 被改掉了(更動後的 commit 以及其後面的 commit 都會重新得到新的 commit hash,但是其更動還是會維持,並不會改變)

#### 如果想要合併 commits 呢?

(`3bd5b80` 及 `ab41bd8` 的改動想要與 `785be4a` 合併;`faf532f` 的改動想要與 `985bc62` 合併)
* `git rebase -i HEAD~9` 一樣先抓出想要的更動的 commit 範圍
* 更改要被合併的 commits 的指令 `pick -> fixup`(Git 會自動往 `fixup` 上一個 commit 找,如果該 commit 是 `pick` 變回把 `fixup` 的 commit 與 `pick` 的 commit 合併 -> commit message 維持 `pick` 的)

* `fixup` 的 commits 都被合併了

#### 如果想要直接移除 commit 以及其改動呢?

(想要移除 `f836ca1`)
* `git rebase -i HEAD~2` 抓出 commit 區塊進入 rebase 的修改模式
* 找到要刪掉的 commit,更改指令 `pick -> drop`

* 完成

#### 依然要記得,如果這條 branch 有跟同事共用的話(比方來說 `master / main` 就不要用 `git rebase` 更改歷史!

> 1. 跟別人共享的 branch 不要 `git rebase`
> 2. 自己的 branch `git rebase` 結束後再 `git merge master`,讓主要的 branch 也有我的更動
==在 `git rebase -i` 的模式下也可以一次下多個指令,不用分次輸入。==

## 19, 175. Introducing Reflogs
`git log`(可以看到當前 branch ==活著的== commits),`git reflog show branchName` 則是可以看到當前 branch HEAD ==移動至哪一點 commit 的記錄==,可透過這個指令來尋找過往**全部的**記錄。
但要注意 `git reflog` 只會:
* 1. 保存本機 local 的記錄
* 2. 只會保存 90 天

## 19, 179. Time-Based Reflog Qualifiers
Git 還提供客製化的篩選,`branchName@{篩選條件}` 可以得到符合篩選條件的 commits。

## 19, 180. Rescuing Lost Commits With Reflog
因為 `git reflog show branchName` 會保存本地的所有 commit 資料,所以如果想要救回被刪掉的 commit(無法直接透過 `git log` 查看的,因為 `git log` 只看得到當前 branch 活著的 commits),可以使用 `git reflog show branchName` 得到 commit hash 後再 `git reset --hard commit hash` 把該 commit 變回活著的狀態。

(目前活著的 commits 有四個)

(捨棄 `1fd7293` 以後的 commits)

(Oh no! 我想要救回被我殺掉的 commits,`git reflog show branchName` 找到 HEAD 移動過的 commit hash)

(`git reset --hard` 想要救回的 commit)

(`git log --oneline` 看到救回的 commits了)

(`git reflog show branchName` 又新增多筆 HEAD 移動時的 commits)
#### `git reflog show branchName` 到底保存了什麼東東?
**只要 HEAD 有移動的話 Git 就會保存記錄**,HEAD 確實是指向 branch,但是我們新增 commit,HEAD 還是指向該 branch 但是會移動到該 branch 最新的 commit,Git 就會記錄該 commit hash。也因為如此,當我們使用 `git reset --hard commit-hash` 刪除 commits 的時候,也會造成 HEAD 的移動,所以 Git 也會記錄。

細看發現有重複的 commit hashes: `2c7445` 以及 `1fd7293`。
* `2c7445`:
* 第一次 -> 新增 commit(所以 HEAD 會移動到該點上,因為在那個時間點上,它是當前最新的 commit)
* 第二次 -> `git reset --hard` 後救回來的記錄,因為它在這個時間點上又是該 branch 最新的 commit,所以 Git 會記錄
* `1fd7293`:
* 第一次 -> 新增 commit(所以 HEAD 會移動到該點上,因為在那個時間點上,它是當前最新的 commit)
* 第二次 -> `git reset --hard` 捨棄 `1fd7293` 以後的 commits,所以在該 branch 上,這個 commit 成為最新的 commit,所以 HEAD 會移動到這個點上,觸發 Git 記錄的條件:HEAD 移動過就保存
如果想要恢復 `git rebase -i` 前的 commit,也可以用 `git reflog show branchName` 得到詳細的記錄(commit hash)再 `git reset --hard commitHash` 恢復 commits。
## 課程
[The Git & Github Bootcamp](https://www.udemy.com/course/git-and-github-bootcamp/)