在開發過程中使用git rebase還是git merge,優缺點分別是什麼?
rebase,合併的結果好看,一條線,但合併過程中出現衝突的話,比較麻煩(rebase過程中,一個commit出現衝突,下一個commit也極有可能出現衝突,一次rebase可能要解決多次衝突);
merge,合併結果不好看,一堆線交錯,但合併有衝突的話,只要解一次就行了;
所以我都是先rebase,如果有衝突,git rebase --abort,再換用merge~~
推薦看下其他人的說法:
在開發過程中使用git rebase還是git merge,優缺點分別是什麼?-vczh的回答 - 知乎
用git來放Windows源代碼- 看評論區 - 知乎專欄
兩個使用場景是不一樣的,merge只是合併另外一個分支的內容,rebase也合併另外一個分支的內容,但是會把本分支的commits頂到最頂端
假設我們現在有3個分支- master分支:線上環境使用的分支
- testing分支:測試環境使用的分支
- my_feature分支:開發新功能的分支,也就是當前分支
A. 假設我在my_feature上開發了一段時間,之後另外的同事開發的功能正式上線到master分支了,那麼我可以在當前的分支下rebase一下master分支,這樣我這個分支的幾個commits相對於master還是處於最頂端的,也就是說rebase主要用來跟上游同步,同時把自己的修改頂到最上面
B. 我在my_feature上開發了一段時間了,想要放到testing分支上,那就切到testing,然後merge my_feature進來,因為是個測試分支,commits的順序無所謂,也就沒必要用rebase (當然你也可以用rebase)
另外,單獨使用rebase,還有調整當前分支上commits的功能(合併,丟棄,修改commites msg)
PS:
其他知友的答案都說到衝突的問題,1. 用merge確實只需要解決一遍衝突,比較簡單粗暴
2. 用rebase有時候會需要多次fix衝突(原因在於本地分支已經提交了非常多的commit,而且很久都沒有和上游合併過)我個人推薦大家開發的時候,盡量及時rebase上游分支(我習慣是每周merge一次),有衝突提前就fix掉,即使我們自己的分支開發了很久(哪怕是幾個月),也不會積累太多的conflict,最後合併進主分支的時候特別輕鬆, 非常反對從master check出新分支,自己悶頭開發幾個月,結果最後merge進主分支的時候,一大堆衝突,自己還嗷嗷叫的行為關於 rebase 和 merge 的使用原則,《Pro Git》這本書中說的已經很好了:
Rebase vs. Merge
Now that you』ve seen rebasing and merging in action, you may be wondering which one is better. Before we can answer this, let』s step back a bit and talk about what history means.
One point of view on this is that your repository』s commit history is a record of what actually happened.It』s a historical document, valuable in its own right, and shouldn』t be tampered with. From this angle, changing the commit history is almost blasphemous; you』re lying about what actually transpired. So what if there was a messy series of merge commits? That』s how it happened, and the repository should preserve that for posterity.
The opposing point of view is that the commit history is the story of how your project was made. You wouldn』t publish the first draft of a book, and the manual for how to maintain your software deserves careful editing. This is the camp that uses tools like rebase and filter-branch to tell the story in the way that』s best for future readers.
Now, to the question of whether merging or rebasing is better: hopefully you』ll see that it』s not that simple. Git is a powerful tool, and allows you to do many things to and with your history, but every team and every project is different. Now that you know how both of these things work, it』s up to you to decide which one is best for your particular situation.
In general the way to get the best of both worlds is to rebase local changes you』ve made but haven』t shared yet before you push them in order to clean up your story, but never rebase anything you』ve pushed somewhere.
git rebase就是那種典型的,使用MVC模型的時候喜歡想著用Model來代替View的這種人,會喜歡做的事情。其實也沒有什麼好和不好,但是保留原始數據顯然是相當重要的。你嫌圖形不好看的話,自己寫一個程序去畫就好了。就像沒有人會在SQLServer裡面真的用一棵樹來表示樹形的留言一樣。
一句話,私有的用rebase,公開的用merge。
一個人工作
私有分支本地內容用rebase多人協作
公開分支公開內容用merge在我的團隊,禁止用rebase只要你的私有branch不公開,一般都應在從私有branch合併(merge)代碼到主幹branch時rebase,不僅僅是好看,你可以rebase的時候把你的commit清理一下(squash),對於主branch的其他程序員來說你的代碼更易讀。另外比較實用的地方就是`git bisect`分析用rebase清理過的線性的commit歷史比較容易。
resolve conflicts很方便,各種工具都可以用。默認的vimdiff就很高效。`git rerere`也很有用
rebase不可能,只有merge了,所以public branch只能merge。
很簡單的事情,非要談得玄而又玄,說白了就是基本功不紮實。
哈哈哈,想起來以前給 WebIDE 寫的一個推廣。內含 WebIDE 的輕微廣告。當時 WebIDE 上線了 Merge、Stash、Rebase、Reset 和 Tag 幾個功能。這篇文章介紹了這個功能的使用方式。(啊啊啊,公眾號複製過來的圖片清晰度好差,找了一下發現原圖刪了T-T)原文如下:
原文:如何和產品妹子解釋什麼是變基CODING 的 WebIDE 上了不少新功能。你和同事激動的討論著。隔壁產品小妹十分好奇,問你:『你們都在說的變基、Merge 都是做什麼的?』
你想了想,這麼解釋給她聽:Merge 合併與衝突『你和你的男朋友生活非常幸福』你說:『兩個人一起為共同生活而努力,我們來假設家庭生活是你們兩個人的主線,每天工作、休息、吃愛吃的食物,做愛做的事情,像這樣』
『而分支就是偶爾他和朋友看個球,你和閨蜜逛個街。主線之外,你們還有各自的支線任務。但是完成支線任務之後,你們兩個還是可以去一起看個電影啊,像這樣支線任務完成回到了主線任務,並且可能你去逛了街穿了美美的衣服,有了一次很棒的約會,支線任務也是為主線添磚加瓦的。』
『那你們平常說的衝突呢?』
『衝突是這樣的。比如今天他下班去踢球,說好今晚約會,結果他一身臭汗的回來。你會不會不開心?』
『生氣啊,他都記不得今晚有約會。』『恩,這個時候衝突就產生了,衝突產生的時候,他的支線任務就很難合併到主線任務中。在我們使用 WebIDE 的時候,合併的時候會提示你「發現衝突」,並且彈出衝突列表,再進行逐行處理,協調主線任務和支線任務。比如他早點回來洗澡,你花點時間補妝。』
『啊啊啊,這挺好,省的男朋友老問我為什麼生氣。笨死了。』
『然後我們再來說說儲藏。』
Stash 儲藏『儲藏的話,是不是說工作做到一半,要存著接著做?』
『某種程度上說是的,你做到一半要有其他事情的時候,就需要把這件事情存起來。比如男朋友要送你一個手工的禮物,像這樣可愛的龍貓』
『哇,好萌。』
『對的,但是又有了別的工作,可是他做到一半的時候可能是這樣的,所以不想讓你看到,不能放家裡只能放在辦公室。就把現在的工作存儲下來。並不提交到主線任務。而且可以順利恢復上次的進度。』
『這麼說就很好理解了,那你快告訴我變基是什麼啊。是不是男朋友跟別的男孩子跑了。』
『額,怎麼會,你男朋友又不是程序員……』
Rebase 變基 『變基的基其實是基礎的意思,簡單來說就是把支線任務變成主線任務。』
『哎?聽起來和 Merge 有點像啊。』
『是的,目的上都是把分支任務整合到主線任務上,但是還是有一些區別的,畫個圖你就了解了,第一張圖是 Merge。』
『第二張圖是 Rebase,他會對比主線「工作」之後兩個支線分支 A「追番」和 B「燒飯」的相似與不同,將不同之處「爐子上要燉湯」提取出來作為 B1,然後把「A+B1」即「追番(一起)」和「燉湯 ing」放在主線任務里。』
『哦?看起來變基是個好功能呢。』『是的, Merge 的線路更加流暢整潔,特別是支線任務複雜的時候。』
Reset 重置『重置就是你和男朋友吵架了,他特別想用的功能……』
『就是返回到不生氣的狀態是嗎?』
『是的是的。』
Tag 標籤『這就是給任務改名字嗎?』
『是的,常常會把任務改成更有意義的名字,比如這樣。』所以你不想了解一下怎麼在 WebIDE 里使用這些功能嗎?
點擊了解:Coding WebIDE 增加 Git 五大高級特性
當然歡迎直接體驗:http://ide.coding.net
rebase!不要merge!實在實在沒辦法才merge
rebase和merge給提交歷史造成的複雜性就是單向鏈表和有向圖的差別。
其他回答里提到的rebase的問題都不是問題。都有優雅的辦法解決。
還有一點,merge之後的歷史記錄不定能rebase,rebase之後的歷史一定能merge。
如果你希望提交歷史是規範和乾淨的,一定知道rebase-i對merge的蛋疼。
如果只是拿git做一個備份工具,不在乎歷史有多亂,那選擇merge比較無腦方便。不管用什麼風格的操作流程來整合你的本地分支,最終,這條分支是要和伺服器上的目標分支做一次merge的。
通常情況下有這麼兩種情形:
1. 本地分支是A-B-C-D,伺服器上是A-B。這時候直接push,伺服器上也會變成A-B-C-D,這種是快進式合併。結束後,歷史樹是一條線。
2.本地分支A-B-C-D,伺服器上是A-B-E。這時候直接push,假如設置允許這次push並且沒有衝突的話,那最終伺服器上會是A-B-分叉:一邊是C-D,一邊是E,然後E和D又連到一個新的merge commit F上。優點:記錄下合併動作;缺點:很多時候這種合併動作是垃圾信息,記錄意義不大,反而把歷史樹搞得複雜不直觀,會對實際工作帶來負面作用。
負面作用實例:某工在push完提交以後,想用當前分支最新的代碼編譯一個版本,於是就在D上打了個tag,因為他想D是最新的一筆提交嘛,然後讓編譯job根據這個tag去抓代碼編譯了,編完後同事跑來跟他反饋說,這版本不對呀,我改的E好像沒起效果嘛。我比你先push的,你說你取了最新的代碼,怎麼會沒有我的改動呢?
某工應該把tag打在F上,才能取到當時最新的版本。他在取版本時不用tag而是用branch,那是可以避免這種情況的,因為branch一般指向的commit就是最新的。但也存在風險:job去取分支時,分支上又被其他人上了新的提交,這個新提交很可能並不是他想要的,還可能帶來不好的副作用。如果先處理一下,使得最終提交是像第1種情形里的那樣,那tag打在D上是沒有問題的。
那某工在提交之前,如果先做一次rebase。git pull --rebase,那麼本地分支就會被更新成A-B-E-C"-D",把C-D的基從B上變到E上,這時再去push,那伺服器上也變成A-B-E-C"-D". 那tag打在D"上就行了。目標分支上的歷史樹還是一條線。
當然他也可以先在本地merge。默認情況下git pull origin 遠程分支,會把遠程分支上的E更新到本地,然後merge到本地分支上。merge結束後,本地最新的一個commit就是F,某工可以清楚地知道現在最新的commit是F而不是D,tag也不會建錯。這時再push,那伺服器上也會進行快進式合併,目標分支也會更新到F上。但歷史樹不是一條線了,中間有分叉然後又合併到一起。
這種有分叉的歷史樹還是有一個優點,D和E分別代表了某工和同事各自的版本,D上不包含E的改動,E上不包含CD的改動。而一條線的話,D"是包含了E的改動的,而E不包含C"D"。此時如果想要D的版本(不包含E),那得把D找出來,建個tag或者branch引用起來;或者在D"後面把E的改動revert掉。
其它可能還有一些情形,但大致上會是第二種情形的變形。rebase和merge的使用得看具體的情形,並不存在誰比誰好。rebase和merge比起來,相對難以理解和使用一些。在學習rebase的過程中,可以把它分解成cherry-pick來做。搞清楚這個問題首先要搞清楚merge和rebase背後的含義。先看merge,官方文檔給的說明是:
git-merge - Join two or more development histories together
顧名思義,當你想要兩個分支交匯的時候應該使用merge。根據官方文檔給的例子,是master merge topic,如圖: A---B---C topic
/
D---E---F---G---H master
然而在實踐中,在H這個commit上的merge經常會出現merge conflict。為了避免解決衝突的時候引入一些不必要的問題,工程中一般都會規定no conflict merge。比如你在github上發pull request,如果有conflict就會禁止merge。
所以才會有題主問的問題:在當前的topic分支,想要引入master分支的F、G commit上的內容以避免merge conflict,方便最終合併到master。
這種情況下用merge當然是一個選項。用merge代表了topic分支與master分支交匯,並解決了所有合併衝突。然而merge的缺點是引入了一次不必要的history join。如圖:
A--B--C-X topic
/ /
D---E---F---G---H master
其實仔細想一下就會發現,在引入master分支的F、G commit這個問題上,我們並沒有要求兩個分支必須進行交匯(join),我們只是想避免最終的merge conflict而已。
rebase是另一個選項。rebase的含義是改變當前分支branch out的位置。這個時候進行rebase其實意味著,將topic分支branch out的位置從E改為G,如圖:
A---B---C topic
/
D---E---F---G master
在這個過程中會解決引入F、G導致的衝突,同時沒有多餘的history join。但是rebase的缺點是,改變了當前分支branch out的節點。如果這個信息對你很重要的話,那麼rebase應該不是你想要的。rebase過程中也會有多次解決同一個地方的衝突的問題,不過可以用squash之類的選項解決。個人並不認為這個是rebase的主要問題。
綜上,其實選用merge還是rebase取決於你到底是以什麼意圖來避免merge conflict。實踐上個人還是偏愛rebase。一個是因為branch out節點不能改變的情況實在太少。另外就是頻繁從master merge導致的冗餘的history join會提高所有人的認知成本。git merge,合併兩個分支,合併後的所有commit會按照提交時間從舊到新排列,並且一般會新增一個名為merge feature/xxx的新commit。分支看著不大整潔,但是能看出合併的先後順序,並且從merge feature/xx這些commit中能大概看出合併了有哪些作用的分支。 git rebase,會把兩個分支合併,但是合併後的commit順序不一定按照commit的提交時間排列。而是先找出當前分支與要合併進來分支的特有commit,然後把屬於當前分支的這些特有commit放到合併後分支的最新地方,所以git rebase後,git log是先看到當前分支的那幾個commit,才到要合併分支的commit。合併後的分支,commit的順序可能會打亂,但是不會新增一個新commit。分支看著比較整潔,看不出有合併的跡象。 使用場景上,多人協同開發時,公共分支,如develop,master分支會merge 個人的分支,如feature/xx,fix yy分支。或團隊偏向於從git log上看出分支的合併順序和合併功能或注重commit的提交時間按順序排列時,可以用merge。 git rebase,適用於團隊喜歡整潔的log,不要有多餘的新增commit,對commit沒有按時間順序排列的需求。 要把公共分支合併到個人分支時,或者就只有一個人在開發時,rebase或merge都可以。 但是多人開發,公共分支要把個人分支合併進來時,推薦merge。因為,假設如下情景:A,B二人合作開發,開始時,A,B二人分別基於develop分支新建有a,b分支,A完成a分支開發後,若讓develop分支rebase a分支,develop分支原有的commit順序可能會打亂,所以當B完成b分支開發後,要合併到develop分支時,git就會發現b分支原來的commit順序跟develop分支的不同,就會提示衝突。而A一開始就用merge的話,到B提交b分支了,git就不會因為原來的commit順序不同而提示衝突了,當然如果是新增的commit內容上有衝突,還是會提示衝突的。 嘻嘻,這個問題以前也困擾過我,查了資料後才有所了解,希望這個解釋可以給你解惑唄。
其實沒那麼複雜啦。你要是看不爽那種純 merge 的 commit,希望它不要出現在 timeline 里,就用 rebase,不然用哪個差別不大。
rebase可以確保生產分支commit是一個線性結構,方便rollback。其實生產也可以選擇打tag來發布。
多個提交的時候,配合squash參數,可以確保多個commit合併為單個commit,通過rebase可以確保主分支commit history線性結構上每個commit點都是相對獨立完整的功能單元。
除了美感,這樣做也有助於團隊間的分工協作,比隨便merge效果好。如果你認同 Trunk Based Development 的理念的話,那應該當 git merge 這個命令壓根不存在,而總是使用 git rebase。
所有人都直接在同一個 branch 上提交代碼?聽上去好像在用 svn 一樣的老古董?
然而 Google 和 Facebook 似乎就是這樣開發軟體的。
看這個吧: Merging vs. Rebasing - Workflow walkthrough
按 git 的邏輯, pull 回來的變更能fast-forward過去的時候就應該 rebase 。而有衝突的時候,merge 能如實反映工作的起點和每一個合併點,但是容易形成複雜的版本樹;rebase 相當於不斷移動分支工作的起點,並且這個移動的過程在版本歷史裡是不可見的,所以版本線清爽,但也不是沒有代價的,要是老在主線或者多人協作的分支上移動,其他人就暈菜了。
不管哪一種,都要頻繁地同步主線變化,不然容易搞成最終合回主線時影響範圍很大,容易引發問題。團隊里有新人的話建議統一 git merge,不然容易出來麻花,過去解完麻花再傳授一番 git rebase 的原理然後 git push -f 一發又有什麼收益呢,想回滾就找 build,想回溯歷史的話 jira/wiki/gitlab 上有的是,個人感覺不是開源社區之類比較看重 git commit 來查 bug 和各種 backport 的話,團隊項目這方面可以選擇更不容易出錯的用法
在自己的分支上開發,會有很多修修補補的零碎commit,要merge之前,先rebase自己的分支:包括把相同的功能改動rearrange and squash到一起,amend commit message,然後再merge,這樣對外來看,就好像你的每一個commit都是個成品。
ref: git rebase -i master上家公司用merge,現在的是rebase,正好體驗了兩者的不同。。。
兩種方法對於最後origin/master合併效果圖的不一樣,這個大家都知道長啥樣。。。但是具體選用哪個,還是取決於公司的workflow是什麼樣的。。。 - 如果要在本地分支做大量的開發測試,比如積攢了十幾甚至幾十個commit,然後合併進master,接下來回歸測試,打tag,發布等,還是用merge,不然大家每天就光在resolve conflict,不用幹活了。。。 - 如果只在本地修改一兩個commit,然後馬上提交到master,跑完所有unit test,integration test,regression test等,直接發布,也就是continuous integration的理想狀態,那麼rebase是極好的,保證了master的線性和萬一出事了可以準確的revert。。。團隊合作必須用merge。為了追求Git的線好看,在團隊合作中使用rebase說輕點是捨本逐末,說重了是對團隊不負責任。個人以及本地項目無所謂。而且用多了我現在覺得多條線反而更好看,只要不超過5條也沒什麼關係。文檔對衍合操作有比較詳細的說明Git - 分支的衍合對於在團隊合作中使用rebase可能導致的問題摘錄如下:
呃,奇妙的衍合也並非完美無缺,要用它得遵守一條準則:
一旦分支中的提交對象發布到公共倉庫,就千萬不要對該分支進行衍合操作。
如果你遵循這條金科玉律,就不會出差錯。否則,人民群眾會仇恨你,你的朋友和家人也會嘲笑你,唾棄你。
在進行衍合的時候,實際上拋棄了一些現存的提交對象而創造了一些類似但不同的新的提交對象。如果你把原來分支中的提交對象發布出去,並且其他人更新下載後在其基礎上開展工作,而稍後你又用 git rebase 拋棄這些提交對象,把新的重演後的提交對象發布出去的話,你的合作者就不得不重新合併他們的工作,這樣當你再次從他們那裡獲取內容時,提交歷史就會變得一團糟。
如果使用feature branch模式開發,那麼feature branch回到master前,按團隊偏好可以選擇先rebase onto master,甚至rebase --interactive,把開發歷史打扮得更漂亮。如果你最後總要做這麼個修改歷史的步驟,那開發feature branch過程中無論怎樣rebase或者merge都是無所謂的,反正這些動作最後都會被抹掉。
但無論如何,最後都應該是merge --no-ff回master。這樣保證master上的每一個commit都是完整的feature,不會出現rollback或者bisect到構成一個feature的一組commit中間而編譯或運行出錯的情況。
推薦閱讀:
※如何用其他方式下載 GitHub 客戶端?
※如何克服解決 Git 衝突的恐懼症?
※如何評價開源中國的碼雲?
※我們可以使用 Git 以及 GitHub 做哪些事情?
※github怎麼綁定自己的域名?