Git(1) - Git reset
March 30, 2017Git reset大概是我學最久的一個command 幾乎每次需要他的時候都要重新google 下完指令之後還挺常不知道為什麼目的達成了 曾經認真唸過一下 知道每個option會造成什麼後果之後就以為自己會了 可是卻不知道reset比我想像中的還要強一些 文章最後會提一下工作上遇到的例子
網路上教基礎git的連結很多 我不會著墨在太基礎的東西上面 主要會分享我很常搞錯或是我覺得比較有趣的指令
血淚經驗談 你沒搞懂為什麼之前 不要照網路上的git reset指令貼上執行 每次執行前想一下為什麼這樣可以達到你要的效果 想不通就再回來看這篇 想通了再執行
Let’s go!
HEAD
HEAD也是很多初學者會搞不清楚的一個概念 說穿了也很簡單 就是
你的目前branch的最新的commit
假設這是目前的git log, HEAD指向master的最新的commit 所以今天你下了git checkout b1 HEAD就會指向你checkout的那個branch的tip
但有個例外 因為checkout並不只可以指定branch 還可以指定一個commit 所以當你checkout <commit>
他的意思是說 嘿 既然你指定的是commit 我就生一個暫時的branch給你 你現在的確在一個branch上 只是這個branch沒有名字 你可以馬上checkout -b 生出一個branch 也可以commit在這個沒有名字的branch上面
再說一次 HEAD就是你的目前branch的最新的commit 即使這個branch可能沒有名字
git reset
git reset [ –soft | –mixed | –hard] <commit>
git reset <commit> 的意思就是 把HEAD移到<commit>
這是現在repo的情況 D在stage裡面, E在working裡
我習慣想像成這樣 紅色就是還沒進staging 綠色就是還沒commit
這裡的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一樣
HEAD會跑到repo的B
MIXED/DEFAULT
狀態回到S0
mixed就是動repo還有staging
這樣子 staging裡面就什麼都沒有, working 裡c,d,e
HEAD會跑到repo的B
HARD
狀態回到S0
hard就是動repo還有staging還有working
這樣子 staging裡面就什麼都沒有, working 裡什麼都沒有
HEAD會跑到repo的B
git reset第二種用法
git reset <commit> [--] <file>
第二種的用法只是第一種的一個特例 就是前面的soft/mixed/hard不能指定 就只能是mixed
然後後面可以指定單獨的file
(先不要管[--]) 這個用法就是你可以只針對一個file做出這個指令
至於--(double hyphen) 是避免有些人會把檔案名稱取的跟branch名稱一樣 或是你有個檔案就叫做"HEAD" 你就必須用 --HEAD 而不是只用HEAD 但基本上你不胡搞 你是不需要用到這個的
還有一件事 如果你commit不給 default就是HEAD, 如果你file不給 default就是整個資料夾
恭喜走出十里坡
基本上git reset已經講完了 你看到一個git reset的command你就知道執行下去會發生什麼事 但看到command知道會發生什麼事只是學git的第一步而已 你要知道什麼時候要用這個command才是高手 以下說明常遇到 需要用git reset的例子
Unstage
有沒有看到那精美的
(use "git reset HEAD
為什麼可以呢 因為default是–mixed 所以白話文就是把HEAD移到HEAD(就是不要動的意思) 然後staging跟著變 這就代表把staging的狀態跟HEAD的狀態搞成一樣
所以要是從S0下這個指令
會變這樣
雖然會變成這樣是因為staging變得跟repo一樣 但你用git status看一下
感覺會像是d從staging變到working去了 所以結果看起來像是"unstage"
當然你要unstage所有的file就是
舉一反三 你要unstage而且要把他們從working拿掉 就是
Undo commit
這大概是數一數二常見的問題 stackoverflow上問如何undo last commit的這題有一萬多個讚 事實上也很簡單 原本狀態是這樣 下這個指令後
就變這樣 輕鬆
在錯的branch上開發
還有一個蠻常見的情況 你要開發前要先跟遠端的origin/master sync, sync完後你忘記換到自己的feature branch而是直接在master上開發 寫了兩個commit後突然發現寫錯branch 該怎麼辦呢
現在情況是這樣
複製一個新的branch 變成這樣
Squash commit
另一個用法 也是我寫這篇文章的原因 是我上禮拜要check in我的code進prod的時候 我必須把我所有local的commit變成一個commit 我們組的Tech lead問我說該怎麼做 我跟他說 嘿嘿我知道 用git interactive rebase 他說那個比較麻煩 要選來選去 只要下一個簡單的git reset就可以 我才大徹大悟 原來之前做學問沒有讀通 把簡單的事搞複雜了
假設現在要把CDE squash成一個commit push到remote 現在狀態是這樣
這時候下個精美的
就會變這樣
那事實上上面那張跟下面那張是同一件事
再下個git commit就搞定了
最後一題
最後來考一下這題 看大家是不是真的懂 小心是個陷阱題
這是現在的State:
請問下完這個command之後
會變成什麼樣子呢
(A) (B) (C)
答案是B 你答對了嗎?