rebase與merge其實都是完成相似的工做的,可是背後的工做方式卻有很大的不一樣,讓咱們用如下兩張圖來講明一下二者的區別git
第一張圖是merge的工做原理,當咱們在mywork分支中調用git merge origin
,git就會根據c2,c4,c6這三個點來計算合併出一個新的commitc點c7。3d
第二張圖則是rebase的工做原理,當咱們在mywork分支中調用git rebase origin
時,c5變成了c5',c6變成了c6',而且父commit指向了c4,而原來的c5和c6就沒有用了。指針
從上面就能夠看出,在執行git rebase後會將兩條分支有變成在同一條commit鏈上,而merge則是依然能夠看到分叉的commit鏈,背後呢其實就是等於rebase修改了git的提交歷史,把以前分支上的commit一個一個接到另外一條分支上code
固然rebase的過程當中也同樣有可能會產生衝突,這個時候只要咱們碰到一個衝突,就須要解決它,而後執行git add
添加,接着執行git rebase --continue
去轉接下一個commit,直到全部commit都轉接到了另外一條分支上。cdn
在rebase進行中的任什麼時候刻,咱們均可以經過執行git rebase --abort
將分支恢復到rebase以前的狀態。對象
rebase的好處就是會讓咱們的commit鏈變得整潔好看,可是對於使用rebase須要注意的點就是不要在master分支上執行,由於一般咱們在master分支上的是相對穩定的代碼,咱們不會想要去修改到它的歷史,另外,不要對已經推送到遠程的分支進行rebase,一般只對本地還未推出的分支作,由於遠程的分支可能其餘的用戶已經將它們拉取到各自的本地版本庫,咱們不但願他們由於咱們執行了rebase而要更改本身本來的提交歷史,這即使對git來講也很難處理,會出現一些意想不到的狀況。若是是上述這些場景仍是應該使用merge來完成。blog
git rebase --skip
直接的使用根基分支的修改咱們能夠先經過git reflog找到以前rebase的那條記錄id ip
git reset commit-id --hard
就能夠回到rebase前的狀態了。 另外在每次作rebase前,git都會爲咱們建立一個ORIG_HEAD記錄rebase前的狀態讓咱們不須要reflog就能夠快速切回去,因此就能夠直接執行git reset ORIG_HEAD --hard
開發
git rebase -i commit-id
進入互動模式
當非文字的文件產生衝突時,其實咱們是沒有辦法決定具體哪一行出了問題的,因此這時候咱們能夠執行git checkout 文件名 --theirs
來使用別人的文件或者執行git checkout 文件名 --ours
來使用咱們的文件it
跟上面同樣第2個同樣咱們先進入互動模式,將pick改成s或者squash,這樣連在一塊兒的s就會與他們上一個pick合併成一個commit了,接着咱們保存退出,git依舊是循環讓咱們去修改咱們改動的commit,完成以後咱們就作到了合併的效果了
同樣是進入互動模式,接着將pick改成e或者edit,接着保存退出,這裏咱們就會看到git提示咱們能夠開始去修改commit,而且在結束後執行rebase continue的動做,其實這裏git就暫時將咱們轉至咱們要修改的那個commit
git reset HEAD^
,看過以前的文章就知道這裏默認其實就是mixed模式,因此當前commit先後的修改就被轉至工做區,接着咱們就能夠按咱們想要的commit形式從新把工做區的文件添加到暫存區而且提交,最後執行rebase continue,咱們就成功完成了拆分commit的動做 其實這個跟上面的場景幾乎差很少,惟一的區別是咱們不須要再使用git reset,而是直接添加提交咱們要的修改就能夠了
調整順序的話咱們只須要在互動模式中直接把pick的每條commit調成咱們要的順序便可,而對於刪除commit,咱們也只須要把想刪掉的pick直接刪除就能夠了。固然在作這些操做以前都要思考清楚,畢竟像調整順序的話若是你把對一個文件的修改commit放到了這個文件的建立commit以前,那就會出事了
git提供了一個filter-branch指令幫咱們批量的對commit進行改動,假設咱們的帳號信息放在了config/database.yml中,咱們能夠執行git filter-branch --tree-filter "rm -f config/database.yml"
,這樣git會對每一條commit都執行刪除的操做,那若是我後悔了想要恢復回來怎麼辦呢,咱們能夠執行git reset refs/original/refs/heads/master --hard
就可已恢復剛纔的刪除操做了,這個refs/original/refs/heads/master其實就是咱們以前說的ORIG_HEAD,是git在咱們作一些危險操做時備份出來的一個指針以便咱們撤回以前的操做。因此這裏若是咱們真的要完全刪除還須要把refs/original/refs/heads/master也一併刪除掉。接着還有一個找的回來的地方就是reflog,因此咱們也要清理掉reflog,它默認是30天清除一次,咱們能夠經過執行git reflog expire --all --expire=now
讓git立刻清除掉。最後咱們在這一系列操做以後就會有一堆閒置沒有用的對象了,咱們能夠經過git fsck --unreachable
列出來這些對象,固然由於他們已經沒有用了,咱們就能夠直接執行git gc --prune=now
把它們即刻清理掉,這樣咱們就完全的把帳號信息從git中刪除了。從這裏也看得出來其實對於git來講要真正意義上刪除記錄是很是困難的,因此咱們都常常說git永遠有後悔藥吃,由於老是有辦法能夠找回原來的歷史記錄。
前面咱們介紹過了reset和rebase,那咱們先來講一下revert,咱們能夠經過執行git revert commit-id
來還原某一個commit,可是這個還原不是刪除那個commit,其實revert是加了一個新的commit而後把內容修改成咱們想要revert的那個commit以前的狀態(這裏若是在revert時想用默認的commit信息不去修改的話能夠加上--no-edit選項)。
因此能夠看出reset和rebase是會改變歷史記錄的,而revert則不會
reset多用於尚未推送到遠程的commit,咱們能夠將它還原至某個咱們指定的commit
rebase也多用於沒有推送到遠程的commit,固然他的能力更強大一些,無論你想作新增,修改或是刪除等均可以作到,很適合用來整理本地的commit歷史
revert的話就比較適合已經推出去commit,或者一些團隊開發規範下不許使用reset和rebase的狀況