簡體   English   中英

為什么 git 合並不創建共同祖先?

[英]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 不將合並視為分支的共同祖先。 也許我錯過了一個選擇?

編輯:

我的合並策略執行以下操作: 圖 1

無論如何都可以獲得以下內容,而無需刪除和重新創建開發分支?

在此處輸入圖像描述

TL;博士

您想要的是“作為合並氣泡的功能”。 要獲得這些,請使用git merge --no-ff從您的主線與每個功能。 您通常應該將每個功能放在自己的分支上,但如果您願意,您可以每次都使用相同的名稱(例如dev )。 分支名稱並不重要,並且 Git 通常不會存儲它們(如果您願意,可以將它們放入提交消息中,但merge branch blergh形式的消息沒有實際價值)。

答案的根源是git merge --squash不進行合並(提交)

Git 中的單詞merge既用作動詞to merge ,意思是合並兩組不同的更改,也用作修飾單詞commit 的形容詞:合並提交是具有兩個或多個父項的提交。 1形容詞形式,合並提交,通常縮寫為一個簡單的名詞,合並 所以我們需要記住,一些 Git 命令執行合並類型的操作,即合並作為動詞,而一些 Git 命令產生合並類型的提交,即進行合並,一個名詞。

git merge命令經常但並不總是兩者兼而有之。 有時它只做兩者之一——合並操作,最后沒有合並提交——有時它都不做。

git cherry-pickgit revert命令總是2執行合並作為動詞部分,但最后從不合並。

git commit命令可以進行普通提交,或者在某些特殊情況下,合並提交或提交:根本沒有父提交。

要了解所有這些部分是如何相互作用的,我們還需要記住一些事情:

  • Git 實際上是從 Git 的index中構建新的提交。
  • 在合並為動詞的操作過程中,索引會被擴展。 現在,它不再保存每個文件的一份副本,而是保存三個. 3
  • 如果 Git 在沖突合並的中間停止,它會留下各種跟蹤文件,例如MERGE_HEADMERGE_MSGCHERRY_PICK_HEAD等。 git status命令知道要查找這些,並且可以告訴您您正處於沖突合並的中間,例如,文件尚未解決,或者所有沖突都已解決。

當您運行git command --continuegit 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 ,因此它在提交之前停止; 但是
  • 不會創建任何“merge going on”文件,因此停止后的狀態是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM