今天恰巧遇到了需要回退分支的問題,我之前一直在用的都是git reset
,自認為清楚明瞭,也就沒去看還有其他的回退方法。今天忽然被問到,特意查了查。
git reset
develop ----1 3-----
/
branch a a
複製程式碼
develop將a分支合併後,想要不留痕跡的撤回合併。這個時候用git reset
就是很好的選擇了。
具體的操作步驟如下:
- 切換分支到develop
- git log 檢視當前分支日誌
例如我的日誌是這樣的:
commit 3
Merge 1 2
Author: admin <admin@163.com>
Date: Wed May 30 15:00:00 2018 +0800
Merge branch `feature/a` into `develop`
close a
See merge request !20
commit 2
Author: admin <admin@163.com>
Date: Wed May 30 14:00:00 2018 +0800
close a
commit 1
Author: admin <admin@163.com>
Date: Wed May 30 13:00:00 2018 +0800
init project
複製程式碼
我要將develop回退到合併之前的狀態,那就是退到 commit 1這了,將commit號複製下來。退出編輯介面。
- 講一下git reset引數定義,具體見官網
--soft 回退後a分支修改的程式碼被保留並標記為add的狀態(git status 是綠色的狀態) --mixed 重置索引,但不重置工作樹,更改後的檔案標記為未提交(add)的狀態。預設操作。 --hard 重置索引和工作樹,並且a分支修改的所有檔案和中間的提交,沒提交的程式碼都被丟棄了。 --merge 和--hard類似,只不過如果在執行reset命令之前你有改動一些檔案並且未提交,merge會保留你的這些修改,hard則不會。【注:如果你的這些修改add過或commit過,merge和hard都將刪除你的提交】 --keep 和--hard類似,執行reset之前改動檔案如果是a分支修改了的,會提示你修改了相同的檔案,不能合併。如果不是a分支修改的檔案,會移除快取區。git status還是可以看到保持了這些修改。 複製程式碼
a分支的程式碼我不需要了,以後應該也不需要了。因此:
git reset 1(貼上過來的commit號) --hard
a分支程式碼我還是想要的,只是這個提交我不想要了:
git reset 1
git log
檢視一下:
commit 1
Author: admin <admin@163.com>
Date: Wed May 30 13:00:00 2018 +0800
init project
複製程式碼
是我想要的沒錯了,可以push到遠端去了。(這一步很危險,一定要確認好reset的結果確實是你想要的結果,否則,丟失了程式碼就不要怨我了)
git push origin develop
![rejected] develop -> develop (non-fast-forward)
error: 無法推送一些引用到 `git@github.cn:...`
提示:更新被拒絕,因為您當前分支的最新提交落後於其對應的遠端分支。
。。。
複製程式碼
好吧,我的分支確實落後於遠端的develop分支。我需要--force
。
git push origin develop --force
Total 0 (delta 0), reused 0 (delta 0)
To git@...
+ 83***...23***** develop -> develop (forced update)
複製程式碼
可以了,回到github檢視network。嗯,就是想要這樣的。
git revert
還是這個例子
develop ----1 3-----
/
branch a a
複製程式碼
還是之前的需求,不想要合併a,只想要沒合併a時的樣子。
操作步驟:
- 切分支到develop:
git checkout develop
- 檢視日誌:
git log
,還是上面的日誌:
commit 3
Merge 1 2
Author: admin <admin@163.com>
Date: Wed May 30 15:00:00 2018 +0800
Merge branch `feature/a` into `develop`
close a
See merge request !20
commit 2
Author: admin <admin@163.com>
Date: Wed May 30 14:00:00 2018 +0800
close a
commit 1
Author: admin <admin@163.com>
Date: Wed May 30 13:00:00 2018 +0800
init project
複製程式碼
這次和git reset
不同的是我不能複製 commit 1
這個commit號了,我需要複製的是commit 2
的commit號。因為revert後面跟的是具體需要哪個已經合併了的分支,而並不是需要會退到哪的commit號。
- 開始回退:
git revert 2
Revert "close a"
This reverts commit 2
#.......
複製程式碼
這是相當於又新增了一個commit,把a分支上的所有修改又改了回去。
- Ctrl+X離開編輯commit資訊頁面。
git log
檢視一下是不是我想的那樣
commit 4
Author: admin <admin@163.com>
Date: Wed May 30 17:00:00 2018 +0800
Revert "close a"
This reverts commit 2
commit 3
Merge 1 2
Author: admin <admin@163.com>
Date: Wed May 30 15:00:00 2018 +0800
Merge branch `feature/a` into `develop`
close a
See merge request !20
commit 2
....
複製程式碼
確實是新增加了一個commit,檢視程式碼發現a分支的修改都不存在了,也達到了我想要的效果。
- push的遠端伺服器上
git push origin develop
檢視network,是這樣的:
develop ----1 3-----revert a------
/
branch a a
複製程式碼
如此看來,git reset
和git revert
都能實現我現在的需求,那這兩個到底有什麼區別呢,在網上查了這個問題,我覺得說的有些抽象,看的不是很明白,於是自己實踐了之後才明白。
區別
-
git revert是用一次新的commit來回滾之前的commit,git reset是直接刪除指定的commit。
這個很好理解,在剛才的操作中我們看日誌已經可以看到這個現象。
git reset
操作之後,我們檢視上面例子的network已經可以看到network中只有commit 1
,分支a
和合併分支後的commit 3
都消失了;git revert
操作之後,network中還是可以看到a分支和合並a分支的操作,只不過在其基礎上又增加了一個revert的commit而已。 -
git reset 是把HEAD向後移動了一下,而git revert是HEAD繼續前進,只是新的commit的內容和要revert的內容正好相反,能夠抵消要被revert的內容。
這個也是可以清晰明瞭的看到,我就不做過多的解釋了
-
在回滾這一操作上看,效果差不多。但是在日後繼續merge以前的老版本時有區別。因為git revert是用一次逆向的commit“中和”之前的提交,因此日後合併老的branch時,導致這部分改變不會再次出現,但是git reset是之間把某些commit在某個branch上刪除,因而和老的branch再次merge時,這些被回滾的commit應該還會被引入。
git revert
現在的需求是我之前已經把a分支revert了,但是我現在又需要a分支的程式碼了,我之前都寫過一遍總不能再重新寫一遍了。我首先想到的方法,把a分支再merge到develop不就好了。
git merge a 複製程式碼
結果
Already up-to-date 複製程式碼
啥?因為我們之前提交合並的a分支的程式碼還在,因此我們並不能在重新合併a分支。
解決辦法:使用revert之前revert的commit號。在上面的例子中就是
git revert 4
。於是又新增了一個commit,把之前revert的程式碼又重新revert回來了。具體參考git reset
依舊是上面的需求。
develop -----1-----4-------5------6 / / / feature b c d 複製程式碼
現在我將develop reset到commit 4這裡,繼續提交我以後的程式碼
develop -----1-----4-------7------8 / \ / feature b e ------5----d -c-/ 複製程式碼
我現在想重新合併d分支的程式碼
develop -----1-----4-------7------8------9 / \ / / feature b e / ------5----d/ -c-/ 複製程式碼
合併過來大概是這個樣子的。也就是說d分支之前已經合併過c分支,因此如果還想要合併d分支的話,c分支的程式碼就會同步的帶過來。
應用場景
講到這裡大部分常用的應用場景也就應該是比較清晰的了。
- 如果回退分支的程式碼以後還需要的話用
git revert
就再好不過了;
如果分支我就是提錯了沒用了還不想讓別人發現我錯的程式碼,那就git reset
吧 - 例如:develop分支已經合併了a、b、c、d四個分支,我忽然發現b分支沒用啊,程式碼也沒必要,這個時候就不能用reset了,因為使用reset之後c和d的分支也同樣消失了。這時候只能用
git revert b分支commit號
,這樣c和d的程式碼依然還在。
總結
我能想到的大概就是這些了,當然在具體專案上我們會遇到很多沒遇到過的情形。我的建議是不要盲目的查詢類似問題後就去修改,因為他的問題並不一定就等同於你的問題。我建議去建立測試的分支,等真正整明白了,再去整線上的分支也不遲。
第一次寫了這麼詳細的一篇文章,主要也是因為網上查到的知識,對git知識體系一般的人來說,看的比較費解。