[英]Why git merge does not create a common ancestor?
這不是解決問題的問題,只是為了拓寬我對git的理解。
我有一個項目基本上是一個人的工作,所以它在我們的 gitlab 服務器上只有一個分支; 在我的開發機器上,我通常有一個“通用”開發分支,並在需要時有一個 quickfix 分支。 我通常在將 quickfix 分支合並到 master 后將其刪除,但我會在較長時間內保持 dev 分支處於活動狀態,因此隨着項目的進展,從 dev 到 master 會有許多提交。
當我在 master 上合並 dev 時,我通常使用--squash
來擺脫不相關的“開發”提交; 然后我建議的合並消息說,“壓縮以下提交”,其中包含自共同祖先以來的提交列表,即我創建 dev 的提交,而不是我之前合並的提交。 當然,我只是簡單地刪除默認評論並編寫自己的評論; 然而,令我驚訝的是 git 沒有意識到從 dev 到 master 的合並為兩個分支創建了一個新的共同祖先。
同樣,這不是問題:我可以簡單地刪除 dev 分支並重新創建它——實際上,這就是我在意識到沒有必要之前所做的事情。 然而,我想了解為什么git 不將合並視為分支的共同祖先。 也許我錯過了一個選擇?
編輯:
無論如何都可以獲得以下內容,而無需刪除和重新創建開發分支?
您想要的是“作為合並氣泡的功能”。 要獲得這些,請使用git merge --no-ff
從您的主線與每個功能。 您通常應該將每個功能放在自己的分支上,但如果您願意,您可以每次都使用相同的名稱(例如dev
)。 分支名稱並不重要,並且 Git 通常不會存儲它們(如果您願意,可以將它們放入提交消息中,但merge branch blergh
形式的消息沒有實際價值)。
答案的根源是git merge --squash
不進行合並(提交) 。
Git 中的單詞merge既用作動詞to merge ,意思是合並兩組不同的更改,也用作修飾單詞commit 的形容詞:合並提交是具有兩個或多個父項的提交。 1形容詞形式,合並提交,通常縮寫為一個簡單的名詞,合並。 所以我們需要記住,一些 Git 命令執行合並類型的操作,即合並作為動詞,而一些 Git 命令產生合並類型的提交,即進行合並,一個名詞。
git merge
命令經常但並不總是兩者兼而有之。 有時它只做兩者之一——合並操作,最后沒有合並提交——有時它都不做。
git cherry-pick
和git revert
命令總是2執行合並作為動詞部分,但最后從不合並。
git commit
命令可以進行普通提交,或者在某些特殊情況下,合並提交或根提交:根本沒有父提交。
要了解所有這些部分是如何相互作用的,我們還需要記住一些事情:
MERGE_HEAD
、 MERGE_MSG
、 CHERRY_PICK_HEAD
等。 git status
命令知道要查找這些,並且可以告訴您您正處於沖突合並的中間,例如,文件尚未解決,或者所有沖突都已解決。 當您運行git command --continue
或git commit
時,Git 從它停止的地方繼續。 (The --continue
variety acts as a sanity check, that there's that particular command to continue at this point.) When you run certain kinds of git reset
, or git command --abort
or git command --quit
, Git terminates the unfinished操作並通過調用正確類型的重置( --hard
或--soft
)來放回( --abort
)或不放回( --quit
)。
這意味着,例如, git merge --no-commit
可以啟動合並,盡可能自行運行它——甚至可能到沒有剩余沖突的地步——然后停下來讓你擺弄隨心所欲地使用 Git 的索引和/或工作樹。 Your eventual git merge --continue
or git commit
will then finish the merge, using the files Git left behind when it stopped, plus any updates you made to the index (a so-called evil merge; see Evil merges in git? ). Or, your git reset --hard
or git merge --abort
all the work that git merge
did, removes the merge-in-progress marker files, and leaves you set up as if you had not even started a git merge
command. 4
無論如何,如果您已經通過這部分而沒有迷路, git merge --squash
變得非常容易理解。 它:
git merge
一樣;--no-commit
,因此它在提交之前停止; 但是git merge --continue
是不允許的,並且git commit
將進行普通提交,而不是合並提交。 由於合並和未來的合並基礎由提交圖決定——也就是說,提交本身包括它們的父鏈接——並且git merge --squash
沒有放入額外的父鏈接,最終提交沒有你想要的歷史。 那么,解決方案是避免git merge --squash
。
您可能(非常合理地)想知道git merge --squash
有什么好處。 答案是:不是那么多,但在一種情況下它絕對是有意義的:那就是當你:
為了進行那一次提交,您將 go 返回到您創建實驗分支的分支,然后運行git merge --squash experiment
(或任何合適的名稱)。 然后,您為一個提交編寫所需的提交消息,然后刪除實驗分支。 它現在“死了”:它的提交沒有進一步的用途。 都是垃圾,一個月左右,當垃圾收集器處理完后,就用垃圾的 rest 將它們拖走。
如果您不打算殺死分支, git merge --squash
可能是錯誤的工具。 (但另請參閱matt 關於將 squash-merge 與 GitHub PRs 結合使用的評論。)
1有兩個以上父母的提交是章魚合並。 這些通常是用git merge -s octopus
制作的,但是通過給git merge
兩個或多個提交說明符來暗示-s octopus
部分。 他們沒有做任何你不能用更典型的雙親合並做的事情。 事實上,他們特別不做事情——即解決沖突——你可以用雙親合並來做,這可能是首先讓章魚合並的主要理由:因為章魚合並比“弱”一個正常的合並,如果你在一組提交中看到一個,你可以很確定它是這些簡單的、無沖突的合並案例之一。
不過,總的來說,我仍然認為章魚合並主要是為了炫耀。
2這里的“總是”有點太強了:例如,有時git cherry-pick
可能會出錯,如果動作的 merge-as-a-verb 部分因合並沖突而停止,你就會被留在操作的中間。
3更准確地說,它最多可容納三個,從三個輸入提交到合並操作:合並基礎、“我們的”或“本地”或HEAD
提交,以及“他們的”或“遠程”或“其他”提交. 但是,如果三個提交之一中缺少一個文件——例如,如果我們修改了文件path/to/file.ext
並且他們完全刪除了它——那么該文件的索引條目可能少於三個。
4 Note that for this to work, the state that git reset --hard
writes—which is to say, the set of files that are in the HEAD
commit right now—must match the state that everything had when you first started the git merge
. 等效地, git status
將不得不nothing to commit, working tree clean
(盡管可能帶有未跟蹤的文件)。 這就是為什么git merge
通常需要“干凈”的 state 才能開始。 內部git merge-recursive
命令不是那么小心,如果您運行git git merge-recursive
— 例如, git stash apply
可以。
好吧,最后一個問題(至少是問題的當前版本)可以這樣實現(假設some-branch
在 A 上,而other-branch
在 revision-6 上):
git checkout some-branch
git merge revision-3 # by using its ID
# now we have created revision B
git checkout other-branch
# let's rebase it
git rebase some-branch # this should set up revision-4, 5 and 6 on top of B
git checkout some-branch
git merge other-branch
你有它。
當我在 master 上合並 dev 時,我通常使用 --squash 來擺脫不相關的“開發”提交
你沒有實現你的既定目的。 你沒有擺脫任何提交。
而且,squash 合並根本不是合並,它不會在這個分支和另一個分支之間建立任何聯系。 因此,合並基礎永遠不會移動,並且歷史記錄會丟失。 這就是為什么,正如我在這里所說, https://stackoverflow.com/a/67609758/341994 ,壁球合並和長壽分支是相反的。
您所描述的是,您將首先壓縮開發提交以簡化歷史記錄,然后與真正的合並合並。 換句話說,停止使用merge --squash
而是使用 squash(使用重置或交互式變基)然后合並。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.