git rebase
是對commit history的改寫。當你要改寫的commit history還沒有被提交到遠程repo的時候,也就是說,還沒有與他人共享之前,commit history是你私人所有的,那么想怎么改寫都可以。
而一旦被提交到遠程后,這時如果再改寫history,那么勢必和他人的history長的就不一樣了。git push
的時候,git會比較commit history,如果不一致,commit動作會被拒絕,唯一的辦法就是帶上-f
參數,強制要求commit,這時git會以committer的history覆寫遠程repo,從而完成代碼的提交。雖然代碼提交上去了,但是這樣可能會造成別人工作成果的丟失,所以使用-f
參數要慎重。
要解決這個問題,就要從提交流程上做規范。
舉個正確流程的栗子:
假設樓主的team中有兩個developer:tom和jerry,他們共同使用一個遠程repo,並各自clone到自己的機器上,為了簡化描述,這里假設只有一個branch:master
。
這時tom機器的repo有兩個branchmaster
, origin/master
而jerry的機器上也是有兩個branchmaster
, origin/master
均如下圖所示
tom和jerry分別各自開發自己的新feature,不斷有新的commit提交到他們各自私有的commit history中,所以他們的master指針不斷的向前推移,分別指向不同的commit。而又由於他們都沒有git fetch
和git push
,所以他們的origin/master
都維持不變。
jerry的repo如下
tom的repo如下,注意T1
和上圖的J1
,分別是兩個不同的commit
這時Tom首先把他的commit提交的遠程repo中,那么他本機origin/master
指針則會前進,和master
指針保持一致,如下
遠程repo如下
現在jerry也想把他的commit提交到遠程repo上去,運行git push
,毫無意外的失敗了,所以他git fetch
了一下,把遠程repo,也就是之前tom提交的T1
給拉到了他本機repo中,如下
commit history出現了分叉,要想把tom之前提交的內容包含到自己的工作中來,有一個方法就是git merge
,它會自動生成一個commit,既包含tom的提交,也包含jerry的提交,這樣就把兩個分叉的commit重新又合並在一起。但是這個自動生成的commit會有兩個parent,review代碼的時候必須要比較兩次,很不方便。
jerry為了保證commit history的線性,決定采用另外一種方法,就是git rebase
。jerry的提交J1
這時還沒有被提交到遠程repo上去,也就是他完全私有的一個commit,所以使用git rebase
改寫J1
的history完全沒有問題,改寫之后,如下
注意J1
被改寫到T1
后面了,變成了J1`
git push
后,本機repo
而遠程repo
異常的輕松,一條直線,沒有-f
所以,在不用-f
的前提下,想維持樹的整潔,方法就是:在git push
之前,先git fetch
,再git rebase
。
git fetch origin master git rebase origin/master git push
強烈推薦閱讀