- Git squash合並多個commit,把多個commit融合成一個
git merge squash 和 rebase 區別
merge 的幾種形式
在 merge pr 的時候,默認是有三種選項的,分別是
- 普通的 merge
- rebase merge
- squash merge
這其實對應於我們在合並分支的時候的幾種方式,所以我就以本地分支的形式來說說有啥區別。
一個簡單的模型
假設我們一開始的 master 分支上已經有了幾個提交,就像這樣:
然后,我們切出一條開發的分支,進行了一些 Feature 的開發,然后我們的分支可能就是這種情況:
這種情況還好,也比較常遇到,但是,現在問題來了,如果在這個時候 master 有了一些新提交(可能是其他分支合並進來的),那么這個時候情形就成了這樣:
這個情況很有趣,但是我們不討論,因為這和我們今天的主題無關,以后可以另外開一個話題來說,今天要說的是第二個情況。
普通 Merge
說到合並分支,可能我們最熟悉的操作是這樣的:
- 先切換到目標分支(master)
- 執行命令:
git merge devel - 刪除舊分支(可以在上面一同做):
git branch -D devel - 提交到遠程分支:
git push origin master
好像這樣沒啥問題的樣子,但是這樣操作之后,你知道結果是怎么樣嗎?假設合並之前的這樣的:
我們這么一番操作之后,那么最后我們的分支的歷史將會是這樣的:
是的,看上去很不錯,也是一條直直的 commit line,我們在 devel 分支中的 commit 也是一個不差得保留在了 master 中。但是,很多時候,我們並不需要那么多的 commit,假設你給一個開源項目提交一個 Bug Fixes,然后一個簡單的修改因為你的粗心大意 pr 了十幾個 commit 過去,如果作者給你 merge 了,這就在這個項目的歷史長河中增加了十幾個 commit 啊,以后的人看 commit history 估計都崩潰了吧;同時,對於你自己管理的項目來說,當你 merge 之后發現有問題,想回滾都蛋疼!
squash merge
在使用 git 的過程中,可能你遇到過想要合並多個 commit 為一個,然后很多人會告訴你用 git commit --amend,然后你發現里面有你的多個 commit 歷史,你可以通過 pick 選擇,squash 合並等等。同樣得,merge 的時候也可以這么干,你只需要這么簡單的兩步:
- 切換到目標分支:
git checkout master - 以 squash 的形式 merge:
git merge --squash devel
你會發現,在 master 分支上居然有未提交的修改,然后你就需要在 master 上主動提交了修改,注意,這里是你 commit 的,也就是改變了 commit 的 author。結果是這樣的:
這里好了,比前面普通的 merge 來說,我們只有一個 commit 了,不管在分支中 commit 了多少,這里都只有一個!
rebase merge
但是,作為處女座的程序員肯定是不能忍受目前的情況的,因為我們既想合並 commits,又想保留作者的信息,那么有沒有什么好辦法呢?肯定是有的啦,這個時候我們可以嘗試一下 rebase,操作步驟是這樣的:
- 先切換到 devel 分支(不一樣咯):
git checkout devel - 變基:
git rebase -i master - 切換回目標分支:
git checkout master - 合並:
git merge devel
這里完成了第二步之后我想你應該大概知道發生了什么事了,我們在 devel 里面對照 master 進行了變基,所謂的變基其實就是找到兩個分支共同的祖先,然后在當前分支上合並從共同祖先到現在的所有 commit,所以我們在第二步的時候會選擇怎么處理這些 commit,然后我們就得到了一個從公共commit 到現在的單個 commit,這個時候別人講我們這個 commit 合並到 master 也只會在 master 上留下一個 commit 記錄,就像這樣:
雖然這個 commit history 線看上去很不錯,而且也比較符合實際情況,但是我們需要注意到的有點就是分支上的開發者需要自己執行變基操作,從而導致他的原始 commit history 變化了(可以理解成被合並了)。
對比
相比一下前面三種方式,我們可以總結出一些東西:
- rebase 可以盡可能保持 master 分支干凈整潔,並且易於識別 author
- squash 也可以保持 master 分支干凈,但是 master 中 author 都是 maintainer,而不是原 owner
- merge 不能保持 master 分支干凈,但是保持了所有的 commit history,大多數情況下都是不好的,個別情況挺好
鏈接:https://www.jianshu.com/p/684a8ae9dcf1
我有一個非常混亂的git歷史。我想壓制一堆舊的提交(不包括最后一個提交)。
我知道如何壓制我最后的n提交。但這是不同的。在這里,我連續提交n1到n2我想要壓縮成一個,而在n2我有一個提交歷史后,我想保留到最后一個。
所以,如果我目前的歷史是這樣的:
---- n1 --- n2 -------- m
我想擠壓n1,n2所以最終看起來像這樣:
---- n1n2 -------- m
其中n1n2是一個包含來自n1to 的壓縮內容的提交n2。
我該怎么做?對歷史的影響n2是m什么?
也就是說,每次提交的哈希都會因為我想做什么n2而m改變?
- 啟動交互式rebase:
git rebase -i HEAD~n (where ’n’ is how far do you want to go back in history)
- 您的默認編輯器將打開。在頂部,
n將以相反的順序顯示最新提交的列表。例如:
pick a5f4a0d commit-1
pick 19aab46 commit-2 pick 1733ea4 commit-3 pick 827a099 commit-4 pick 10c3f38 commit-5 pick d32d526 commit-6
- 為要壓縮的所有提交指定
squash(或快捷方式s)。例如:
pick a5f4a0d commit-1
pick 19aab46 commit-2 squash 1733ea4 commit-3 squash 827a099 commit-4 pick 10c3f38 commit-5 pick d32d526 commit-6
Git將更改和更改直接應用於它之前,並使您將提交消息合並在一起。
- 保存並退出。
- Git將應用所有更改,並將再次打開您的編輯器以合並三個提交消息。您可以修改提交消息或保持原樣(如果是這樣,將連接所有提交的提交消息)。
- 你完成了!您選擇的提交將全部與前一個提交相關。
