git

Git(1) - Git reset

Git reset大概是我學最久的一個command 幾乎每次需要他的時候都要重新google 下完指令之後還挺常不知道為什麼目的達成了 曾經認真唸過一下 知道每個option會造成什麼後果之後就以為自己會了 可是卻不知道reset比我想像中的還要強一些 文章最後會提一下工作上遇到的例子

網路上教基礎git的連結很多 我不會著墨在太基礎的東西上面 主要會分享我很常搞錯或是我覺得比較有趣的指令

血淚經驗談 你沒搞懂為什麼之前 不要照網路上的git reset指令貼上執行 每次執行前想一下為什麼這樣可以達到你要的效果 想不通就再回來看這篇 想通了再執行

Let’s go!

HEAD也是很多初學者會搞不清楚的一個概念 說穿了也很簡單 就是

你的目前branch的最新的commit

假設這是目前的git log, HEAD指向master的最新的commit Alt text 所以今天你下了git checkout b1 Alt text HEAD就會指向你checkout的那個branch的tip

但有個例外 因為checkout並不只可以指定branch 還可以指定一個commit 所以當你checkout <commit>

Alt text 他的意思是說 嘿 既然你指定的是commit 我就生一個暫時的branch給你 你現在的確在一個branch上 只是這個branch沒有名字 你可以馬上checkout -b 生出一個branch 也可以commit在這個沒有名字的branch上面 Alt text

再說一次 HEAD就是你的目前branch的最新的commit 即使這個branch可能沒有名字

git reset

git reset [ –soft | –mixed | –hard] <commit>

git reset <commit> 的意思就是 把HEAD移到<commit>

這是現在repo的情況 Alt text D在stage裡面, E在working裡

我習慣想像成這樣 紅色就是還沒進staging 綠色就是還沒commit Alt text

這裡的repo是指local的 不是remote的 別忘了一個鐵則 Working >= Staging >= Repo

這樣具體化之後 之後的解釋會簡單很多

再說一次 git reset <commit> 的意思就是 把HEAD移到<commit> 就是把你現在這個branch的最新的commit移到你指定的commit

soft就是只動repo

mixed就是動repo還有staging

hard就是動repo還有staging還有working

你只要知道這三種用法就可以

現在的State是S0 因為等一下會一直回到現在的state, S0的HEAD在Repo的C

SOFT

soft 就是只動repo

這樣子 staging裡面就會有c和d, working 裡e一樣

git reset --soft <B commit>

Alt text

HEAD會跑到repo的B

MIXED/DEFAULT

狀態回到S0

mixed就是動repo還有staging

這樣子 staging裡面就什麼都沒有, working 裡c,d,e

git reset --mixed <B commit>

Alt text

HEAD會跑到repo的B

HARD

狀態回到S0

hard就是動repo還有staging還有working

這樣子 staging裡面就什麼都沒有, working 裡什麼都沒有

git reset --hard <B commit>

Alt text

HEAD會跑到repo的B

git reset第二種用法

git reset <commit> [--] <file>

第二種的用法只是第一種的一個特例 就是前面的soft/mixed/hard不能指定 就只能是mixed

然後後面可以指定單獨的file

(先不要管[--]) 這個用法就是你可以只針對一個file做出這個指令

git reset --mixed <commit> 

至於--(double hyphen) 是避免有些人會把檔案名稱取的跟branch名稱一樣 或是你有個檔案就叫做"HEAD" 你就必須用 --HEAD 而不是只用HEAD 但基本上你不胡搞 你是不需要用到這個的

還有一件事 如果你commit不給 default就是HEAD, 如果你file不給 default就是整個資料夾

恭喜走出十里坡

基本上git reset已經講完了 你看到一個git reset的command你就知道執行下去會發生什麼事 但看到command知道會發生什麼事只是學git的第一步而已 你要知道什麼時候要用這個command才是高手 以下說明常遇到 需要用git reset的例子

Unstage

Alt text 有沒有看到那精美的

(use "git reset HEAD ..." to unstage)

為什麼可以呢 因為default是–mixed 所以白話文就是把HEAD移到HEAD(就是不要動的意思) 然後staging跟著變 這就代表把staging的狀態跟HEAD的狀態搞成一樣

所以要是從S0下這個指令

git reset HEAD d

會變這樣

Alt text

雖然會變成這樣是因為staging變得跟repo一樣 但你用git status看一下

Alt text

感覺會像是d從staging變到working去了 所以結果看起來像是"unstage"

當然你要unstage所有的file就是

git reset HEAD

舉一反三 你要unstage而且要把他們從working拿掉 就是

git reset --hard HEAD 

Undo commit

這大概是數一數二常見的問題 stackoverflow上問如何undo last commit的這題有一萬多個讚 事實上也很簡單 原本狀態是這樣 Alt text 下這個指令後

git reset --soft HEAD~

就變這樣 輕鬆 Alt text

在錯的branch上開發

還有一個蠻常見的情況 你要開發前要先跟遠端的origin/master sync, sync完後你忘記換到自己的feature branch而是直接在master上開發 寫了兩個commit後突然發現寫錯branch 該怎麼辦呢

現在情況是這樣

Alt text

git branch feature_branch

複製一個新的branch 變成這樣 Alt text

git reset --hard origin/master

Alt text

git checkout feature_branch

Alt text

Squash commit

另一個用法 也是我寫這篇文章的原因 是我上禮拜要check in我的code進prod的時候 我必須把我所有local的commit變成一個commit 我們組的Tech lead問我說該怎麼做 我跟他說 嘿嘿我知道 用git interactive rebase 他說那個比較麻煩 要選來選去 只要下一個簡單的git reset就可以 我才大徹大悟 原來之前做學問沒有讀通 把簡單的事搞複雜了

假設現在要把CDE squash成一個commit push到remote 現在狀態是這樣 Alt text

這時候下個精美的

git reset --soft HEAD~3

就會變這樣 Alt text

那事實上上面那張跟下面那張是同一件事 Alt text

再下個git commit就搞定了

最後一題

最後來考一下這題 看大家是不是真的懂 小心是個陷阱題

這是現在的State:

Alt text

請問下完這個command之後

git reset --hard HEAD~1

會變成什麼樣子呢

(A) Alt text (B) Alt text (C) Alt text

答案是B 你答對了嗎?